# Configure OpenID Connect (OIDC) authentication Type of document: How-to guide Product: NGINX Gateway Fabric --- This guide describes how to configure OpenID Connect (OIDC) authentication in NGINX Gateway Fabric using the `AuthenticationFilter` custom resource definition (CRD). ## Overview OIDC authentication lets you delegate user login to a trusted Identity Provider (IdP) such as Keycloak, Okta, or Auth0. Once a user signs in through the IdP, that session is recognized across every route protected by the same IdP. They are not prompted to log in again when moving between applications. NGINX Gateway Fabric redirects unauthenticated users to the IdP, receives an authorization code after login, and exchanges that code for identity tokens on the user's behalf. Your backend services receive only requests that have already passed authentication and never handle credentials directly. When a user requests a protected resource, NGINX Gateway Fabric uses the [Authorization Code Flow](https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth): 1. NGINX redirects the browser to the IdP's authorization endpoint. 1. The user authenticates with the IdP. 1. The IdP redirects the browser back to NGINX with a short-lived authorization code. 1. NGINX exchanges that code for an ID token and access token via a direct, back-channel HTTPS call to the IdP's token endpoint. 1. NGINX validates the ID token, creates a session cookie, and forwards the original request to the backend. 1. Subsequent requests carry the session cookie, so the IdP is not contacted again until the session expires. TLS is required in two directions. For inbound connections, the callback redirect from the IdP back to NGINX must be served over HTTPS. The `AuthenticationFilter` must be attached to an HTTPRoute that uses an HTTPS listener, and the Gateway listener's `tls.certificateRefs` provides the certificate NGINX presents to the browser. This is the same certificate that NGINX would provide to any client. For outbound connections, NGINX connects to the IdP over HTTPS to exchange the authorization code for tokens. By default, NGINX trusts the system CA bundle. To use a custom CA, specify it in `oidc.caCertificateRefs`. Attaching an OIDC `AuthenticationFilter` to a non-HTTPS route will cause the filter to be rejected. OIDC configuration references Kubernetes `Opaque` Secrets for sensitive material. The `clientSecretRef` field expects a Secret with the key `client-secret`. Your IdP requires a client ID and secret to identify and authenticate the application contacting its realm. NGINX presents these credentials when exchanging the authorization code for tokens. The `caCertificateRefs` field expects a Secret with the key `ca.crt`, containing PEM-encoded CA certificates that NGINX uses to verify the IdP's TLS certificate on outbound connections. If omitted, NGINX uses the system CA bundle. The `crlSecretRef` field expects a Secret with the key `ca.crl`, containing a PEM-encoded Certificate Revocation List. NGINX checks the IdP's certificate serial number against this list before every outbound connection. This field can be omitted if CRL checking is not required. You can consolidate multiple keys in a single Secret or use separate Secrets for each. Either approach works as long as each Secret contains the correct key name. **Note:** OIDC authentication requires [NGINX Plus](/ngf/install/nginx-plus.md) and is not supported with open-source NGINX. ## Before you begin To follow this guide, you need the following: - [Install](/ngf/install/) NGINX Gateway Fabric with NGINX Plus. ### Install cert-manager Install cert-manager onto the cluster using Helm with Gateway API features enabled. - Add the Helm repository. ```shell helm repo add jetstack https://charts.jetstack.io helm repo update ``` - Install cert-manager, and enable the GatewayAPI feature gate: ```shell helm install \ cert-manager jetstack/cert-manager \ --namespace cert-manager \ --create-namespace \ --set config.apiVersion="controller.config.cert-manager.io/v1alpha1" \ --set config.kind="ControllerConfiguration" \ --set config.enableGatewayAPI=true \ --set crds.enabled=true ``` ### Generate certificates The following steps use `cert-manager` to issue a local Certificate Authority (CA) and sign certificates for both Keycloak and NGINX. `cert-manager` creates the required Kubernetes Secrets directly so no manual secret creation is needed for TLS. Create a self-signed `ClusterIssuer` to bootstrap the CA, then issue the CA certificate and create a second `ClusterIssuer` backed by it: ```yaml kubectl apply -f - <_` ```yaml kubectl apply -f - < 53/UDP,53/TCP 10d ``` NGINX Gateway Fabric creates an `NginxProxy` resource during installation. Edit it to add the `dnsResolver` field: ```bash kubectl edit nginxproxies.gateway.nginx.org -n nginx-gateway ngf-proxy-config ``` ```yaml spec: dnsResolver: addresses: - type: IPAddress value: 10.96.0.10 ``` ### Create the Keycloak Secret This Secret holds the client secret and the CA certificate NGINX uses to verify Keycloak's TLS certificate on outbound connections. The CA certificate is extracted from the `keycloak-tls-cert` Secret that cert-manager created. ```shell kubectl create secret generic keycloak-secret \ --from-literal=client-secret=$CLIENT_SECRET \ --from-file=ca.crt=<(kubectl get secret keycloak-tls-cert -o jsonpath='{.data.ca\.crt}' | base64 -d) ``` ### Create the AuthenticationFilter The `AuthenticationFilter` defines how NGINX communicates with the IdP. The only required fields are `issuer`, `clientID`, and `clientSecretRef`. Everything else is optional. ```yaml kubectl apply -f - < ``` ### Deploy the HTTPRoutes Create an HTTPRoute with two rules. The `/coffee` rule attaches the `AuthenticationFilter`, whereas `/tea` is publicly accessible with no authentication required. ```yaml kubectl apply -f - < ``` --- ## Verify OIDC authentication For local testing, add the following entry to your `/etc/hosts` file so your browser can resolve `cafe.example.com` to the Gateway's public IP: ```text cafe.example.com ``` The steps below use a browser for OIDC since the flow involves redirects and cookies that curl cannot handle end-to-end. ### Accessing the protected `/coffee` route Open `https://cafe.example.com:$GW_PORT/coffee` in a browser. Because the route has an `AuthenticationFilter`, NGINX will: 1. Detect there is no valid session cookie. 1. Redirect your browser to the IdP's login page. 1. Log in with username `testuser` and password `testpassword`. 1. After you log in, redirect you back to NGINX with an authorization code. 1. Exchange the code for tokens in the background, set a session cookie, and forward you to the `coffee` backend. You will see a response from the `coffee` application only after a successful login. ### Accessing the unprotected `/tea` route Since `/tea` requires no authentication, you can access it directly with curl: ```shell curl -k --resolve cafe.example.com:$GW_PORT:$GW_IP https://cafe.example.com:$GW_PORT/tea ``` ```text Server address: 10.244.0.10:8080 Server name: tea-75bc9f4b6d-ms2n8 Date: 17/Mar/2026:10:01:00 +0000 URI: /tea Request ID: c7eb0509303de1c160cb7e7d2ac1d99f ``` The `tea` backend responds immediately with no authentication challenge because no `AuthenticationFilter` is attached to that rule. ## Optional configuration ### Session management By default, NGINX issues a session cookie named `NGX_OIDC_SESSION` with an 8-hour timeout that resets on each request to a protected resource. Use `session.cookieName` and `session.timeout` to override these values. ```yaml spec: type: OIDC oidc: session: cookieName: my-app-session timeout: 30m ``` ### Logout Use `logout.uri` to set the path a user visits to log out. When a request hits that path, NGINX clears the session and redirects to the IdP's logout endpoint. If `logout.postLogoutURI` is not set, NGINX returns a `200 OK` with the body "You have been logged out.". It can be set to a path to redirect the user there after logout. The path must be matched by an existing HTTPRoute rule. Set it to a full URL to redirect the user to an external page. Use `logout.frontChannelLogoutURI` if your IdP uses front-channel logout, where the IdP sends a logout request to a browser-visible URL to clear the NGINX session. The IdP must send `iss` and `sid` as query parameters. Set `logout.tokenHint` to `true` if your IdP requires the original ID token to be passed in the logout request. ```yaml spec: type: OIDC oidc: logout: uri: /logout postLogoutURI: /after_logout frontChannelLogoutURI: /frontchannel_logout tokenHint: true ``` ### PKCE PKCE (Proof Key for Code Exchange) prevents authorization code interception attacks. NGINX enables it automatically when the IdP requires the `S256` code challenge method. Set `pkce` explicitly if you need to force it on or off. ```yaml spec: type: OIDC oidc: pkce: true ``` ### Extra authentication arguments Use `extraAuthArgs` to append additional query parameters to the authorization request sent to the IdP. For example, `prompt: "login"` forces the IdP to show the login page on every request, and `max_age` sets the maximum time in seconds since the user last authenticated before re-authentication is required. ```yaml spec: type: OIDC oidc: extraAuthArgs: prompt: "login" max_age: "3600" ``` ### Custom redirect URI By default, NGINX Gateway Fabric registers the OIDC callback at `/oidc_callback__`. Use `redirectURI` to set a different path. If you provide a path-only value, NGINX creates a location block to handle the callback. If you provide a full URL, it is treated as an external handler and no location block is created. Register the same value in your IdP as an allowed redirect URI. ```yaml spec: type: OIDC oidc: redirectURI: /my_callback ``` ### Custom IdP metadata URL By default, NGINX fetches IdP metadata from `/.well-known/openid-configuration`. Use `configURL` if your IdP exposes metadata at a different path. ```yaml spec: type: OIDC oidc: configURL: "https://keycloak.example.com/realms/my-realm/.well-known/openid-configuration" ``` ### Certificate Revocation List (CRL) A CRL is a list of certificate serial numbers that a CA has revoked before expiry. When NGINX connects to the IdP over TLS, it checks the IdP's certificate against the CRL and rejects the connection if the certificate has been revoked. You are responsible for keeping the CRL up to date. A stale CRL may not catch recently revoked certificates. The `crlSecretRef` and `caCertificateRefs` fields are separate so you can rotate the CRL independently, though both keys can live in the same Secret. ```yaml spec: type: OIDC oidc: crlSecretRef: name: oidc-crl ``` --- ## Troubleshooting ### AuthenticationFilter is not accepted - Confirm the filter's `type` is `OIDC` and the `oidc` block is present. - Check that the Secrets referenced by `clientSecretRef`, `caCertificateRefs`, and `crlSecretRef` exist in the same namespace as the filter and contain the expected keys (`client-secret`, `ca.crt`, `ca.crl`). ### HTTPRoute is not accepted or reports `ResolvedRefs=False` - Verify the `extensionRef` in the HTTPRoute matches the `AuthenticationFilter` name and namespace exactly. - Confirm the route's `parentRefs` points to an HTTPS listener. OIDC filters are rejected on non-HTTPS listeners. ### Browser is stuck in a redirect loop - Confirm the `redirectURI` registered in the IdP exactly matches the path NGINX is using (default: `/oidc_callback__`). - Ensure the Gateway's TLS certificate is valid for the hostname the browser is using. ## Further reading - [Example deployment files for OIDC authentication](https://github.com/nginx/nginx-gateway-fabric/tree/main/examples/oidc-authentication) - [NGINX OIDC module reference](https://nginx.org/en/docs/http/ngx_http_oidc_module.html) - [How OpenID Connect works](https://openid.net/developers/how-connect-works/) - [Single Sign-On with OpenID Connect and Identity Providers](https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-oidc)