Skip to content

Security Profiles and Token Binding

Hans Zandbelt edited this page Aug 3, 2026 · 2 revisions

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.

Security Profiles

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 aud values
  • token endpoint JWT authentication aud values
  • the iss parameter requirement in authentication responses
  • whether the scope parameter is sent alongside a request_uri in 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 ]

Individual directives still override what the profile sets, so a profile is a starting point, not a lock.

Pushed Authorization Requests (RFC 9126)

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.

Sender-Constrained Tokens

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 (RFC 9449)

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.

DPoP proof API

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.

Mutual TLS (RFC 8705)

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.

Client authentication

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.

Certificate-bound access tokens

# 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 with OIDCProviderTokenEndpoint, OIDCProviderUserInfoEndpoint, OIDCProviderRevocationEndpoint or OIDCProviderPushedAuthorizationRequestEndpoint, 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.

See also

Clone this wiki locally