-
-
Notifications
You must be signed in to change notification settings - Fork 333
Security Profiles and Token Binding
This page describes the settings that harden the interaction between mod_auth_openidc and the
Provider beyond plain OpenID Connect Core: security profiles (OIDCProfile), Pushed Authorization
Requests, and the two ways of sender-constraining tokens - DPoP and mutual TLS.
OIDCProfile selects a coherent set of defaults rather than requiring each individual setting to be
turned on by hand. It configures:
- the Authentication Request method
- DPoP
- PKCE
- ID token
audvalues - token endpoint JWT authentication
audvalues - the
issparameter requirement in authentication responses - whether the
scopeparameter is sent alongside arequest_uriin the authentication request
# OIDC10: adheres to the core OpenID Connect spec v1.0 (the default)
# FAPI20: the FAPI 2.0 Security Profile, i.e.:
# Auth Request Method: PAR, DPoP: Required, PKCE: S256, aud: client_id,
# token endpoint JWT aud: iss, iss parameter: required,
# scope: omitted when a request_uri is used
#OIDCProfile [ OIDC10 | FAPI20 ]A profile sets defaults that individual directives can override, but not for everything: FAPI20
enforces four settings regardless of what is configured, because the profile does not permit them to
vary.
| setting | under FAPI20
|
|---|---|
| authentication request method | always PAR, whatever OIDCProviderAuthRequestMethod says |
| PKCE | always S256
|
| token endpoint authentication audience | always the provider's issuer |
| revocation endpoint authentication audience | always the provider's issuer |
Everything else remains configurable. In particular the acceptable ID token aud values may be
overridden, and DPoP stands down to the configured mode when access tokens are certificate-bound and
the OP does not advertise DPoP support -- FAPI 2.0 section 5.3.2.1 accepts either mTLS binding or
DPoP, so mandating DPoP would break the mTLS variant of the profile.
With PAR, the authentication request parameters are POSTed to the Provider over a back channel and
the browser is only sent a short request_uri reference, so the parameters cannot be inspected or
tampered with in the front channel.
# Send authentication request parameters to the Pushed Authorization Request endpoint
OIDCProviderAuthRequestMethod PAR
# Only needed when OIDCProviderMetadataURL is not used, or the discovered metadata does
# not advertise "pushed_authorization_request_endpoint"
#OIDCProviderPushedAuthorizationRequestEndpoint <url>OIDCProviderAuthRequestMethod is also settable per Provider through the auth_request_method key
in the Provider's .conf file, see Multiple Providers.
It is implied by OIDCProfile FAPI20.
A plain bearer access token can be replayed by whoever obtains it. Both mechanisms below bind the token to a key that the legitimate client holds, so a stolen token on its own is not enough.
DPoP binds the token to a key held by mod_auth_openidc, proven per request with a signed proof
JWT. It requires a signing key, so OIDCPrivateKeyFiles/OIDCPublicKeyFiles must be configured
with an RSA or EC private signing key.
# off: no DPoP token is requested from the OP
# optional: a DPoP token is requested but we continue even if the returned token is Bearer
# required: a DPoP token is requested and we fail if the returned token type is not DPoP
# When not defined "off" is used.
# The 2nd parameter optionally enables the DPoP proof API described below.
#OIDCDPoPMode [off|optional|required] [on|off]OIDCProfile FAPI20 implies required. The mode can be overridden per Provider with the
dpop_mode key in the Provider's .conf file.
Applications behind the module that need to use the DPoP-bound access token themselves cannot
simply replay it - each call needs a fresh proof signed with the module's key. Enabling the second
parameter of OIDCDPoPMode exposes an endpoint that mints one:
<redirect_uri>?dpop=<access_token>&url=<url>[&method=<method>][&nonce=<nonce>]
method defaults to GET. The response is a JSON object holding the proof to send in the DPoP
header alongside the access token:
{"DPoP":"eyJ0eXAiOiJkcG9wK2p3dCIs..."}Caveat: this endpoint hands out a proof for whatever access token is passed to it, so it would
undo the proof-of-possession property if it were reachable by the party holding the token. The
module therefore rejects any request whose client address is not the server's own local address, and
logs a warning telling you to instead expose it through a separate virtual (sub)host that requires
client certificate authentication and proxies the request. The check can be disabled by setting the
OIDC_DPOP_API_INSECURE environment variable, which - as the name says - you should not do outside
of testing.
RFC 8705 covers two related things that are configured separately: authenticating the client with a TLS certificate, and binding the access token to that certificate.
OIDCProviderTokenEndpointAuth tls_client_auth
# or, for a self-signed client certificate:
#OIDCProviderTokenEndpointAuth self_signed_tls_client_auth
# PEM-formatted client certificate and its private key
OIDCClientTokenEndpointCert <filename>
OIDCClientTokenEndpointKey <filename>
# Optional password for an encrypted private key; supports "exec:" like OIDCClientSecret
#OIDCClientTokenEndpointKeyPassword [ <passphrase> | "exec:/path/to/otherProgram arg1" ]These three settings are also available per Provider as token_endpoint_tls_client_cert,
token_endpoint_tls_client_key and token_endpoint_tls_client_key_pwd.
# off: only when one of the mutual-TLS client authentication methods is used, i.e. never
# inferred from the presence of a client certificate alone
# auto: additionally when a client certificate is configured and the Provider metadata
# advertises "tls_client_certificate_bound_access_tokens"
# on: additionally when a client certificate is configured, regardless of what the
# Provider advertises
# When not defined "auto" is used.
#OIDCCertBoundAccessTokens [off|auto|on]Two things are easy to get wrong here:
- The client must be registered with the OP for certificate-bound access tokens. The Provider advertising support says that the OP can bind, not that this client is set up for it.
- When the Provider publishes
mtls_endpoint_aliases, those aliases are used for the back-channel endpoints - except where an endpoint has been configured explicitly withOIDCProviderTokenEndpoint,OIDCProviderUserInfoEndpoint,OIDCProviderRevocationEndpointorOIDCProviderPushedAuthorizationRequestEndpoint, which always take precedence over the alias.
Per Provider, this is the cert_bound_tokens key.
Under OIDCProfile FAPI20 access tokens are sender-constrained through either mutual TLS or DPoP,
so on is implied and DPoP is not required on top of certificate binding.
-
Multiple Providers - the per-Provider
.confkeys for all of the above - OAuth 2.0 Resource Server - validating tokens on the receiving side
- The authoritative documentation for every directive is auth_openidc.conf