This example treats Authorizer as just another OpenID
Provider: a small Express app built on the certified
openid-client library (v6, pinned)
consumes it exactly the way it would consume Auth0, Okta, or Keycloak. No
Authorizer SDK, no Authorizer-specific endpoints.
The RP relies only on standard behavior, all of which Authorizer implements
(server source: internal/http_handlers/{openid_config,authorize,token,userinfo,jwks,logout}.go):
- Discovery (OIDC Discovery 1.0) —
GET /.well-known/openid-configurationpublishes issuer + all endpoints; the RP hardcodes nothing but the issuer URL. - Authorization Code + PKCE (OIDC Core §3.1, RFC 7636) —
response_type=codewithcode_challenge_method=S256,state, andnonce. - Token endpoint (RFC 6749 §4.1.3) — form-encoded code exchange;
openid-clientthen validates the ID token: RS256 signature againstjwks_uri,iss,aud,exp, and thenonceround-trip (OIDC Core §3.1.3.7).at_hashis present and checked too. - UserInfo (OIDC Core §5.3) — bearer
GET /userinfo; the returnedsubis verified against the ID token'ssub(§5.3.2, a MUST). - RP-initiated logout (OIDC RP-Initiated Logout 1.0) — discovery advertises
end_session_endpoint; the RP redirects there withid_token_hint+post_logout_redirect_uri. With a validid_token_hintAuthorizer logs the session out and 302s straight back — no confirmation page.
From a stock instance (make dev, http://localhost:8080):
| Field | Value |
|---|---|
issuer |
the URL the request hit (see pitfall #3) |
authorization_endpoint |
{issuer}/authorize |
token_endpoint |
{issuer}/oauth/token |
userinfo_endpoint |
{issuer}/userinfo |
jwks_uri |
{issuer}/.well-known/jwks.json |
end_session_endpoint |
{issuer}/logout |
code_challenge_methods_supported |
["S256", "plain"] |
id_token_signing_alg_values_supported |
configured --jwt-type, always incl. RS256 |
token_endpoint_auth_methods_supported |
client_secret_basic, client_secret_post, none (public + PKCE), private_key_jwt |
response_modes_supported |
query, fragment, form_post, web_message |
scopes_supported |
openid, email, profile, offline_access |
Prereqs: Node ≥ 20, an Authorizer server from main:
# in the authorizer server repo
make dev # :8080, client-id/secret + allowed-origins preconfigured for :8090Then:
npm install
cp .env.example .env # make-dev defaults already filled in
npm start
# open http://localhost:8090 and click "Log in with Authorizer"You'll land on Authorizer's hosted login page; after signup/login you're
redirected back and the page renders the validated ID-token claims and the
/userinfo response. "Log out (RP-initiated)" ends the session on both sides.
The RP client here is the instance's boot-seeded interactive client
(--client-id/--client-secret). The .env.example values are the public
make dev development defaults — replace them for any real deployment.
-
PKCE downgrades to
plainwhen the method is omitted. Per RFC 7636 §4.2 an authorization request carryingcode_challengebut nocode_challenge_methodmeans theplainmethod, and Authorizer follows the RFC (it exists for clients that can't compute SHA-256 — Auth0 sometimes omits the method, for example). If you hand-roll requests, always sendcode_challenge_method=S256explicitly; this example does (seebuildAuthorizationUrlinserver.js). -
form_postbreaks behind CSP-injecting proxies. Withresponse_mode=form_postAuthorizer serves an auto-submitting HTML form with a purpose-builtContent-Security-Policy(nonce-basedscript-src, relaxedform-action) so the browser may POST to yourredirect_uri. A reverse proxy that injects its own CSP header on top will silently break the form submission. This example uses the defaultqueryresponse mode, which has no such failure mode. -
issuerfollows the request host. Authorizer derivesiss(and all discovery URLs) from the host the request hit. Do discovery through the same URL end users use, or ID-tokenissvalidation will fail — relevant behind proxies and inside docker networks. -
redirect_urimust be an allowed origin. Authorizer validatesredirect_uriagainst--allowed-origins; an unknown origin getsinvalid redirect_uriat/authorize.make devalready allowslocalhost:8090; add your RP's host otherwise. The same list gatespost_logout_redirect_uri— an invalid one is ignored (per the RP-Initiated Logout spec), not an error. -
Plain-HTTP issuers need an explicit opt-in.
openid-clientv6 refuseshttp://by default; the demo passesallowInsecureRequestsfor localhost. Never do that against a production issuer.
server.js the whole RP: discovery, /login, /callback, /logout (~150 lines)
.env.example make-dev defaults (fake/dev credentials only)