Docs
  • 0.1.0latest

Clients and their keys

Public and confidential clients, private_key_jwt assertions accepted once, DPoP proofs, exact redirect URIs and the discovery document.

Every company's issuer is <PUBLIC_URL>/<company>. Its metadata answers at <issuer>/.well-known/oauth-authorization-server and <issuer>/.well-known/openid-configuration, and its key set at the jwks_uri they name. The metadata is generated from what the server does: it lists the scopes and claims it releases and advertises nothing it would refuse.

Two kinds of client

  • A public client (none) holds no key: a browser or native application. It always uses PKCE.
  • A confidential client (private_key_jwt) holds a private key and registers the public half, EdDSA (Ed25519) or ES256. There are no shared client secrets.

Clients are registered in the admin console or through its API (see Clients and signing keys), with their redirect URIs, a scope ceiling, the resources their tokens may be addressed to, the grant types they may redeem, and optionally dpop_bound_access_tokens, post_logout_redirect_uris and a backchannel_logout_uri. A client redeems only the grant types it registered.

Redirect URIs

A redirect URI matches a registered one exactly, as a string. The one exception is a native application's loopback address, where only the port may differ (RFC 8252 §7.3).

Client assertions

A confidential client authenticates at the token, PAR and revocation endpoints with a private_key_jwt assertion (RFC 7523):

  • signed by a key the client registered, under EdDSA or ES256;
  • addressed to the company's issuer alone;
  • valid for at most five minutes, and at most ten seconds ahead of the server's clock;
  • its subject names the client, so client_id may be left out beside it (RFC 7521 §4.2); when it is sent, it must be that subject.

Each assertion is accepted once: its jti is kept in a TessariDB space until it could no longer be accepted, and a second presentation is refused as a replay.

DPoP proofs

A client may bind its tokens to a key of its own with DPoP (RFC 9449). A proof is accepted when it is typed dpop+jwt, signed by the public key it carries under the server's algorithms, bound to this method and URI, at most a minute old and at most ten seconds ahead, and seen once per key. The token it accompanies is bound to that key's RFC 7638 thumbprint.

An authorization request may name the key its code is bound to as dpop_jkt; a pushed request sent with a proof is bound to the proof's key, which a dpop_jkt beside it must name. Such a code is redeemed only with a proof made by that key (RFC 9449 §10). See Tokens for what a bound token changes at the token endpoint and at UserInfo.

Scopes and claims

The server releases openid, profile and groups. An access token carries authority only (see Tokens); a user's names and groups are answered by UserInfo and appear in no token. sub is pairwise: each client knows a user by an identifier of its own.