openid-connect
Authenticates users against an OpenID Connect provider.
openid-connectPriority 2599What it does
openid-connect protects the route it is added to with an OpenID Connect provider, Keycloak or Entra ID for example, in one of two modes. With Bearer only off, a browser without a session is sent to the provider to sign in. It comes back to the Redirect URI with a code the gateway exchanges for tokens, and keeps a session cookie from then on. With Bearer only on, every request has to carry a bearer token in its Authorization header, which the gateway checks against the provider's published keys or by asking the provider.
A request that passes reaches the backend with the access token in X-Access-Token, the user information in X-Userinfo and, after a browser sign-in, the ID token's claims in X-ID-Token. A request that fails is sent to sign in, answered 401, or answered 403 when the token lacks a scope under Required scopes.
When to use it
- People sign in with the company identity provider before they reach an internal web application behind the gateway.
- API clients call a route with the bearer token the provider issued them, and the backend trusts the gateway's check instead of verifying tokens itself.
- The backend needs to know who the user is, and reads it from the headers the gateway adds.
Not for tokens your own consumers hold, signed without an identity provider — see jwt-auth (Authenticates consumers with a JSON Web Token they present), fine-grained permissions decided by Keycloak's authorization services — see authz-keycloak (Authorizes requests with Keycloak's authorization services) or a SAML identity provider — see saml-auth (Authenticates users with a SAML identity provider).
Guides
Pages that walk through a job with openid-connect, step by step.
- Sign people in with your identity providerSend browsers to Keycloak or another OpenID Connect provider before they reach an internal web application.
Set it up
Send users to the identity provider to sign in and keep their session in a cookie
The callback path has to be one this route serves, as /orders/callback is under a route for /orders/*.
Under Basics set Discovery to https://idp.example.com/.well-known/openid-configuration, set Client ID to orders-api, set Client secret to <secret>, turn off Bearer only, set Redirect URI to https://api.example.com/orders/callback, set Scope to openid profile email and fill Session as in the JSON below.
{
"discovery": "https://idp.example.com/.well-known/openid-configuration",
"client_id": "orders-api",
"client_secret": "<secret>",
"bearer_only": false,
"redirect_uri": "https://api.example.com/orders/callback",
"scope": "openid profile email",
"session": {
"secret": "<secret>",
"cookie_name": "gateway_session",
"cookie_http_only": true,
"cookie_secure": true
}
}
What you will see
- A browser without a session is answered
302to the provider's sign-in page, asking for thescopeyou wrote and naming theredirect_urithe provider sends it back to. - The answer sets the
gateway_sessioncookie, markedSecureandHttpOnly, which carries the sign-in state until the provider sends the browser back to the callback. - Once the person has signed in at the provider, the browser comes back to the callback and the gateway sends it on to the address it first asked for. The request then goes on to your backend with the access token in
X-Access-Token, the ID token's claims inX-ID-Tokenand the user information inX-Userinfo. - Decoded from Base64,
X-UserinfoandX-ID-Tokenare JSON objects of the provider's claims about the person, such aspreferred_username;X-ID-Tokenalso names the provider iniss.
More scenarios
Accept only bearer tokens and check their signature against the provider's keys
Under Basics set Discovery to https://idp.example.com/.well-known/openid-configuration, set Client ID to orders-api and turn on Bearer only. Under Behavior turn on Use JWKS and fill Required scopes as in the JSON below.
{
"discovery": "https://idp.example.com/.well-known/openid-configuration",
"client_id": "orders-api",
"bearer_only": true,
"use_jwks": true,
"required_scopes": [
"orders:read"
]
}
What you will see
- A request without a bearer token is answered
401with aWWW-Authenticate: Bearerchallenge naming the realm, and so is one whose token the provider did not sign, with anerror_descriptionthat says why. - A token the provider signed that carries every required scope passes: the backend receives it in
X-Access-Token, the user information inX-Userinfo, and theAuthorizationheader as the client sent it. - A signed token that lacks a required scope is answered
403with a JSONerrornaming the scope asnot present.
Sign users out at a path this route serves and send them back to the site
Like the callback, the logout path has to be one this route serves: /orders/logout under a route for /orders/*, where the default /logout is not.
Under Basics set Discovery to https://idp.example.com/.well-known/openid-configuration, set Client ID to gateway, set Client secret to <secret>, turn off Bearer only, set Redirect URI to https://gateway.example.com/orders/callback and fill Session as in the JSON below. Under Behavior set Logout path to /orders/logout and set Post logout redirect URI to https://gateway.example.com/.
{
"discovery": "https://idp.example.com/.well-known/openid-configuration",
"client_id": "gateway",
"client_secret": "<secret>",
"bearer_only": false,
"redirect_uri": "https://gateway.example.com/orders/callback",
"logout_path": "/orders/logout",
"post_logout_redirect_uri": "https://gateway.example.com/",
"session": {
"secret": "<secret>"
}
}
What you will see
- A request to the Logout path is answered
302to the provider's sign-out page, withpost_logout_redirect_uritelling the provider where to send the browser afterwards.
Things to know
- Redirect URI — Its path has to be one this route serves: the provider sends the browser there with the sign-in code, and only this route finishes the sign-in. The same address is registered at the provider.
- The people it signs in are not consumers: no consumer or credential is created for them, the backend receives no
X-Consumer-Username, and a plugin that lets only named consumers through answers401because it finds none. The backend tells people apart byX-Userinfo; Authentication explains consumers. - Set raw ID token header — Off by default, so the ID token as the provider signed it does not reach the backend:
X-ID-Tokencarries only its claims, as Base64 JSON. On, the signed token is added asX-Raw-ID-Token. - Logout path — Compared with the whole request path, so it has to be one this route serves as well: the default
/logoutis not under/orders/*. With Bearer only off, a request to it is sent to the provider’s sign-out page. - Bearer only — On, the
Authorizationheader reaches the backend as the client sent it, besideX-Access-Token; nothing here strips it. - Client ID — With Bearer only on, a token the provider signed passes whichever of its clients it was issued to: Client ID does not narrow the tokens, Required scopes does.
- Authorization params — Each value is a string or a number; any other shape is refused when you save.
- Private key — Turning DPoP on asks for this key and the public JWK together. A key left here while DPoP is off is kept as it is.
- Public JWK — Paste the public half of the key pair only. A JWK that still carries one of its private members (d, p, q, dp, dq, qi, oth, k) is refused.
- On a consumer or a consumer group it runs only after the consumer has been identified by a credential; it does not identify the consumer itself.
Settings
The fields as the console shows them, section by section. A field under “When … is …” is there only while that value is set.
Basics
discoverystringRequiredhttps://idp.example.com/.well-known/openid-configurationclient_idstringRequiredclient_secretstringRequired unless Bearer only is true and Public key is set, or unless Bearer only is true and Use JWKS is true, or unless Bearer only is true and Introspection endpoint auth method is private_key_jwt, or unless Bearer only is false and Token endpoint auth method is private_key_jwt, or unless Bearer only is false and Use PKCE is trueStored on the engine and shown only to roles that manage this object.
bearer_onlybooleandefault falseWhen Bearer only is false
redirect_uristringThe address the identity provider sends the browser back to; its path has to be one this route serves, such as /orders/callback under a route for /orders/*. It is registered at the provider. Left empty, the gateway derives one from each request's own path, so the provider would need to accept a different address for every path.
https://gateway.example.com/orders/callbackscopestringdefault openidsessionobjectRequiredsession.secretstringRequiredThe key that encrypts and signs the session cookie. Use the same value on every node. A different value per node ends a session when a request lands on another node. Stored on the engine and shown only to roles that manage this object.
session.absolute_timeoutintegersession.cookie_domainstringThe domain the session cookie is sent to. Empty keeps it to the host that set it.
session.cookie_http_onlybooleanKeeps the session cookie out of reach of scripts in the page.
session.cookie_namestringsession.cookie_pathstringsession.cookie_same_sitestringWhen the browser sends the session cookie on requests that come from other sites.
session.cookie_securebooleanSends the session cookie over HTTPS only.
session.idling_timeoutintegersession.rolling_timeoutintegersession.storagestringdefault cookieWhen Storage is redis
session.redisobjectRequiredsession.redis.connect_timeoutintegerdefault 1000session.redis.databaseintegerdefault 0The Redis database number the sessions are stored in.
session.redis.hoststringdefault 127.0.0.1session.redis.keepalive_timeoutintegerdefault 10000session.redis.passwordstringStored on the engine and shown only to roles that manage this object.
session.redis.portintegerdefault 6379session.redis.prefixstringdefault sessionsText put in front of every session key in Redis.
session.redis.read_timeoutintegerdefault 1000session.redis.send_timeoutintegerdefault 1000session.redis.server_namestringThe name sent to Redis when the encrypted connection is opened, for a server that answers to several names.
session.redis.sslbooleandefault falseEncrypts the connection to Redis.
session.redis.ssl_verifybooleandefault trueChecks the Redis certificate when the connection is encrypted.
session.redis.usernamestringBehavior
unauth_actionstringdefault authWhat an unauthenticated request gets: auth sends the browser to the identity provider, deny answers 401, pass lets the request through. Read only for the browser sign-in: with Bearer only on, a request without a token is answered 401 whatever this says.
use_pkcebooleandefault falseAdds a proof key to the authorization flow, as public clients require.
logout_pathstringdefault /logoutpost_logout_redirect_uristringWhere the browser is sent after the logout path is called.
use_jwksbooleandefault falseVerifies bearer tokens with the provider's published keys instead of asking the provider about each one. Not read when a public key is set.
introspection_endpointstringrequired_scopesarray of stringsScopes the access token must carry, such as payments. A token missing any of them is refused with 403.
claim_validatorobjectclaim_validator.audienceobjectChecks the audience claim of the token.
claim_validator.audience.claimstringdefault audThe claim that carries the audience.
claim_validator.audience.match_with_client_idbooleandefault falseRequires the audience to be, or to include, this plugin's client ID.
claim_validator.audience.requiredbooleandefault falseRefuses a token without an audience claim.
claim_validator.issuerobjectThe issuers whose tokens are accepted. Empty takes the issuer from the discovery document. Without either, the issuer is not checked.
claim_validator.issuer.valid_issuersarray of stringsset_userinfo_headerbooleandefault trueAdds the user info to the request as the X-Userinfo header for the upstream.
set_access_token_headerbooleandefault trueAdds the access token to the request as a header for the upstream.
access_token_in_authorization_headerbooleandefault falseSends the access token in the Authorization header instead of X-Access-Token.
set_id_token_headerbooleandefault trueAdds the ID token to the request as the X-ID-Token header for the upstream.
Advanced
use_noncebooleandefault falseAdds a one-time value to the authorization request and checks it in the token.
authorization_paramsobjectExtra parameters added to the authorization request. An object whose keys are any name, each with a string or number as its value.
{"prompt":"login","ui_locales":"en"}renew_access_token_on_expirybooleandefault trueRenews an expired access token in the background instead of sending the user to sign in again.
access_token_expires_inintegerLifetime given to an access token whose provider sends no expires_in.
access_token_expires_leewayintegerdefault 0How long before its expiry an access token is renewed.
refresh_session_intervalintegerHow often the ID token is refreshed without a new sign-in.
force_reauthorizebooleandefault falseRuns the authorization flow again even when a token is already cached.
revoke_tokens_on_logoutbooleandefault falseTells the identity provider that the tokens are no longer needed when the user logs out.
accept_unsupported_algbooleandefault trueAccepts an ID token signed with an algorithm the plugin cannot verify, skipping the signature check.
jwt_verification_cache_ignorebooleandefault falseVerifies every token again instead of trusting a cached verification.
public_keystringintrospection_intervalintegerdefault 0The longest an introspected token is cached. Zero caches it until the expiry the introspection answer gives.
introspection_expiry_claimstringThe claim in the introspection answer that says how long the result may be cached.
introspection_addon_headersarray of stringsRequest headers copied into the introspection call, one per entry.
token_signing_alg_values_expectedstringjwk_expires_inintegerdefault 86400How long the JWK set is cached.
iat_slackintegerdefault 120Clock skew tolerated on the iat claim of an ID token.
claim_schemaobjectA JSON schema the claims of the token have to satisfy.
{"type":"object","required":["sub"],"properties":{"sub":{"type":"string"}}}accept_none_algbooleandefault falseAccepts an unsigned ID token, for a provider that does not sign its tokens.
cache_segmentstringA name that keeps this plugin's cache apart from the caches of other routes.
realmstringdefault apisixtoken_endpoint_auth_methodstringdefault client_secret_basicintrospection_endpoint_auth_methodstringdefault client_secret_basicclient_rsa_private_keystringThe private key the client assertion is signed with, for a provider that authenticates the client with a signed token. Sent to the engine and never shown again.
client_rsa_private_key_idstringThe key ID sent with the client assertion so the provider can pick the matching public key.
client_jwt_assertion_algstringThe algorithm the client assertion is signed with.
client_jwt_assertion_audiencestringThe audience written into the client assertion, usually the provider's token endpoint.
client_jwt_assertion_expires_inintegerdefault 60Life of the signed JWT the client presents.
set_raw_id_token_headerbooleandefault falseAdds the ID token as it was signed to the request as the X-Raw-ID-Token header.
set_refresh_token_headerbooleandefault falseAdds the refresh token to the request as the X-Refresh-Token header for the upstream.
parobjectSends the authorization request to the provider directly and hands the browser only a reference to it.
par.enabledbooleandefault falsepar.endpointstringThe provider's endpoint the authorization request is pushed to.
par.endpoint_auth_methodstringHow the plugin authenticates itself at the pushed authorization endpoint.
dpopobjectBinds the tokens to a key this plugin holds, so a stolen token cannot be used elsewhere.
dpop.enabledbooleandefault falsedpop.private_keystringRequired when DPoP proof JWTs is trueThe key the proof tokens are signed with. Sent to the engine and never shown again.
dpop.public_jwkobjectRequired when DPoP proof JWTs is trueThe public half of the proof key, as a JSON Web Key.
{"kty":"EC","crv":"P-256","x":"f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU","y":"x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0"}dpop.signing_algstringdefault ES256The algorithm the proof tokens are signed with.
ssl_verifybooleandefault truetimeoutintegerdefault 3How long a call to the identity provider may take. A value of 1000 or more that divides by 1000 is read in the older, thousand-times-finer form: 2000 means 2. A provider that does not answer holds each request for twice this value.
proxy_optsobjectA proxy the identity provider is reached through.
proxy_opts.http_proxystringThe proxy for plain HTTP calls to the provider, as an address with a port.
proxy_opts.http_proxy_authorizationstringThe Proxy-Authorization value for the HTTP proxy, such as a Basic credential.
proxy_opts.https_proxystringThe proxy for HTTPS calls to the provider, as an address with a port.
proxy_opts.https_proxy_authorizationstringThe Proxy-Authorization value for the HTTPS proxy, such as a Basic credential.
proxy_opts.no_proxystringHosts reached directly, not through the proxy, separated by commas.