Beta

Create a tenant

A new tenant starts with its own users, OAuth settings, audit history and logs. You are its first Tenant Admin.

Pushed requests and request objects in your tenant

An ordinary authorization request travels in the browser's address bar, where anyone along the way can read or change it. Your tenant offers two ways to protect it.

The same controls apply to BTL's own organization under BTL Admin, OAuth Management.

Pushed authorization requests

A client first posts the whole authorization request to /oauth/par (RFC 9126), authenticating exactly as it would at the token endpoint. A public client sends only client_id. The tenant checks the request at once, with the same rules as the authorization endpoint, and answers HTTP 201 with a reference:

{"request_uri": "urn:ietf:params:oauth:request_uri:<random>", "expires_in": 60}

The browser then opens the authorization endpoint with only client_id and that request_uri. Any other parameter in the address is ignored: the pushed request is the whole request. A reference works once, only for the client that pushed it, and only until it expires. A request_uri that is not one of these references is refused with request_uri_not_supported; the tenant never fetches a request from a URL.

A problem found when the request is pushed is answered as JSON, such as invalid_request for missing PKCE or an unregistered redirect URI, so the client learns of it before sending anyone to sign in. A push larger than 16 KiB gets HTTP 413.

Unregistered redirect URIs. RFC 9126 section 2.4 lets a server accept, in a pushed request, a redirect URI the client never registered, because the client authenticated the push. Your tenant does this only for a client you choose: open it in Clients and, under How authorization requests arrive, tick Accept unregistered redirect URIs in pushed requests. It is off by default and only for confidential clients. Ordinary authorization requests from the client still keep to its registered list. The address must still be a valid redirect URI for the client (https, or http to a loopback address, without a fragment) and written in its normal form, and a FAPI 2.0 client's must use https. Add an Unregistered redirect URI prefix, such as https://printer.example/oauth/, to accept only addresses on that scheme and host whose path starts with the prefix's path; one outside it is refused with redirect_uri_outside_prefix. A leaked client secret could send codes to any address the setting allows, so keep the prefix as narrow as the client needs. Changing either setting ends the client's sign-ins in progress and waiting pushes; tokens it already holds stay.

Request objects

A request object is the authorization request as a JWT that the client signs with one of its own keys (RFC 9101), registered under Client public keys in Access Token Management, inline or from a jwks_uri. Send it in the request parameter of the authorization request, with client_id beside it, or inside a pushed request. Its claims are the request's parameters, plus:

{"iss": "<client ID>", "client_id": "<client ID>", "aud": "<issuer>", "exp": <within the tenant's lifetime>,
 "jti": "<optional, unless the tenant requires it>", "response_type": "code", "redirect_uri": "...", "state": "...", ...}

The header names ES256, PS256, RS256 or EdDSA (Ed25519), and the kid when the client has several keys; typ may be oauth-authz-req+jwt. Unsigned request objects are refused. A signed request object may also be encrypted to one of the tenant's encryption keys, as Signed and encrypted messages explains. Only the request object's parameters are used, whatever else the address holds. A request object with a jti works once.

A request object whose signature or claims cannot be verified stops on an error page instead of returning to the application, because the return address is inside the object and cannot be trusted.

Signed responses

The answer can be protected too. With response_mode=jwt, or query.jwt, fragment.jwt or form_post.jwt (JARM), the code or error, state and any tokens come back as one parameter, response: a JWT this tenant signs with iss (this issuer), aud (the client ID) and an exp five minutes ahead, or as far ahead as Signed authorization response lifetime in Flow Policy says (60 to 600 seconds; a lab tenant may go down to 10). jwt alone means query.jwt for code and fragment.jwt for responses that carry tokens, which never use query.jwt. The application verifies the signature with the tenant's published keys before reading anything. Errors found once the client and return address are known come back signed the same way. Protocol Audit records the response mode each answer to the application used, such as form_post or query.jwt, under Response mode.

Allow the modes in Flow Policy and for each client. A client's Authorization response signing algorithm picks the newest active key of that algorithm. By default the newest active RS256 key signs, as JARM specifies. A FAPI 2.0 client, which cannot use RS256, and a tenant with no active RS256 key use the key of the client's ID token manager instead, which its OpenID Connect library can already verify. Discovery lists the modes and authorization_signing_alg_values_supported. A client can also have these responses encrypted to its own key; see Signed and encrypted messages.

Tenant and client settings

In Flow Policy, Pushed authorization requests are Allowed by default; choose Required from every client to refuse requests sent through the browser, or Off. Required from every client leaves out the built-in Token Decoder, which always sends ordinary browser requests. Choose how long a reference lasts, from 10 to 600 seconds (60 by default). Request objects are Allowed or Off, with the longest lifetime an exp may have, from 60 to 3600 seconds, and whether each must carry a jti.

Each client in Clients can also require pushed requests, require signed request objects, and fix the signing algorithm. Signed request objects can be required per client only, since each client signs with its own keys. A client cannot require a feature Flow Policy turns off, and Flow Policy cannot turn a feature off while a client still requires it. These settings change what future requests must look like, so saving them revokes nothing.

Discovery publishes pushed_authorization_request_endpoint, require_pushed_authorization_requests, request_parameter_supported and request_object_signing_alg_values_supported to match. request_uri_parameter_supported stays false, since only pushed request references are accepted.

Refusals and history

Audit records each push under oauth.par, with request_pushed on success and the algorithm and key used when a request object was pushed. A push to an unregistered redirect URI succeeds as request_pushed_unregistered_redirect, and both that record and the oauth.authorize record that issued the code or token name the address: its scheme, host and path, never its query. A request that started from a pushed request or a request object says so in its request_started record. A request object must not name the client in sub, and a client assertion typed as a request object or DPoP proof is refused, so one kind of signed JWT cannot be used as the other. Each failed request object check has its own reason, so you can tell them apart: request_object_signature_invalid, request_object_unknown_key, request_object_client_mismatch, request_object_audience_invalid, request_object_expired, request_object_claims_invalid, request_object_jti_required, request_object_replayed, request_object_type_invalid, request_object_alg_not_allowed and request_object_encrypted. A client that skips a requirement gets pushed_request_required or request_object_required.

Up to 1,000 pushed requests can wait across the tenant, and up to 100 for any one client, so one client cannot use up the room the others need. Both appear on the tenant's Usage view. A refused push answers HTTP 429 with Retry-After.

We value your privacy

We use cookies and similar technologies to enhance your browsing experience, and analytics to understand our traffic. By clicking "Allow all", you consent to optional analytics. Cookie Policy

Documentation