Skip to content
Open the app

The OIDC flow

First Six authenticates students and staff through your institution's identity provider using OpenID Connect. The flow is implemented server-side (authorization code with PKCEProof Key for Code Exchange: a one-time secret the client generates so a stolen auth code can't be redeemed by anyone else.), so there is no third-party auth SDK in the browser and no password ever reaches us.

The flow

  1. Start

    A sign-in hits /auth/sso/{slug}/start, which sets the state, nonce, and PKCE verifier in HttpOnly cookies and redirects to your IdP's authorize endpoint.

  2. Authenticate

    Your IdP authenticates the user and redirects back to /auth/sso/{slug}/callback with an authorization code.

  3. Exchange and verify

    The callback validates state (CSRF), exchanges the code using the PKCE verifier and client secret, and verifies the id_token against your IdP's JWKS (issuer and audience checked).

  4. Link and mint a session

    The verified email/subject is linked to a First Six record, and an HttpOnly session cookie is minted. That cookie is the only thing that persists; every later read is authorized by it through row-level security.

Per-tenant configuration

Each institution has an sso_config object holding the public parts of the connection:

{
  "issuer": "https://login.microsoftonline.com/<tenant-id>/v2.0",
  "client_id": "<application-client-id>",
  "discovery_url": "https://login.microsoftonline.com/<tenant-id>/v2.0/.well-known/openid-configuration",
  "scopes": "openid email profile"
}

When discovery_url is set it resolves the authorize, token, and JWKS URLs for you, so you usually only need the issuer, client id, discovery URL, and scopes (which must include openid).

The client secret is never in the config row

Only the public connection details live in the tenant config. The OIDC client secret is held in server-side environment configuration, not in the database, so it is never readable by the application clients that can read the rest of the config.

Microsoft Entra

For Microsoft Entra ID, the connection is:

  1. Register the app

    Register First Six as an application in Entra and add the callback URL as a redirect URI. PKCE is mandatory for confidential clients on Entra.

  2. Collect the details

    Take the Application (client) ID and your tenant id, and generate a client secret.

  3. Hand them to us

    Give us the client id, the secret, and the tenant id (or the discovery URL). We store the secret server-side and put the rest in sso_config.

Linking, not creating

This is the key behaviour to understand: a successful login links an identity to an existing record. It does not create one.

claim_sso_identity matches the verified email (or IdP subject) to a student or staff row that already exists, and binds the auth identity to it. This is just-in-time linkingJust-in-time linking: the account is connected at first login, but only if a matching record already exists., not just-in-time creation. If there is no matching row, the login fails. So the roster has to be in place first, which is what SIS sync is for. Provision the roster, then turn on SSO.

Troubleshooting

A failed sign-in shows a plain SSO error: page carrying the specific failure, worded below, so you can match what the user saw to the fix. IdP errors are surfaced verbatim (capped in length) for the same reason.

The IdP shows an error before First Six ever loads

First Six was never reached, so this is the app registration. Nine times out of ten the redirect URI: it must be exactly https://<your-first-six-host>/auth/sso/{slug}/callback with your institution's slug, character for character, including the scheme. Check for a trailing slash, a www. difference, or a staging host left in from testing.

SSO error: Token exchange failed

The page includes the IdP's own error text. invalid_client means the client secret is wrong or has expired (Entra secrets expire; check the app registration's Certificates and secrets blade and issue a new one). invalid_grant usually means the authorization code was already used or timed out: have the user start again from the sign-in page rather than refreshing the callback URL.

SSO error: id_token verification failed

The token came back but did not verify. Check the config trio against the app registration: the issuer must match what your discovery document declares (for Entra, the tenant-specific v2.0 issuer, not the common one), and the client_id must be the Application ID the token is minted for, since it is checked as the token's audience.

SSO error: Nonce mismatch

The one-shot sign-in cookies did not survive the round trip. A stale tab replaying an old callback does this, as does a browser blocking cookies on the First Six domain. Start a fresh sign-in from the login page; if it repeats, check for an extension or policy stripping HttpOnly cookies.

The IdP sign-in succeeds but First Six cannot link the account

Linking, not creating: there was no roster row matching the identity the IdP asserted. Either the student has not been synced yet (run SIS sync first), or the email your IdP asserts differs from the one on the roster: a personal alias, a renamed account, or preferred_username differing from email. The roster email and the asserted email must agree.

You are not authorised to sign in here.

The identity linked, but to a role this app refuses: a student account at the staff console door, or a non-owner at the operations console. That refusal is deliberate and happens before any session exists. Sign in to the app that matches the account.

Common questions

Do you support SAML?

The production path is OIDC. A SAML proof-of-concept exists, but converging a real SAML-federated institution onto the same identity-linking path is follow-up work. SAML is available per engagement: production SAML is enabled as part of an engagement that needs it, so raise it early in scoping if it's a hard requirement.

What happens if a user signs in before they're on the roster?

The login fails, because there's no record to link to. Run SIS sync first so every student resolves to a row, then enable SSO.

Which claim do you match on?

The verified email (or the IdP subject). Make sure the email your IdP asserts matches the email on the roster you sync.

Testing before you go live

You do not need your real IdP wired up to exercise this end to end. First Six ships a gated test identity provider (FakeU) that mints valid tokens against demo personas. See testing SSO before launch.

Next steps

Was this helpful?
Need more help?

The fastest answer is usually one question away.

Contact us
Edit this page on GitHub