Providers
A preset is a small typed config helper with no network access.
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 |
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):
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.