Skip to content

Auth0 ​

@priemskiyyy/anchor-auth0 reads the auth0-spa-js client your application created. It answers the ID token in the client's cache as a session of its issuer and subject, mints an access token for each profile's audience and scope through getTokenSilently, and ends the session by clearing the client and handing back Auth0's logout page. auth0-spa-js emits no events, so call anchor.sync() after a login. Logging in, handling the redirect callback and federated logout stay with anchor.native, which is your client.

Install ​

sh
pnpm add @priemskiyyy/anchor @priemskiyyy/anchor-auth0 @auth0/auth0-spa-js

The adapter supports auth0-spa-js 2.

Create an Anchor ​

ts
import { Anchor } from "@priemskiyyy/anchor";
import { accessToken, auth0 } from "@priemskiyyy/anchor-auth0";
import { createAuth0Client } from "@auth0/auth0-spa-js";

const client = await createAuth0Client({
  domain,
  clientId,
  authorizationParams: { redirect_uri: location.origin },
});

export const anchor = new Anchor({
  adapter: auth0({
    sdk: client,
    tokens: {
      api: accessToken({
        audience: "https://api.example.com",
        scope: "read:projects",
      }),
    },
    returnTo: location.origin,
  }),
});

if (location.search.includes("code=")) {
  await anchor.native.handleRedirectCallback();
}

await anchor.start();

After loginWithPopup() or any other native call that changes the session, call anchor.sync().

Options ​

OptionTypeDefaultWhat it does
sdkAuth0ClientrequiredThe client createAuth0Client() returned.
user(claims: IdToken) => TUsera snapshot of the profile claimsMaps the ID token claims to the plain data a session carries.
tokensRecord<string, Auth0AccessToken>noneThe token profiles, each accessToken({ audience, scope }).
returnTostringAuth0's defaultWhere Auth0's logout page sends the browser back to; one of your Allowed Logout URLs.

Capabilities ​

CapabilityAuth0
ObservationNone: auth0-spa-js emits no events. Call anchor.sync() after a login.
Token profilesaccessToken({ audience, scope }), one per API.
Forced refreshYes: cacheMode: "off".
EndLocal: the client's cache is cleared. Remote: pending until the browser opens the logout page.
Navigation after endYes: result.navigation.url is Auth0's /v2/logout page.
RuntimesBrowsers. react-native-auth0 has its own adapter.

How a session is mapped ​

SessionFrom the ID token
principal.authorityiss, your Auth0 domain's issuer
principal.subjectsub, such as auth0|42 or google-oauth2|1234
principal.tenantnull
contextorg_id and sid: another organization or a new login is a new session
user{ sub, name, nickname, picture, email, email_verified }, or your mapper's result
guestfalse
impersonatedtrue when the token carries an act claim

Tokens ​

session.getToken(profile) checks that the cached ID token still belongs to the session, then calls getTokenSilently({ authorizationParams: { audience, scope }, cacheMode, detailedResponse: true }). It checks the cached ID token again once the token arrives, because a token the silent iframe mints belongs to whoever Auth0's session cookie names now: a login in another tab meanwhile rejects with SESSION_CHANGED, never that user's token. The token's kind is access-token and its scheme is the token_type Auth0 answers, Bearer or DPoP. login_required, consent_required, interaction_required, mfa_required and missing_refresh_token reject with INTERACTION_REQUIRED: call loginWithRedirect or loginWithPopup yourself. Any other failure rejects with TOKEN_FAILED and the Auth0 error as its cause.

Ending the session ​

session.end() calls logout({ openUrl }) only while the cache still holds the session's ID token, and answers { local: "completed", remote: "unknown", navigation: { url } }. The Auth0 session survives until the browser opens that page, so open it:

ts
const result = await session.end();

if (result.navigation !== null) {
  location.assign(result.navigation.url);
}

When the cache holds another login, it logs nobody out and answers not-applicable. Federated logout (logoutParams.federated) stays with anchor.native.logout().

Behavior ​

  • The factory is cold: creating the adapter touches nothing on the client.
  • A read answers from getIdTokenClaims(): no cached ID token is signed-out.
  • An ID token without iss or sub is outside the contract; the first state is then unavailable.
  • The adapter installs no listener, so dispose() has nothing to remove.
  • auth0-spa-js keeps tabs apart with its memory cache: the crossTab() hint makes a sign-out in one tab read again in the others.

What Anchor does not normalize ​

Logging in, the redirect callback, popups, organization invitations, MFA challenges, DPoP proofs and nonces, and refresh-token rotation stay with auth0-spa-js. Read them through anchor.native.

The adapter runs against a fake modelled on auth0-spa-js 2.27.0 and the real client in jsdom; see the verification matrix. React Native has its own adapter. The package README is the reference.

Released under the MIT License.