External Authentication Policy in NGINX Ingress Controller: Patterns for VirtualServer and Ingress

by

in , ,
Man holding a laptop and standing in front of an oversized computer screen displaying a checklist

NGINX Ingress Controller 5.5.0 introduced ExternalAuth, a new Policy type that lets you define external authentication once in a Policy resource and apply it consistently across both VirtualServer and Ingress traffic paths. This is the first blog in a two part series that covers the ExternalAuth Policy, and is focused on:

  • How ExternalAuth policy works in NGINX Ingress Controller.
  • Where to attach it in VirtualServer, VirtualServerRoute, and Ingress.
  • Patterns for basic auth, OAuth2, and per-route policies.

Why Use a Policy for External Authentication?

Many teams start with per-service authentication logic or raw NGINX config snippets and quickly end up with drift. Using a dedicated Policy for external auth gives you:

  • A single source of truth for your authentication configuration.
  • Reuse across services and namespaces.
  • Cleaner reviews because auth rules are isolated from route logic.

How ExternalAuth Policy Works in NGINX Ingress Controller

At a high level:

  1. Create a Policy resource with spec.externalAuth.
  2. Attach it where traffic is defined:
    • VirtualServer.spec.policies (or per-route spec.routes[].policies)
    • VirtualServerRoute.spec.subroutes[].policies
    • Ingress via the nginx.org/policies annotation, or per-route via Mergeable Ingress minion annotations
  3. NGINX Ingress Controller renders an internal auth_request location that proxies every inbound request to your auth service before forwarding it to the backend.
    • A 2xx response allows the request through.
    • A 401 or 403 response rejects it.

If the auth service returns 401 and authSigninURI is configured, NGINX redirects the client there instead, enabling browser-based OAuth2 flows.

Example Basic Auth Policy

apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
  name: external-auth-basic-policy
spec:
  externalAuth:
    authURI: "/auth"
    authServiceName: "default/basic-auth-svc"
    sslEnabled: true
    sslVerify: true
    sslVerifyDepth: 2
    sniName: "external-auth-tls"
    trustedCertSecret: "external-auth-ca-secret"

Example OAuth2 Policy With Sign-in Redirect

The examples in this blog use oauth2-proxy with GitHub, but the ExternalAuth policy works with any OAuth2/OIDC provider — just configure oauth2-proxy for your provider of choice (Google, Azure AD, Okta, Keycloak, etc.):

apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
  name: external-auth-oauth2-policy
spec:
  externalAuth:
    authURI: "/oauth2/auth"
    authSigninURI: "/oauth2/signin"
    authServiceName: "default/oauth2-proxy-svc"
    sslEnabled: true
    sslVerify: true
    sslVerifyDepth: 2
    sniName: "external-auth-tls"
    trustedCertSecret: "external-auth-ca-secret"

Example VirtualServer Referencing ExternalAuth Policies

You can deploy both of the above policies end-to-end with the VirtualServer external-auth-oauth2 example on GitHub, or the basic-auth-only example for a simpler starting point.

Policies can be applied at the server level (all routes inherit it) or at the route level (per-path control). This VirtualServer applies different auth per route:

apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
  name: cafe
spec:
  host: cafe.example.com
  tls:
    secret: tls-secret
    redirect:
      enable: true
  upstreams:
  - name: tea
    service: tea-svc
    port: 80
  - name: coffee
    service: coffee-svc
    port: 80
  routes:
  - path: /tea
    action:
      pass: tea
    policies:
      - name: external-auth-basic-policy
  - path: /coffee
    action:
      pass: coffee
    policies:
      - name: external-auth-oauth2-policy

Example Ingress Referencing an ExternalAuth Policy

Reference the policy in the nginx.org/policies annotation to protect all paths on the Ingress:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: cafe-ingress
  annotations:
    nginx.org/policies: "external-auth-basic-policy"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - cafe.example.com
    secretName: tls-secret
  rules:
  - host: cafe.example.com
    http:
      paths:
      - path: /tea
        pathType: Prefix
        backend:
          service:
            name: tea-svc
            port:
              number: 80
      - path: /coffee
        pathType: Prefix
        backend:
          service:
            name: coffee-svc
            port:
              number: 80

You can deploy this end-to-end with the Ingress external-auth example on GitHub.

To apply different policies to different paths, use the Mergeable Ingress (master/minion) pattern: create a master Ingress for the host and TLS, and set nginx.org/policies on each minion independently. For example, the /tea minion can reference external-auth-basic-policy and the /coffee minion can reference external-auth-oauth2-policy. See the Ingress external-auth-mergeable example on GitHub for a complete walkthrough.

Important Behavior to Remember

  • For VirtualServer and VirtualServerRoute, a route-level policy overrides the same-type policy at spec.policies.
  • Only one ExternalAuth policy per route is applied. If multiple ExternalAuth policies are referenced on the same route, the first one is used and subsequent ones are ignored.
  • Without authSigninURI, unauthenticated requests receive a bare 401 instead of being redirected.
  • When authSigninURI is set, NGINX Ingress Controller generates a location block for authSigninRedirectBasePath (defaults to /oauth2) that proxies all requests under that path to the authentication service. This covers the OAuth2 callback endpoint (typically /oauth2/callback). Override the default with authSigninRedirectBasePath if your auth service uses a different path prefix.
  • The trustedCertSecret must be type nginx.org/ca with the CA stored under the ca.crt key. Use namespace/secret for cross-namespace references.
  • When deploying oauth2-proxy, set redirect_url to the full callback URL on your host (e.g. https://cafe.example.com/oauth2/callback). This is the URL your OAuth provider redirects back to after authorization. It must match what you registered in the OAuth App settings.

More Information

You can find complete working examples on GitHub and more documentation in our docs:

NGINX Community Forum