@axa-fr/oidc-client

中文文档
7.29.8发布于 2026/10/15MITTypeScriptESM + CJS构建来源已验证近 30 天下载 18万

OpenID Connect & OAuth authentication using native javascript only, compatible with angular, react, vue, svelte, next, etc.

原始使用文档
文档目录17 节
  1. Quick start
  2. Call a protected API
  3. Service worker
  4. Install and update the worker
  5. Trusted-domain options
  6. Configuration
  7. Silent login
  8. Token renewal
  9. Pushed Authorization Requests (PAR)
  10. DPoP
  11. API
  12. Errors and events
  13. Token synchronization events
  14. Missing or mismatched login state
  15. Named configurations and routing
  16. Service-worker protocol
  17. Examples and further reading

@axa-fr/oidc-client

A framework-independent OpenID Connect (OIDC) client for browser applications, written in TypeScript. It supports Authorization Code Flow with PKCE, session restoration, token renewal, multiple named configurations, optional service-worker token isolation, Pushed Authorization Requests (PAR), and DPoP.

For React applications, use @axa-fr/react-oidc.

Quick start

npm install @axa-fr/oidc-client

Register a public browser client with your identity provider:

  1. Enable Authorization Code Flow with PKCE.
  2. Register your application's exact callback URL, such as https://app.example.com/authentication/callback, and its post-logout URL.
  3. Allow your application's origin to call the provider's browser-accessible endpoints through CORS.
  4. Choose the scopes your application needs. Request offline_access only if your provider uses it to issue refresh tokens.

Never put a client secret in browser code. Replace the example issuer and client ID below with your own values. Configure your web server to serve the application on the callback route as well as /.

Add these elements to your page:

<p id="session-status" role="status">Loading session…</p>
<button id="login" type="button" hidden>Sign in</button>
<button id="logout" type="button" hidden>Sign out</button>

Run this module when those elements are available, including on the callback route:

import { OidcClient } from '@axa-fr/oidc-client';

const configuration = {
  client_id: 'your-public-client',
  authority: 'https://issuer.example.com',
  redirect_uri: `${window.location.origin}/authentication/callback`,
  scope: 'openid profile',
};

const oidcClient = OidcClient.getOrCreate(() => fetch)(configuration);
const status = document.getElementById('session-status');
const loginButton = document.getElementById('login');
const logoutButton = document.getElementById('logout');

function showError() {
  status.textContent = 'Authentication could not be completed. Please try again.';
}

loginButton.addEventListener('click', () => {
  oidcClient.loginAsync('/').catch(showError);
});
logoutButton.addEventListener('click', () => {
  oidcClient.logoutAsync('/').catch(showError);
});

async function start() {
  if (window.location.pathname === new URL(configuration.redirect_uri).pathname) {
    const { callbackPath } = await oidcClient.loginCallbackAsync();
    window.history.replaceState(null, '', callbackPath || '/');
  } else {
    await oidcClient.tryKeepExistingSessionAsync();
  }

  const isAuthenticated = oidcClient.tokens != null;
  status.textContent = isAuthenticated ? 'Signed in' : 'Not signed in';
  loginButton.hidden = isAuthenticated;
  logoutButton.hidden = !isAuthenticated;
}

start().catch(showError);

This minimal example uses browser storage, not a service worker. Read the next section before choosing a token-storage strategy for production.

Call a protected API

Use the client's fetch wrapper after authentication:

const oidcFetch = oidcClient.fetchWithTokens(fetch);
const response = await oidcFetch('https://api.example.com/profile');

if (!response.ok) {
  throw new Error(`API request failed with status ${response.status}`);
}
const profile = await response.json();

The wrapper attaches an access token, or a token placeholder when the service worker hides it, and integrates with token renewal. Only use it for API URLs you trust: the wrapper itself is not a destination allowlist. HTTP error responses still need an explicit response.ok check.

Service worker

The optional service worker can keep access and refresh tokens outside the application's JavaScript context and attach access tokens to configured API requests. It does not prevent XSS: injected code can still act through your application and make requests. Continue to use normal XSS defenses, carefully restrict trusted destinations, and avoid rendering or logging raw tokens.

