Skip to content

feat(auth0-server-js): Add MCD support#119

Open
nandan-bhat wants to merge 11 commits intomainfrom
feature/mcd-rwa
Open

feat(auth0-server-js): Add MCD support#119
nandan-bhat wants to merge 11 commits intomainfrom
feature/mcd-rwa

Conversation

@nandan-bhat
Copy link
Contributor

@nandan-bhat nandan-bhat commented Feb 3, 2026

Summary

This PR adds MCD support to @auth0/auth0-server-js, enabling per-request domain resolution while preserving single‑instance SDK usage. It also adds discovery/JWKS caching in @auth0/auth0-auth-js.

Changes

  • domain can now be a resolver function. Resolver mode requires storeOptions per call so the SDK can select the correct domain.
  • Transaction/session domain tracking: origin domain/issuer are persisted and used for token exchange, refresh, and logout; issuer validation prevents cross‑tenant mixups.
  • if authorizationParams.scope is overridden, openid is enforced so iss is present during callback.
  • auth0-auth-js now uses LRU + in‑flight de‑dupe to avoid redundant discovery/JWKS fetches.

Usage example

import { ServerClient } from '@auth0/auth0-server-js';
import type { DomainResolver, DomainResolverContext } from '@auth0/auth0-server-js';

type StoreOptions = { request: { headers: Record<string, string | undefined> } };
const defaultAuth0Domain = 'default-tenant.us.auth0.com';

const domainResolver: DomainResolver<StoreOptions> = async ({ storeOptions }: DomainResolverContext<StoreOptions>) => {
  const host = storeOptions?.request?.headers.host;
  if (!host) return defaultAuth0Domain;
  if (host === 'brand-1.my-app.com') return 'custom-domain-1.auth0.com';
  if (host === 'brand-2.my-app.com') return 'custom-domain-2.auth0.com';
  return defaultAuth0Domain;
};

const auth0 = new ServerClient<StoreOptions>({
  domain: domainResolver,
  clientId: '<client_id>',
  clientSecret: '<client_secret>',
  transactionStore,
  stateStore,
});

MCD behavior notes

  • Static domain usage remains unchanged.
  • In resolver mode, missing storeOptions can cause the resolver to return no domain and fail the request.
  • Sessions are domain‑specific in resolver mode; cross‑domain session reuse is blocked.

Documentation

@abbaspour
Copy link

Does this support support simple domains array list? similar to #120 ?

@nandan-bhat
Copy link
Contributor Author

Does this support support simple domains array list? similar to #120 ?

No. It doesn't support domains array list.

audience?: string;
codeVerifier: string;
originDomain?: string;
originIssuer?: string;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are these called originDomain and originIssuer and in the sessionData domain and issuer ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use different names because they are for different moments. originDomain and originIssuer are saved only for the current login flow, so we can safely handle the callback.

domain and issuer are saved in the user session after login succeeds, and are used later for normal session actions like getting tokens and logout.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in e2e3704

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants