Concepts
Identity & Sessions
How the LSC platform models users across identity providers, links accounts, and delivers a stable canonical identity.
The identity model
Every user in the LSC platform has two layers of identity: an external identity issued by an OIDC provider, and a canonical identity owned by the platform.
A (provider, subject) pair returned by an OIDC provider on sign-in — for example, Microsoft Entra issuing a sub claim for a corporate account. The platform may know a user by multiple external identities.
A stable, platform-owned UUID called the lsc_id. It is minted once on the user's first sign-in and never changes, even if the underlying provider account changes or additional providers are linked.
A user may sign in with multiple providers (e.g. Microsoft and Google). All linked accounts resolve to the same lsc_id. The platform stores each (providerId, subject) pair and maps them to the canonical identity.
How lsc_id is minted
When a user signs in for the first time, the IDP creates a canonical identity record and generates an lsc_id. On subsequent sign-ins the existing record is looked up by (providerId, subject) and the same lsc_id is returned. The mint is idempotent — running it twice for the same user produces the same result.
- 1
User authenticates with a provider
The IDP receives the OIDC callback and extracts (providerId, subject) from the ID token.
- 2
Lookup existing canonical identity
The IDP queries the identity store by (providerId, subject). If a record exists, return its lsc_id.
- 3
Mint on first sign-in
If no record exists, generate a ULID-format UUID, write a new canonical_identities row, and return the new lsc_id.
- 4
Attach to the session
The lsc_id is added to the session payload before the session cookie is written. It also appears in the bridge token for first-party app consumers.
Account linking
A single user can authenticate with multiple identity providers. When a second provider is linked, the platform creates an additional account record pointing to the same lsc_id. From that point on, either provider resolves to the same canonical identity.
canonical_identities
lsc_id: "01JN84K2X3MHFYTC7Q6P0WVRAE"
accounts linked:
providerId: "microsoft" subject: "aad|abc123"
providerId: "google" subject: "108203948572635849021"The security implication: tenant gating on the IDP checks all linked Microsoft accounts, not just the one used for the current sign-in. A user cannot bypass a tenant restriction by signing in with a non-Microsoft provider.
Sessions
Sessions are managed by the LSC Identity Provider. After a successful sign-in the IDP writes an encrypted session cookie and, for first-party apps, issues a short-lived bridge token.
An httpOnly cookie set by the IDP. Contains the user's sub, email, name, and lsc_id. Valid for the IDP domain only.
A separate httpOnly cookie containing only the lsc_id, shared across LSC subdomains. First-party apps read this to identify users without a full token exchange.
A short-lived signed JWT issued by the IDP after sign-in. First-party apps exchange it to establish their own local session. Contains lsc_id as a claim.
Consuming the lsc_id
External apps receive the lsc_id as a claim in the OIDC ID token after a successful token exchange. Include openid profile email in your scope request — the IDP adds lsc_id automatically.
// ID token payload (after jwtVerify)
{
"sub": "aad|abc123",
"email": "user@company.com",
"name": "Jane Smith",
"lsc_id": "01JN84K2X3MHFYTC7Q6P0WVRAE",
"iss": "https://auth.lifescienceconnect.com",
"aud": "your-client-id",
"exp": 1749999999
}