OIDC Client for JavaScript
Library to provide OpenID Connect (OIDC) and OAuth2 protocol support for client-side, browser-based JavaScript client applications. Also included is support for user session and access token management.
| ID | oidc-client-ts |
| Version | 2.4.0 |
| Source | modules/oidc-client-ts |
Dependencies
Used by
Documentation
oidc-client-ts — client-side OIDC (PKCE public)
Browser-side OpenID Connect / OAuth2 login for xOpat, built on the vendored
oidc-client-ts library. It runs the
whole flow in the browser as a PKCE public client (no client_secret),
obtains a token, and hands it to XOpatUser so HttpClient "just works". It is
the default OIDC provider.
For IdPs that require a confidential client (a
client_secret), use the server-sideoidc-server-tsmodule instead — a secret shipped to the browser is insecure (this module warns and still proceeds PKCE-style). The canonical auth model is documented insrc/AUTH.md.
Purpose
- Perform automated login + silent token refresh against an OIDC IdP.
- Register an
"oidc"broker into the core auth registry (APPLICATION_CONTEXT.auth,XOpatAuth), so features can require login for a named context without touching OIDC code. - Attach the obtained JWT to upstream requests (via
XOpatUser+HttpClient).
Behavior
- Auto-declared contexts (
auth-broker.js): at boot the broker reads this module's public static config and declares each context intoAPPLICATION_CONTEXT.auth— so the main viewer login needs no plugin or feature code (this replaced the removedoidc-authplugin). Convention: the default / main context = the main viewer identity (updates the appbar user + the defaultXOpatUser/HttpClientcontext); any other id is a sub-identity. Key the default context as""/null/"core"(all equivalent — they canonicalize to"core"and fire the barelogin/secret-updatedevents; seesrc/AUTH.md). Shape:A legacy bare top-level"modules": { "oidc-client-ts": { "permaLoad": true,"contexts": {"core": { // "" / null / "core" → main identity"oidc": { "authority": "...", "client_id": "...", "scope": "..." },"authMethod": "redirect", // "redirect" | "popup""tokenForServer": "access_token", // or "id_token""serviceName": "...", "usesStore": "default"// "isMain": true // implied for "core"// "autoLogin": false // declare WITHOUT the boot login}}}}}oidcblock (+method) is accepted as thecorecontext for back-compat.OIDCAuthClient.init()auto-logs-in when there is no session (redirect/popup), so a declaredcorecontext logs the user in at boot. - Broker registration (
auth-broker.js): registers"oidc"intoAPPLICATION_CONTEXT.auth. A feature may ALSO declare a (sub-)context in code — e.g. inpluginReady— and then gate on it:await APPLICATION_CONTEXT.auth.configureContext({contextId: "anthropic", // XOpatUser sub-context + token key + verifier idmethod: "oidc",config: { authority, client_id, scope }, // the OIDC block (see below)serviceName: "Anthropic Chat",authMethod: "popup", // "popup" (default) | "redirect"tokenForServer: "id_token" // which token the server verifies});if (!APPLICATION_CONTEXT.auth.isAuthenticated("anthropic")) {await APPLICATION_CONTEXT.auth.login("anthropic"); // interactive} - One
OIDCAuthClientper context (oidc-auth.js), each with its own authority/client_id/scope. These are sub-contexts (updateXOpatUser: false) — not the main viewer identity. - Flows:
authMethod: "popup"(default; opens a new tab, keeps the workspace) or"redirect"(full-page).login()resolves viaXOpatUserevents, not the broker promise, because a redirect unloads the page — completion is detected here and on reload. redirect_uri: if not set, defaults to the current page URL stripped of?query/#hash(origin + pathname);popup_redirect_uridefaults to it. So the URL you register with the IdP is the page the viewer loads at (e.g.http://localhost:9000/). Setredirect_uriexplicitly to pin it.- Server-side verification (
register.server.ts): registers the"oidc"RS256/JWKS verifier for RPC and proxy — incoming Bearer tokens are checked against the IdP JWKS. Core stays auth-agnostic; the verifier ships with this module and is mounted once at boot (loadServerExtensions).
Configuration
1. The client OIDC block (per context)
The oidc block inside a contexts.<ctx> entry (see Behavior), or passed as
config to configureContext when a feature declares a context in code:
"oidc": {
"authority": "https://accounts.google.com", // IdP base (issuer)
"client_id": "<oauth-client-id>",
"scope": "openid email profile",
// "redirect_uri": "http://localhost:9000/", // optional — pin instead of page URL
"confidential": false // must be false; a secret warns
}
Register with the IdP (Google Console → Authorized redirect URIs): the
redirect URI = the page URL the viewer loads at (or your explicit
redirect_uri), and add the origin under Authorized JavaScript origins.
2. The server verifier (per context)
Under core.server.secure.rpcVerifiers.<contextId> (and/or proxies.<alias>.auth):
"rpcVerifiers": {
"anthropic": {
"verifiers": { "oidc": {
"jwksUri": "https://www.googleapis.com/oauth2/v3/certs",
"issuer": "https://accounts.google.com",
"audience": "<client_id>"
} },
"mode": "all"
}
}
Which token — tokenForServer
tokenForServer selects the token stored as the XOpatUser "jwt" secret (default
"access_token"). Pick it by who consumes the token — an upstream API called
directly (→ access_token, and add the API's scope) vs. our own RS256/JWKS
verifier (→ a JWT; Google's is the id_token). The full decision rule + pitfalls
are in src/AUTH.md
— e.g. DICOM against Google Healthcare needs access_token +
.../auth/cloud-healthcare in scope, or it 401s after login.
Security
Auth/OIDC config is deployment-trusted — read it with getStaticMeta
(ENV/include.json), never getOption (session/third-party controllable). Never
put a client_secret here (it would ship to the browser). See AGENTS.md §3/§7
and src/AUTH.md.