Skip to content

OIDC login

OIDC login lets Auth redirect a user to an OpenID Connect provider, verify the callback, link the provider identity, and create a Layeron Auth session.

Auth uses the provider discovery document, PKCE S256, a one-time state value, an OIDC nonce, JWKS signature verification, and ID token claim validation.

Add an OIDC provider to the Auth module.

Terminal window
import { auth } from "@layeron/modules"
const appAuth = auth({
providers: [
{
provider: "oidc",
id: "acme",
issuer: "https://idp.example.com",
clientId: "client_123",
clientSecret: process.env.OIDC_CLIENT_SECRET,
redirectToAllowlist: ["https://app.example.com"],
},
],
})

The provider id is the value used in runtime calls. The issuer must use HTTPS, and the discovery document issuer value must match the configured issuer exactly.

The default scopes are openid, email, and profile. Custom scopes must include openid.

Create the authorization URL from a public route, then redirect the browser to the returned URL.

Terminal window
app.post("/auth/oidc/start", { auth: "public" }, async () => {
const result = await appAuth.oauth.createAuthorizationUrl({
provider: "acme",
callbackUrl: "https://api.example.com/auth/oidc/callback",
redirectTo: "https://app.example.com/dashboard",
})
return Response.redirect(result.authorizationUrl, 302)
})

Auth stores only the state hash, the PKCE verifier, the nonce hash, the callback URL, and the optional redirectTo value in the Auth state database.

Pass the provider id, authorization code, and state returned by the provider to Auth.

Terminal window
app.get("/auth/oidc/callback", { auth: "public" }, async ({ request }) => {
const url = new URL(request.url)
const result = await appAuth.oauth.verifyCallback({
provider: "acme",
code: url.searchParams.get("code") ?? "",
state: url.searchParams.get("state") ?? "",
callbackUrl: "https://api.example.com/auth/oidc/callback",
})
return Response.redirect(result.redirectTo ?? "/", 302)
})

Auth exchanges the code at the provider token endpoint, verifies the ID token signature with the provider JWKS, checks iss, aud, exp, iat, nbf, and nonce, then creates a Layeron Auth session.

In managed and managed_core mode, Auth creates the user row when the OIDC identity is first seen. Auth stores emailVerifiedAt only when the provider sets the configured email verification claim to true.

In custom, mapped, and external mode, Auth uses the configured userIdClaim as the application user id. The default claim is sub. Your user resolver or mapped user table must already return a user for that id before Auth creates the session.

Auth links repeat logins by the configured provider id and the OIDC sub claim. Auth does not automatically link a new OIDC identity to an existing user by email address.

redirectTo may be a relative path such as /dashboard. Absolute URLs must use HTTPS and must match an origin in redirectToAllowlist.

When clientSecret is set, Auth uses client_secret_basic by default. Set tokenEndpointAuthMethod to client_secret_post when the provider requires the secret in the token request body. Set it to none for public PKCE clients.