[]previous and next page

Providers

A preset is a small typed config helper with no network access.

For agentsUpdated

A preset is a small typed config helper with no network access. Spread it first and add your registration after it, so your values win.

Helper Kind What it sets
google() OpenID Connect, discovery issuer https://accounts.google.com, scopes openid email profile
microsoft({ tenant }) OpenID Connect a tenant GUID: discovery, issuer compared exactly. common, organizations, consumers or a domain: fixed endpoints, and the issuer of the ID token must be https://login.microsoftonline.com/<GUID>/v2.0 (check the tid claim yourself)
auth0({ domain, audience? }) OpenID Connect, discovery issuer https://<domain>/ (with the trailing slash Auth0 uses), the audience parameter for an API
okta({ domain, authorizationServer? }) OpenID Connect, discovery the default authorization server unless told otherwise; null selects the org server
keycloak({ baseUrl, realm }) OpenID Connect, discovery issuer <baseUrl>/realms/<realm>
github() OAuth 2.0, no ID token endpoints fixed; the profile is read from https://api.github.com/user
discord() OAuth 2.0, no ID token endpoints fixed; the profile is read from https://discord.com/api/users/@me
src/providers.tsts
import {
  auth0,
  createOidcClient,
  discord,
  github,
  keycloak,
  microsoft,
  okta,
} from "@shadow-js/dusk/auth";
import { sessions } from "./auth";

const registration = (name: string) => ({
  clientId: `${name}-client-id`,
  clientSecret: `${name}-client-secret`,
  redirectUri: `https://app.example/auth/${name}/callback`,
  sessions,
});

export const clients = {
  auth0: createOidcClient({
    ...auth0({ domain: "example.eu.auth0.com", audience: "https://api.example" }),
    ...registration("auth0"),
  }),
  okta: createOidcClient({ ...okta({ domain: "example.okta.com" }), ...registration("okta") }),
  keycloak: createOidcClient({
    ...keycloak({ baseUrl: "https://id.example.com", realm: "main" }),
    ...registration("keycloak"),
  }),
  microsoft: createOidcClient({
    ...microsoft({ tenant: "11111111-2222-3333-4444-555555555555" }),
    ...registration("microsoft"),
  }),
  github: createOidcClient({ ...github(), ...registration("github") }),
  discord: createOidcClient({ ...discord(), ...registration("discord") }),
};

Every preset has a name that keys what the client keeps in the session, so several providers can live in one app (one route pair per provider, as above). If they all sign the same person in, give them different sessionKey options only if you want them to hold different users at once; by default the last sign-in wins.

Any other OpenID Connect issuer needs only its address (the client reads /.well-known/openid-configuration and checks that the document names the issuer you asked for):

src/other-provider.tsts
import { createOidcClient } from "@shadow-js/dusk/auth";
import { sessions } from "./auth";

export const generic = createOidcClient({
  issuer: "https://id.example.com",
  clientId: "my-client",
  clientSecret: "my-secret",
  redirectUri: "https://app.example/auth/callback",
  scopes: ["openid", "profile", "email", "offline_access"],
  sessions,
});

/** No discovery: the endpoints by hand. `issuer` is still the only `iss` an ID token may have. */
export const manual = createOidcClient({
  issuer: "https://id.example.com",
  endpoints: {
    authorization: "https://id.example.com/oauth/authorize",
    token: "https://id.example.com/oauth/token",
    jwks: "https://id.example.com/oauth/keys",
    userinfo: "https://id.example.com/oauth/userinfo",
    endSession: "https://id.example.com/oauth/logout",
  },
  clientId: "my-client",
  redirectUri: "https://app.example/auth/callback",
  sessions,
});

A client without clientSecret is a public client: PKCE alone protects the code, and no shared-secret algorithm (HS256) is ever accepted for its ID tokens. tokenEndpointAuthMethod chooses how a confidential client proves itself: client_secret_basic (the default), client_secret_post or none.