Concepts
Auth Flow
The LSC platform supports two integration patterns. External and vendor apps use standard OIDC. First-party LSC apps use a session bridge that skips the redirect loop.
Pattern 1 — OIDC (external apps)
Any application with a registered client_id can authenticate users through the LSC Identity Provider using the standard OpenID Connect authorization-code flow with PKCE. The IDP handles provider selection, MFA, and corporate SSO. Your app only sees the resulting ID token.
- 1
User hits a protected route
Your middleware detects no local session and redirects to your login route, passing ?callbackURL= so the user lands back where they started.
- 2
Login route generates PKCE and redirects to the IDP
Server-side: fetch the discovery document, generate code_verifier + code_challenge + state + nonce, store them in short-lived httpOnly cookies, and issue a 302 to the IDP authorize endpoint.
- 3
IDP authenticates the user
The IDP shows its own login UI — Microsoft Entra SSO by default. Your app is not involved. The user's corporate credentials are never sent to your application.
- 4
Browser returns to your callback with ?code=&state=
Read the stored state cookie and verify it matches. A mismatch means the request was not initiated by your app — abort immediately.
- 5
Server-to-server token exchange
POST to the IDP token endpoint with the code, code_verifier, client_id, client_secret, and redirect_uri. Receive an id_token. This exchange happens entirely on your server — the browser never sees the token.
- 6
Verify the ID token and create a local session
Verify the id_token signature using the IDP JWKS endpoint. Extract sub, email, name, and lsc_id. Write an encrypted local session cookie (AES-256-GCM). Redirect to the original callbackURL.
IDP endpoints — all derived from the discovery document at https://dev-auth.lifescienceconnect.com/.well-known/openid-configuration/api/auth. Fetch it at startup and cache — it changes rarely.
See the OIDC Integration example for complete, runnable route code.
Pattern 2 — Session bridge (first-party apps)
First-party apps hosted on LSC domains share a session with the IDP and use a bridge token handshake instead of a full OIDC redirect. This avoids the round-trip to the IDP authorize endpoint on every app load.
- 1
App loads — check for an existing local session
If the app's own encrypted session cookie is present and valid, the user is considered authenticated. No IDP call needed.
- 2
No local session — check for a bridge token
The IDP sets a short-lived signed bridge token in a shared cookie after sign-in. The app checks for this cookie on the callback route.
- 3
Exchange the bridge token
Verify the bridge token signature using the platform JWKS. Extract the lsc_id and user claims. Write the app's own encrypted local session. The bridge token is consumed — set it to expire.
- 4
No bridge token — redirect to the IDP
If neither session nor bridge token is present, redirect the user to the IDP sign-in page. After authentication the IDP issues a new bridge token and redirects back.
For external integrations — use OIDC (Pattern 1). The session bridge is an internal mechanism for first-party LSC apps sharing the IDP domain. External apps cannot access the bridge cookie.
Token types
sub, email, name, lsc_id, iss, aud, exp
Returned by the token exchange. Verify with IDP JWKS. Never store raw — extract claims into your own session.
lsc_id, user claims, nonce
Shared-cookie mechanism for first-party apps only. Single-use.
Whatever your app extracted from the ID token or bridge token
AES-256-GCM encrypted, httpOnly cookie. Your app owns this — the IDP never sees it again.
Security checklist
- Always verify state before exchanging the code. A mismatch is a sign of a CSRF attempt.
- Token exchange is server-to-server only. Never call the token endpoint from the browser — it requires your
client_secret. - Never store the raw id_token in a cookie. Extract the claims you need and re-encrypt a short-lived local session.
- Do not call /api/auth/sign-in/social directly. That is an internal IDP endpoint for first-party LSC apps. External apps must always go through /api/auth/oauth2/authorize.
- Validate callbackURL before redirecting. If your login route accepts a
?callbackURL=parameter, anchor it to your own origin before using it — otherwise it is an open redirect. Usenew URL(callbackURL, request.url)and verify the resulting.originmatches your app before issuing the redirect.