Dosu LogoDosu Logo
Ask
Join our Discord
element-webPublic
Element
Documentselement-web
OIDC Integration
OIDC Integration
Type
Topic
Status
Published
Created
Jul 16, 2026
Updated
Jul 16, 2026
Created by
Dosu Bot
Updated by
Dosu Bot

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:

FilePurpose
discovery.tsIssuer metadata discovery and validation
validate.tsOpenID metadata and token validation
authorize.tsAuthorization URL generation and code exchange
tokenRefresher.tsToken refresh lifecycle
register.tsDynamic client registration
error.tsOidcError enum
index.tsPublic exports, OidcClientConfig interface

Issuer Discovery#

Primary entry point: MatrixClient.getAuthMetadata()#

The preferred API is MatrixClient.getAuthMetadata(), which:

  1. Tries the stable GET /auth_metadata endpoint (Matrix v1.15+, ClientPrefix.V1)
  2. On M_UNRECOGNIZED, falls back to getAuthIssuer() + discoverAndValidateOIDCIssuerWellKnown() for older homeservers
  3. Passes the raw metadata through validateAuthMetadataAndKeys() in all paths

Legacy: discoverAndValidateOIDCIssuerWellKnown()#

discoverAndValidateOIDCIssuerWellKnown(issuer) is deprecated in favour of getAuthMetadata. It:

  • Constructs the .well-known URL (see below)
  • Fetches it with a 5-second timeout
  • Delegates to validateAuthMetadataAndKeys()

validateAuthMetadataAndKeys()#

validateAuthMetadataAndKeys(authMetadata):

  1. 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
  2. Creates a temporary OidcClientSettingsStore (from oidc-client-ts) and uses MetadataService.getSigningKeys() to fetch JWKS — but only when jwks_uri is present; otherwise signingKeys is set to null
  3. Returns an OidcClientConfig — ValidatedAuthMetadata extended with signingKeys

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 valueResolved 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/realmhttps://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, and idTokenClaims
  • Initialization is lazy — oidcClientReady is a public Promise<void> that resolves once the internal oidc-client-ts OidcClient is configured via initialiseOidcClient()
  • doRefreshAccessToken(refreshToken) deduplicates concurrent refresh calls via a single in-flight promise; OIDC ErrorResponse errors are re-thrown as TokenRefreshLogoutError to signal the session should be terminated
  • persistTokens(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.

Documents
Desktop File WM Class Configuration
Desktop Window Management
Drag and Drop
E2EE Key Management
Element Call Integration
Element Web Labs Feature Gating
Encryption Reset
IndexedDB Crypto Store
Internationalization & Localization
Link Preview
Matrix Notification System
Matrix Power Levels
Matrix RTC Infrastructure
Message Delivery State
Message Rendering Pipeline
OIDC Integration
Poll Rendering
Rageshake Logging
Read Receipts
Resizable Panels
Resize Handle Interaction
Room Invite Flows
Room List Section Management
Room State Synchronization
Session Restoration
Space Panel
Unread Filter
User Profile Caching
Virtualized List Rendering
Voice Message Audio Decoding
Widget Iframe Management
Windows Desktop Integration