sequenceDiagram
    participant App as Browser application
    participant IdP as Identity provider
    participant SW as OIDC service worker
    participant API as Trusted API
    App->>IdP: Authorization request with PKCE
    IdP-->>App: Redirect with authorization code
    App->>SW: Token request
    SW->>IdP: Exchange code and verifier
    IdP-->>SW: Tokens
    SW-->>App: Token placeholders and session information
    App->>SW: Protected API request
    SW->>API: Request with access token
    API-->>App: Response through service worker

The diagram shows worker mode with access-token hiding enabled. The ID token and decoded claims may still be available to application code.

Install and update the worker

node ./node_modules/@axa-fr/oidc-client/bin/copy-service-worker-files.mjs public

Replace public with your application's static-assets directory, and ensure that directory exists before running the command. This creates or updates OidcServiceWorker.js and creates OidcTrustedDomains.js if it does not already exist. Keep the worker version aligned with the client package; for example, merge this script into your package.json:

{
  "scripts": {
    "postinstall": "node ./node_modules/@axa-fr/oidc-client/bin/copy-service-worker-files.mjs public"
  }
}

Edit public/OidcTrustedDomains.js, not the generated worker:

const trustedDomains = {
  default: {
    oidcDomains: [/^https:\/\/issuer\.example\.com(?:\/|$)/],
    accessTokenDomains: [/^https:\/\/api\.example\.com\//],
  },
};

Include the provider's endpoint origins if they differ from its issuer URL. Keep access-token destinations as narrow as possible; patterns can include paths. String entries are interpreted as regular-expression prefixes, not exact origin matches. The example uses anchored regular expressions with escaped dots and hostname boundaries to avoid unintended matches. The default key must match the OIDC configuration name.

Add these options to your client configuration:

service_worker_relative_url: '/OidcServiceWorker.js',
service_worker_only: true,

Serve the worker from a URL whose scope covers your application, using HTTPS (or localhost for development). With service_worker_only: true, login requires an available service worker. With false (the default), the client can fall back to browser storage when worker mode is unavailable. Choose that fallback deliberately.

Trusted-domain options

These options belong in each named entry in OidcTrustedDomains.js, not in OidcConfiguration.

OptionPurpose
oidcDomainsProvider URLs the worker is allowed to handle.
accessTokenDomainsAPI URLs to which the worker may attach an access token.
domainsShorthand for a shared provider/API destination list; separate lists give finer control.
showAccessTokenDefaults to false. Set to true only when an integration requires the real access token in application JavaScript; refresh tokens remain hidden in worker mode.
allowMultiTabLoginIsolates login state, nonce, and PKCE verifier by tab. Requires the OIDC fetch wrapper for protected API requests; see below.
convertAllRequestsToCorsExceptNavigateDefaults to false; optionally changes non-navigation requests to CORS mode.
setAccessTokenToNavigateRequestsDefaults to true; controls token attachment to matching navigation requests.
bypassAllNonOidcRequestsDefaults to false. When enabled for all initialized worker configurations, requests outside OIDC and access-token destinations are left to the browser.
demonstratingProofOfPossessionEnables worker-side DPoP for this configuration.
demonstratingProofOfPossessionOnlyWhenDpopHeaderPresentDefaults to true when worker DPoP is enabled. Requires a DPoP header before worker-side DPoP injection; set to false to remove that condition.
demonstratingProofOfPossessionConfigurationOverrides the worker's cryptographic algorithms; see DPoP.

With allowMultiTabLogin: true, use oidcClient.fetchWithTokens(fetch) for protected API requests. Its tab-specific token placeholder tells the worker which session to use. Plain fetch or a default Axios request does not provide that marker, so automatic token attachment cannot select the tab's token and requests may receive HTTP 401. Use the OIDC fetch directly, or an integration that actually routes requests through it.

Configuration

The public OidcConfiguration type is the complete reference. Required fields are client_id, authority, redirect_uri, and scope.

OptionDefault / behavior
authority_configurationSupply endpoint metadata instead of discovery. See AuthorityConfiguration in the type reference, including PAR metadata.
storagesessionStorage when worker storage is not used. localStorage persists across tabs and browser sessions, but both are accessible to application JavaScript.
login_state_storageDefaults to storage; separates authorization state, verifier, nonce, and login parameters from token storage. If tokens use localStorage, consider sessionStorage here to avoid cross-tab login races.
silent_redirect_uriEnables the iframe-based silent-login flow. Must differ from redirect_uri; register it with the provider.
silent_login_uriPage that starts silent login; derived from silent_redirect_uri if omitted.
silent_login_timeout12000 milliseconds.
refresh_time_before_tokens_expiration_in_second120 seconds; the client applies random jitter when this setting is greater than 60.
token_renew_mode'access_token_or_id_token_invalid'; alternatives are 'access_token_invalid' and 'id_token_invalid'.
token_automatic_renew_modeTokenAutomaticRenewMode.AutomaticBeforeTokenExpiration; see token renewal.
token_request_timeoutToken-request timeout in milliseconds.
extrasAdditional authorization-request parameters, such as { prompt: 'consent' }.
token_request_extrasAdditional token-request parameters. Never use this to embed a browser client secret.
par, par_request_timeout'disabled' and 10000 milliseconds; see PAR.
authority_time_cache_wellknowurl_in_secondDiscovery-cache lifetime; defaults to one hour.
authority_timeout_wellknowurl_in_millisecondDiscovery timeout; defaults to 10000 milliseconds.
monitor_sessionfalse; enables OIDC session monitoring when supported by the provider and browser.
logout_tokens_to_invalidate['access_token', 'refresh_token']; token types to revoke during standard logout.
preload_user_infofalse; fetch user information during login/session restoration instead of waiting for a consumer.
demonstrating_proof_of_possessionfalse; see DPoP.
demonstrating_proof_of_possession_configurationCryptographic settings for client-side DPoP.
service_worker_relative_urlURL of the worker; omit to disable worker mode.
service_worker_onlyfalse; disallows browser-storage fallback when true.
service_worker_keep_alive_path'/'; path used by worker keep-alive requests.
service_worker_activateFunction to override the default browser-based activation decision.
service_worker_registerCustom (url) => Promise<ServiceWorkerRegistration> registration function.
loading_timeout_msUsed by the React provider's loading watchdog; see the React guide.

Silent login

Silent login uses an iframe and the provider's existing session. It depends on provider support and browser cookie/privacy policies; it is not a guaranteed replacement for refresh tokens.

For a vanilla application, implement both the silent_login_uri page and the silent_redirect_uri callback page. The login page starts loginAsync with isSilentSignin: true (the third argument); the callback page calls silentLoginCallbackAsync(). Keep these routes separate from the interactive callback. The React provider handles these routes for you; its silent-login route implementation is also a reference for forwarding silent-login parameters.

Token renewal

By default, the client schedules renewal before tokens expire. It uses a refresh token when available, or the configured silent-login flow. Refresh-token issuance and lifetime remain provider decisions.

For renewal only when an API request needs it:

import { TokenAutomaticRenewMode } from '@axa-fr/oidc-client';

const renewalOptions = {
  token_automatic_renew_mode: TokenAutomaticRenewMode.AutomaticOnlyWhenFetchExecuted,
};

Merge this option into your configuration and use fetchWithTokens(fetch) or getValidTokenAsync() before protected requests. Plain fetch does not trigger this renewal mode.

  • renewTokensAsync(extras?, scope?) requests renewal and preserves the non-throwing behavior for terminal OIDC failures.
  • renewTokensOrThrowAsync(extras?, scope?) returns tokens or rejects, for callers that need explicit failure handling.
  • Automatic renewal reports failures through events.

Pushed Authorization Requests (PAR)

PAR (RFC 9126) sends authorization parameters to the provider before navigating the browser.

par modeBehavior
'disabled' (default)Uses the normal front-channel request and ignores PAR metadata.
'auto'Uses a discovered or configured pushed_authorization_request_endpoint. Falls back only when no endpoint exists and the server does not require PAR.
'required'Requires a PAR endpoint; fails before navigation if none is available.

If metadata says require_pushed_authorization_requests: true, 'auto' also fails when the endpoint is missing. Set par_request_timeout to adjust the default 10-second timeout.

Once PAR is selected, the client sends authorization parameters (including state, nonce, PKCE, and login extras) as application/x-www-form-urlencoded data. The subsequent browser redirect carries client_id and the returned request_uri. An endpoint failure or invalid response never silently downgrades to the front-channel flow.

Failures use PushedAuthorizationRequestError, with codes from PushedAuthorizationRequestErrorCode: ENDPOINT_UNAVAILABLE, REQUEST_FAILED, or INVALID_RESPONSE. Use isPushedAuthorizationRequestError(error) and inspect code, plus status, oauthError, and oauthErrorDescription when available.

The PAR endpoint must allow CORS from your application and accept public clients without a client secret. If discovery is unavailable, configure pushed_authorization_request_endpoint and, where applicable, require_pushed_authorization_requests in authority_configuration.

DPoP

Demonstrating Proof of Possession (RFC 9449) binds supported tokens to a key and uses signed proofs for requests. It requires support from the authorization server and resource server. It reduces the usefulness of a stolen token without its key; it does not make an application immune to XSS.

For client-side DPoP, set demonstrating_proof_of_possession: true in the OIDC configuration and opt in for protected requests:

const dpopFetch = oidcClient.fetchWithTokens(fetch, true);
const response = await dpopFetch('https://api.example.com/profile', { method: 'GET' });

Similarly, pass true as the second argument to userInfoAsync(false, true) if the user-info endpoint requires DPoP. For worker-side DPoP, configure demonstratingProofOfPossession in the matching trusted-domain entry and review demonstratingProofOfPossessionOnlyWhenDpopHeaderPresent.

The default cryptographic configuration uses ECDSA P-256, SHA-256, and the ES256 JWT algorithm. Override algorithms through demonstrating_proof_of_possession_configuration (client) or demonstratingProofOfPossessionConfiguration (worker). See DemonstratingProofOfPossessionConfiguration.

API

See OidcClient for full TypeScript signatures.

APIUse
OidcClient.getOrCreate(getFetch, location?)(configuration, name?)Create or reuse a named client. Pass () => fetch for the browser fetch implementation; the default name is 'default'.
OidcClient.get(name?)Retrieve an existing client, or null if it has not been initialized.
OidcClient.getOrThrow(name?)Retrieve an existing client, or throw on missing initialization.
tryKeepExistingSessionAsync()Restore an existing session; returns whether it was kept.
loginAsync(callbackPath?, extras?, isSilentSignin?, scope?, silentLoginOnly?)Start authentication. callbackPath is the application destination after the callback, not the registered redirect_uri.
loginCallbackAsync()Complete interactive authentication, start automatic renewal, and return { callbackPath }.
silentLoginCallbackAsync()Complete the iframe silent-login callback.
logoutAsync(callbackPathOrUrl?, extras?)Clear the session, revoke configured tokens, and use the provider's logout endpoint when available.
clearSessionAsync()Clear the local session without provider logout or token revocation.
isLoggingOutIndicates logout is in progress; avoid starting a competing login flow.
renewTokensAsync(extras?, scope?)Request token renewal.
renewTokensOrThrowAsync(extras?, scope?)Request renewal with a rejected promise on failure.
getValidTokenAsync(waitMs?, numberWait?)Wait for usable tokens; also supports on-demand renewal.
fetchWithTokens(fetch, demonstratingProofOfPossession?)Wrap fetch for protected API requests.
userInfoAsync<T>(noCache?, demonstratingProofOfPossession?)Load user information, optionally bypassing the cache.
userInfo<T>()Read cached user information.
tokens, configurationInspect current session data and effective configuration. Tokens can contain worker placeholders; do not display or log them.
subscribeEvents(handler)Subscribe to (eventName, data) and receive a subscription ID.
removeEventSubscription(id)Remove an event subscription.
OidcClient.eventNamesDiscover supported event names.
publishEvent(name, data)Publish an event to the client's subscribers.
generateDemonstrationOfProofOfPossessionAsync(accessToken, url, method, extras?)Low-level proof generation; prefer the fetch wrapper for normal requests.
signalServiceWorker(message, options?)Send a typed worker-protocol message.

You can inject a custom location adapter as the second argument to getOrCreate, using the exported ILOidcLocation interface and OidcLocation implementation.

Errors and events

Known authentication, callback, renewal, and network failures use OidcError. Inspect stable code and phase fields rather than parsing messages. Other fields include retryable, optional status, oauthError, oauthErrorDescription, and cause.

import { OidcError, OidcErrorCode } from '@axa-fr/oidc-client';

try {
  await oidcClient.renewTokensOrThrowAsync();
} catch (error) {
  if (error instanceof OidcError && error.code === OidcErrorCode.LOGIN_REQUIRED) {
    await oidcClient.loginAsync('/');
  } else {
    throw error;
  }
}

retryable means a retry without user interaction may succeed, not that one is always performed automatically. Network failures, HTTP 408/429/5xx responses, and DPoP nonce challenges can be retryable; LOGIN_REQUIRED needs an interactive login.

Non-throwing flows also expose errors through events. Refresh event payloads include an error field; user-info, logout, and protected API requests have dedicated error events. Log only selected diagnostic fields, not complete event payloads:

const subscriptionId = oidcClient.subscribeEvents((name, data) => {
  const error = data instanceof OidcError ? data : data?.error;
  if (error instanceof OidcError) {
    console.warn(name, error.code, error.phase, error.status);
  }
});

// When the subscriber is no longer needed:
oidcClient.removeEventSubscription(subscriptionId);

Token synchronization events

Use OidcClient.eventNames with subscribeEvents to observe synchronization, including checks that reuse valid tokens without a refresh request. Automatic renewal checks can emit these events approximately once per second; browser scheduling and the time spent synchronizing can delay them.

EventPayloadMeaning
syncTokensAsync_begin{}Synchronization is starting.
syncTokensAsync_end{ status }Synchronization resolved; inspect status to determine the outcome.
syncTokensAsync_error{ message, error, exception }Synchronization threw. message is 'sync tokens error'; exception is the original thrown value, and error is that same value only when it is an OidcError, otherwise undefined.

One synchronization run emits a begin event followed by an end or error event. Internal refresh retries do not emit additional lifecycle pairs. When a cross-tab lock is needed but unavailable, syncTokensAsync_lock_not_available is emitted before synchronization begins and can repeat while waiting for the lock. The lifecycle events are emitted both with and without a lock.

An end event reports resolution, including the following outcomes:

statusMeaning
'LOGGED_IN'Tokens are valid, were refreshed, or were synchronized from another tab.
'LOGGED'Silent login supplied tokens.
'SESSION_LOST'Synchronization could not retain the session. Existing refreshTokensAsync_error events carry the renewal error.
'LOGGED_OUT'Synchronization detected logout or missing stored tokens.
'GIVE_UP'Renewal was deferred, for example while offline in a hidden document or when waiting for an API request to trigger renewal. This does not establish token validity.
nullThere were no current tokens to synchronize.

Resolved failures such as 'SESSION_LOST' emit an end event rather than a synchronization error event. After an error event, the original synchronization exception is rethrown; no end event follows it.

These events cover synchronization itself. Initial worker setup and lock acquisition happen before the begin event; subsequent storage updates, session destruction, and timer scheduling happen after the end event. Failures in those steps are outside this event boundary, so an end event does not guarantee that the renewal call has finished successfully.

Keep subscribers lightweight and do not throw: subscriber exceptions propagate and can interrupt renewal. Log only selected diagnostic fields. The error payload contains the original exception, whose properties or cause may include sensitive data; do not log complete payloads, exceptions, or tokens.

import { isOidcError, OidcClient } from '@axa-fr/oidc-client';

// Use an already initialized client.
const oidcClient = OidcClient.getOrThrow();
const subscriptionId = oidcClient.subscribeEvents((name, data) => {
  if (name === OidcClient.eventNames.syncTokensAsync_end) {
    console.debug(name, data.status);
  } else if (name === OidcClient.eventNames.syncTokensAsync_error) {
    if (isOidcError(data.error)) {
      console.warn(name, data.error.code, data.error.phase, data.error.status);
    } else {
      console.warn(name);
    }
  }
});

// When the subscriber is no longer needed:
oidcClient.removeEventSubscription(subscriptionId);

React applications can observe the same payloads with the provider's onEvent(configurationName, eventName, data) callback. See React error handling for its session-loss response.

Missing or mismatched login state

OidcStateError extends OidcError and identifies callback-state failures:

  • OidcStateErrorCode.STATE_MISSING: stored authorization state is missing.
  • OidcStateErrorCode.STATE_MISMATCH: returned state does not match stored state.
  • OidcStateErrorCode.NONCE_MISSING: stored nonce is missing.

Use isOidcStateError(error) and inspect error.code, especially for errors passed through silent-login handling. Storage clearing, eviction, and competing login attempts can cause these failures. Do not bypass state or nonce checks; offer a fresh login. During silent renewal, a missing nonce is reported through the session-lost flow.

Named configurations and routing

Use distinct names for separate providers or sessions with different scopes:

const paymentsClient = OidcClient.getOrCreate(() => fetch)(paymentsConfiguration, 'payments');
const existingClient = OidcClient.get('payments');

Each name reuses its initialized configuration. Give configurations distinct callback routes and matching keys in OidcTrustedDomains.js.

The library retains hash-route callback matching for legacy integrations. However, OAuth redirect URIs must not contain a fragment: use path-based callback URLs, as in the quick start, for new deployments. Existing hash-callback setups depend on provider-specific behavior and need matching application routing. Interactive and silent callback URLs must be different.

Service-worker protocol

The worker's versioned postMessage protocol is documented in PROTOCOL.md. Constants and helpers such as PROTOCOL_VERSION, ServiceWorkerMessageType, TOKEN_PLACEHOLDERS, and buildSecuredTokenPlaceholder are exported from @axa-fr/oidc-client and @axa-fr/oidc-client-service-worker/protocol.

import { OidcClient, ServiceWorkerMessageType } from '@axa-fr/oidc-client';

const client = OidcClient.getOrThrow();
const result = await client.signalServiceWorker<{ state: string }>({
  type: ServiceWorkerMessageType.GET_STATE,
  configurationName: 'default',
  data: null,
});

This requires an active worker. See the protocol reference for payloads, timeouts, and compatibility guarantees; avoid logging protocol responses containing session state.

Examples and further reading

The demos belong to the pnpm monorepo. Follow their READMEs rather than installing each workspace independently.

For source development, use the root contributor commands and the committed lockfile. Keep this package's Vitest and coverage versions aligned.

版本与发布记录

@axa-fr/oidc-client 共发布 248 个版本,首次发布于 2023/07/28;过去 12 个月发布了 31 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
7.29.8latest2026/10/1523 天前
7.29.72026/10/1325 天前
7.29.62026/09/201 个月前
7.29.52026/09/191 个月前
7.29.42026/09/191 个月前
7.29.32026/09/131 个月前

关于 @axa-fr/oidc-client 的常见问题

@axa-fr/oidc-client 是什么?

OpenID Connect & OAuth authentication using native javascript only, compatible with angular, react, vue, svelte, next, etc.

如何安装 @axa-fr/oidc-client?

在项目目录运行 npm install @axa-fr/oidc-client,或使用 pnpm add / yarn add / bun add。

@axa-fr/oidc-client 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@axa-fr/oidc-client 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@axa-fr/oidc-client 可以商用吗?

@axa-fr/oidc-client 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@axa-fr/oidc-client 的下载量有多少?

根据 npm 官方下载统计,@axa-fr/oidc-client 在 2026/10/08 – 2026/11/06 这 30 天内被下载 180,034 次,日均约 6,001 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@axa-fr/oidc-client 还在维护吗?

最新版本 7.29.8 发布于 2026/10/15(23 天前);至今共发布 248 个版本,其中过去 12 个月发布了 31 个;首次发布于 2023/07/28。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。