Configuration / Integration Settings
OIDC Login
A login is completed by the Worker. The callback is an ordinary request, so the token exchange and
the id_token signature check happen in JavaScript before PHP is entered, and PHP is handed a
decided result. src/ops/oidc.ts is the implementation.
This route needs no module. drupal/openid_connect also works, through its own client: the
openssl_* functions are bridged to the host's crypto and the token POST is parked. An unverified
id_token is an unauthenticated login either way, so both paths verify the signature.
| setting | where | what it does |
|---|---|---|
oidc_issuer |
sealed | the provider's issuer URL; https, or a loopback host |
oidc_client_id |
sealed | the client registered with that provider |
OIDC_CLIENT_SECRET |
binding | for a provider that issued one; a secret, not a var |
The issuer and the client id sit in the object's own storage, and the secret is a binding. None of the three can be set from KV: a writer who could change the issuer would point the consent screen at a provider they control, and every login on the site would authenticate against it.
Plain http is refused except on a loopback host, which is what lets a provider run on the same machine during development. A deployed Worker has no loopback to reach, so the exemption grants nothing there.
Register https://<your-site>/oidc?action=callback as the redirect URI. /oidc starts a login and
accepts an optional return path, which must be site-relative. It is a public route because a
provider's redirect carries no header this Worker controls; state is what authenticates it.
Completing a login also needs drupal/externalauth installed on the site, which is what maps the
verified identity onto an account. No contrib module ships in the packed tree, so install it from the
Extend page at /_cfw/extend before the first login.
/setup/oidc reads and writes the two cfw_meta values, and /_cfw/access is the page over it.
| action | method | what it does |
|---|---|---|
?action=status |
GET | the issuer, the client id, whether the secret binding is set, the redirect URI |
?action=save |
POST | validates and stores the pair, then fetches the discovery document |
?action=clear |
GET | forgets both, which turns login off |
It is an owner route, so it takes the token minted at first run and refuses the admin page's weaker gate. Reading which provider is configured is harmless; choosing it is not.
The issuer must be https and must carry no query or fragment: the discovery URL is built by
appending a fixed path, so an issuer carrying either produces a URL nobody intended. A trailing slash
is normalised away so that discovery and the later iss comparison agree on one spelling. Saving
fetches the discovery document immediately and reports what it advertises, so a wrong issuer surfaces
at configuration time, well before someone's first login. The secret is never read back.
The claims never travel in a URL. The browser carries a single-use ticket; the claims stay in the object's storage and the row is deleted before they are returned, so a second presentation of the same ticket finds nothing. A redirect lands in browser history, in a referrer and in any proxy log on the path.
Five conditions refuse a login, each tested against real generated keys: a signature from a key
outside the provider's JWKS, an issuer that is not the configured one, an audience belonging to
another client of the same provider, an expired token, and a nonce from a different login. none
and the HMAC algorithms are refused by omission. The signing algorithm is taken from the key and not
from the token header, and an account is keyed by issuer and subject together.