OIDC Integration#
The matrix-js-sdk implements OpenID Connect (OIDC) delegated authentication, allowing Matrix clients to delegate identity and authorization to an external OIDC provider (OP). The implementation lives entirely under src/oidc/ and is marked @experimental throughout . The primary spec reference is MSC2965.
The module is organized as follows:
| File | Purpose |
|---|---|
discovery.ts | Issuer metadata discovery and validation |
validate.ts | OpenID metadata and token validation |
authorize.ts | Authorization URL generation and code exchange |
tokenRefresher.ts | Token refresh lifecycle |
register.ts | Dynamic client registration |
error.ts | OidcError enum |
index.ts | Public exports, OidcClientConfig interface |
Issuer Discovery#
Primary entry point: MatrixClient.getAuthMetadata()#
The preferred API is MatrixClient.getAuthMetadata(), which:
- Tries the stable
GET /auth_metadataendpoint (Matrix v1.15+,ClientPrefix.V1) - On
M_UNRECOGNIZED, falls back togetAuthIssuer()+discoverAndValidateOIDCIssuerWellKnown()for older homeservers - Passes the raw metadata through
validateAuthMetadataAndKeys()in all paths
Legacy: discoverAndValidateOIDCIssuerWellKnown()#
discoverAndValidateOIDCIssuerWellKnown(issuer) is deprecated in favour of getAuthMetadata. It:
- Constructs the
.well-knownURL (see below) - Fetches it with a 5-second timeout
- Delegates to
validateAuthMetadataAndKeys()
validateAuthMetadataAndKeys()#
validateAuthMetadataAndKeys(authMetadata):
- Validates the raw metadata object via
validateAuthMetadata(), which checks required fields (issuer,authorization_endpoint,token_endpoint,revocation_endpoint) and capability arrays (response_types_supported,grant_types_supported,code_challenge_methods_supported) per the OIDC Discovery spec - Creates a temporary
OidcClientSettingsStore(fromoidc-client-ts) and usesMetadataService.getSigningKeys()to fetch JWKS — but only whenjwks_uriis present; otherwisesigningKeysis set tonull - Returns an
OidcClientConfig—ValidatedAuthMetadataextended withsigningKeys
Well-Known URL Construction and Path Resolution#
The .well-known URL is built with the two-argument JavaScript URL constructor :
new URL(".well-known/openid-configuration", issuer)
Key nuance: JavaScript's URL constructor resolves the first argument as a relative reference per RFC 3986. For a relative path without a leading /, the resolution replaces the last path segment of the base:
issuer value | Resolved URL |
|---|---|
https://auth.example.com/ | https://auth.example.com/.well-known/openid-configuration ✅ |
https://auth.example.com/realm/ | https://auth.example.com/realm/.well-known/openid-configuration ✅ |
https://auth.example.com/realm | https://auth.example.com/.well-known/openid-configuration ⚠️ |
If the issuer has a multi-segment path without a trailing slash (e.g. https://auth.example.com/realm), the final segment (realm) is treated as a "file" and replaced by .well-known/..., effectively stripping the last path component. Test fixtures and the codebase consistently use issuers with a trailing slash (e.g. "https://auth.org/") , and the JSDoc on discoverAndValidateOIDCIssuerWellKnown only illustrates the root-hostname case . Always ensure the issuer string ends with a trailing slash when it contains a path.
Token Refresh#
OidcTokenRefresher manages the access-token refresh lifecycle:
- Constructed with
issuer,clientId,redirectUri,deviceId, andidTokenClaims - Initialization is lazy —
oidcClientReadyis a publicPromise<void>that resolves once the internaloidc-client-tsOidcClientis configured viainitialiseOidcClient() doRefreshAccessToken(refreshToken)deduplicates concurrent refresh calls via a single in-flight promise; OIDCErrorResponseerrors are re-thrown asTokenRefreshLogoutErrorto signal the session should be terminatedpersistTokens(tokens)is a no-op by design; override it to persist the new tokens to storage- Token expiry is calculated from the request start time (not server receipt time) to be conservative
In Element Web, a concrete TokenRefresher subclass is wired into MatrixClientPeg during doSetLoggedIn() for sessions that have a stored refresh token .
Authorization Flow#
generateOidcAuthorizationUrl() builds the auth-code PKCE redirect URL and stores user state (homeserver URL, nonce) in sessionStorage. completeAuthorizationCodeGrant() exchanges the code, validates the ID token (now optional — id_token may be omitted by the OP ), and returns BearerTokenResponse + idTokenClaims.
The scope is generated by generateScope(deviceId), producing: openid urn:matrix:org.matrix.msc2967.client:api:* urn:matrix:org.matrix.msc2967.client:device:{deviceId}.
Both functions support response_mode=fragment in addition to the default query mode .
Error Handling#
All OIDC-specific errors are defined in OidcError, including OpSupport (metadata validation failure), InvalidIdToken, InvalidBearerTokenResponse, DynamicRegistrationFailed, and MissingOrInvalidStoredState. Token refresh OIDC errors surface as TokenRefreshLogoutError to drive automatic logout.