Authentication Proxy: Simplify Authentication in Any Application

At please-open.it, we implement authentication solutions for applications across various languages and frameworks. Over the years, we’ve encountered the same challenges repeatedly: authentication is complex to integrate, session management is often misunderstood, and authentication concerns are tightly coupled with application code.

We are convinced that the authentication proxy pattern is by far the most efficient, effective, and simple approach to implement in enterprises. While this architectural pattern isn’t new, we wanted to make it more accessible. That’s why we built a ready-to-use solution based on OpenResty’s OIDC module—an authentication proxy that acts as a security gateway between users and backend applications.

Project: https://github.com/please-openit/authentication-proxy

The Authentication Integration Challenge

Why is Authentication Complex?

Integrating authentication into applications is rarely straightforward, regardless of the programming language or framework you’re using:

  • Multiple protocols to understand: OAuth2, OpenID Connect, SAML each have their own specifications
  • Security best practices: Token validation, secure storage, CSRF protection, and more
  • Session management complexity: Handling session lifecycle, refresh tokens, logout propagation
  • Framework-specific implementations: Each language/framework has its own authentication libraries with different APIs
  • Testing difficulties: Authentication flows are hard to test in development environments

The Session Management Problem

One of the most common issues we see is poor session management. Many applications:

  • Create their own session storage mechanisms (often insecure)
  • Don’t properly validate tokens on each request
  • Fail to implement proper session timeouts
  • Don’t handle token refresh correctly
  • Mix authentication state with application state

This leads to security vulnerabilities, inconsistent behavior, and maintenance nightmares.

Enter the Authentication Proxy Pattern

The authentication proxy pattern shifts authentication responsibility from the application to a dedicated component sitting in front of it. This architectural approach brings numerous benefits.

What is an Authentication Proxy?

An authentication proxy is a reverse proxy that:

  1. Intercepts all incoming requests before they reach your application
  2. Authenticates users via an identity provider (like Keycloak, Auth0, Okta)
  3. Manages sessions independently from your application
  4. Injects user information as HTTP headers to your backend
  5. Proxies authenticated requests to your application

Your application receives pre-authenticated requests with user information in headers—no authentication code required.

Our OpenID Connect Authentication Proxy

We’ve built an authentication proxy based on NGINX + OpenResty with Lua scripting that provides enterprise-grade authentication for any backend application.

Key Features

Full OpenID Connect Support

  • Standards-compliant OIDC authentication
  • Works with any OIDC provider (Keycloak, Auth0, Okta, Google, etc.)
  • Automatic token validation and refresh

Define Public vs Private URLs

  • Configure which URLs require authentication
  • Public endpoints remain accessible without login
  • Fine-grained access control per route

Claims-Based Header Injection

  • Inject user information from OIDC tokens into HTTP headers
  • Configure any token claim as a header (email, roles, tenant ID, etc.)
  • Your backend receives X-USER, X-EMAIL, X-ROLES, etc.

Optional Interstitial Page

  • Display security information or terms of service before access
  • Toggle on/off via feature flag
  • Fully customizable HTML page

Lightweight and Efficient

  • Based on NGINX/OpenResty - proven performance
  • Redis-backed session storage
  • Minimal resource footprint
  • Scales horizontally

Simple Deployment

  • Docker Compose ready
  • Interactive configuration script
  • Single configuration file (lua/config.lua)
  • No compilation required

Architecture Overview

graph LR
    Browser[Browser]
    Proxy[Authentication Proxy - NGINX + OpenResty]
    OIDC[OIDC Provider - Keycloak]
    Redis[(Redis Sessions)]
    Backend[Backend Application]
    
    Browser <-->|1 Request| Proxy
    Proxy <-->|2 Auth| OIDC
    Proxy <-->|3 Session| Redis
    Proxy -->|4 Request + Headers| Backend
    
    style Proxy fill:#4a90e2,color:#fff
    style OIDC fill:#50c878,color:#fff
    style Redis fill:#d32f2f,color:#fff
    style Backend fill:#9c27b0,color:#fff

Components:

  • NGINX/OpenResty: Web server with Lua scripting capabilities
  • lua-resty-openidc: OpenID Connect library for Lua
  • Redis: Session storage backend
  • Lua Scripts: Authentication logic and header injection
  • Backend Application: Your application (in any language)

How It Works

Authentication Flow

  1. User accesses the application (e.g., https://app.example.com/dashboard)
  2. Proxy checks authentication:
    • Session exists? → Continue to step 5
    • No session? → Redirect to identity provider
  3. User logs in at the identity provider (Keycloak, etc.)
  4. Proxy receives tokens and creates a session in Redis
  5. Proxy injects headers from token claims:
    X-USER: user-id-123
    X-EMAIL: user@example.com
    X-NAME: john.doe
    X-ROLES: admin,editor
    
  6. Request forwarded to backend with user context

Filtering on User Claims

The proxy can filter access based on user attributes from the OIDC token. For example:

-- Example: Only allow users with 'admin' role
if not has_role(res.id_token.roles, "admin") then
    ngx.status = 403
    ngx.say("Access denied: admin role required")
    return ngx.exit(403)
end

You can implement custom logic to:

  • Restrict access by roles
  • Filter by tenant/organization
  • Validate custom claims
  • Implement complex authorization rules

This Pattern is Universally Applicable

The authentication proxy pattern works for almost any application:

Language Agnostic

  • Python (Django, Flask, FastAPI)
  • Node.js (Express, NestJS)
  • Java (Spring Boot, Quarkus)
  • Go (Gin, Echo)
  • Ruby (Rails, Sinatra)
  • PHP (Laravel, Symfony)
  • Legacy applications (even those without source code access!)

Architecture Compatible

  • Monoliths
  • Microservices
  • Serverless functions (with some adaptations)
  • Static sites with API backends
  • Third-party applications (SaaS tools you can’t modify)

Use Cases

  • Add authentication to legacy applications without code changes
  • Protect internal tools with SSO
  • Unify authentication across multiple applications
  • Protect APIs with OIDC tokens
  • Add MFA/2FA to any application (handled by identity provider)
  • Implement custom authentication flows without touching app code

Separation of Concerns: A Better Architecture

One of the biggest advantages of this pattern is the clear separation between authentication and application logic.

Before (Coupled Architecture)

graph TB
    subgraph Application
        Auth[Authentication Code: OIDC client, Token validation, Session mgmt, Logout]
        Business[Business Logic: Your actual application code]
    end
    
    style Application fill:#f0f0f0,stroke:#333,stroke-width:2px
    style Auth fill:#ff9999,color:#000
    style Business fill:#99ccff,color:#000

Problems:

  • Developers must learn authentication protocols
  • Authentication bugs affect the entire application
  • Difficult to change authentication methods
  • Hard to test business logic in isolation

After (Proxy Architecture)

graph LR
    Proxy[Authentication Proxy: OIDC client, Token validation, Session mgmt, Header injection]
    
    subgraph Application
        Business[Business Logic - Pure app code]
    end
    
    Proxy -->|Authenticated requests + User headers| Application
    
    style Proxy fill:#4a90e2,color:#fff
    style Business fill:#99ccff,color:#000
    style Application fill:#f0f0f0,stroke:#333,stroke-width:2px

Benefits:

  • Developers focus on business logic - no authentication code
  • Security team owns authentication - proper separation of responsibilities
  • Easy to change authentication - modify proxy config, not app code
  • Consistent authentication - same mechanism across all apps
  • Testable - test business logic without mocking authentication

Who Does What?

ResponsibilityAuthentication ProxyApplication Team
User authentication✅ Proxy handles it❌ Not their concern
Session management✅ Proxy manages❌ Not their concern
Token validation✅ Proxy validates❌ Not their concern
User logout✅ Proxy handles❌ Not their concern
Business logic❌ Not proxy’s job✅ App team owns it
Authorization rules🤝 Shared (proxy filters, app decides)🤝 Shared

This separation allows:

  • Security/DevOps teams to configure and maintain authentication
  • Development teams to focus on features
  • Clear accountability for each layer

Total Freedom for Authentication Layer

Since authentication is handled by the proxy, you have complete freedom to configure it at the identity provider level:

Multi-Factor Authentication (MFA/2FA)

Add MFA without touching your application:

  • SMS codes
  • Authenticator apps (TOTP)
  • Push notifications
  • Hardware tokens (YubiKey, etc.)
  • Biometric authentication

Configure it once in your identity provider (Keycloak), and all applications benefit.

Authentication Methods

Choose any authentication method:

  • Username/password
  • Social login (Google, Facebook, GitHub)
  • Enterprise SSO (SAML, LDAP, Active Directory)
  • Passwordless (magic links, WebAuthn)
  • Certificate-based authentication

Conditional Access

Implement sophisticated access policies:

  • Step-up authentication (require MFA for sensitive actions)
  • Risk-based authentication (IP location, device fingerprinting)
  • Time-based access restrictions
  • Geo-fencing

Authentication Flows

Customize the authentication experience:

  • Custom login pages
  • Brand-specific themes
  • Terms of service acceptance
  • Privacy policy agreements
  • Custom user registration flows

All of this without changing a single line of your application code.

Configuration Made Simple

Our proxy includes an interactive Python script that generates configuration:

./generate_config.py

The script asks:

  • Enable interstitial page? (yes/no)
  • OIDC discovery URL? (e.g., https://keycloak.example.com/realms/myrealm/.well-known/openid-configuration)
  • Client ID? (e.g., my-app)
  • Client secret? (e.g., secret123)
  • HTTP headers to inject? (e.g., X-USER from sub claim)
  • Backend URL? (e.g., https://backend.example.com)

It generates lua/config.lua:

local _M = {}

-- Feature flag to enable/disable the interstitial page
_M.interstitial_enabled = true

-- OpenID Connect configuration
_M.oidc = {
    ssl_verify = "no",
    redirect_uri = "http://localhost/cb",
    discovery = "https://keycloak.example.com/realms/myrealm/.well-known/openid-configuration",
    client_id = "my-app",
    client_secret = "secret123"
}

-- User headers configuration
_M.user_headers = {
    { name = "X-USER", claim = "sub" },
    { name = "X-EMAIL", claim = "email" },
    { name = "X-NAME", claim = "preferred_username" },
    { name = "X-ROLES", claim = "roles" },
}

-- Backend URL to proxy to
_M.backend_url = "https://backend.example.com/"

return _M

Deployment

Docker Compose (Development)

git clone https://github.com/please-openit/authentication-proxy
cd authentication-proxy
./generate_config.py
docker-compose up -d

Access your authenticated application at http://localhost

Production Deployment

Deploy the proxy in front of your application:

graph TB
    Internet[Internet]
    LB[Load Balancer]
    Proxy[Authentication Proxy - NGINX + OpenResty]
    Backend[Backend Application]
    
    Internet --> LB
    LB --> Proxy
    Proxy --> Backend
    
    style LB fill:#ff9800,color:#fff
    style Proxy fill:#4a90e2,color:#fff
    style Backend fill:#9c27b0,color:#fff

Requirements:

  • Redis instance (for sessions)
  • Identity provider (Keycloak, Auth0, etc.)
  • SSL/TLS certificates (handled by load balancer or NGINX)

Kubernetes Example

apiVersion: apps/v1
kind: Deployment
metadata:
  name: auth-proxy
spec:
  replicas: 3
  template:
    spec:
      containers:
      - name: nginx
        image: your-registry/auth-proxy:latest
        env:
        - name: REDIS_HOST
          value: redis-service
      - name: redis
        image: redis:alpine

Resource Efficiency

The proxy is extremely lightweight:

  • Memory: ~50-100MB per NGINX worker process
  • CPU: Minimal overhead (mostly I/O bound)
  • Latency: Adds < 10ms to request time
  • Throughput: Handles thousands of requests/second

This makes it cost-effective even for high-traffic applications.

Real-World Use Cases

Case 1: Legacy Application Modernization

A client had a PHP 5.4 legacy application with custom authentication that didn’t support modern protocols.

Solution:

  • Deployed authentication proxy in front
  • Configured Keycloak as identity provider
  • Application reads user from X-USER header
  • Added MFA without touching legacy code

Result: Modern authentication for a 10-year-old application in 2 days.

Case 2: Microservices Authentication

A client with 15 microservices in different languages needed unified authentication.

Solution:

  • Single authentication proxy for all services
  • Each service reads headers (X-USER, X-TENANT)
  • Consistent authentication across the stack
  • No authentication libraries in any microservice

Result: Simplified architecture, faster development, easier maintenance.

Case 3: Third-Party Tool Protection

A client needed to protect a self-hosted GitLab instance with their corporate SSO.

Solution:

  • Authentication proxy in front of GitLab
  • GitLab configured to trust X-USER header
  • Corporate SSO (Keycloak with LDAP) handles authentication

Result: SSO integration without modifying GitLab.

Comparison with Alternatives

ApproachProsCons
Authentication Proxy✅ Language agnostic
✅ No code changes
✅ Centralized auth
✅ Easy to change
⚠️ Extra network hop
⚠️ Single point of failure (mitigated with HA)
Application-level Auth✅ No extra component
✅ Full control
❌ Requires code changes
❌ Language-specific
❌ Duplicated across apps
API Gateway Auth✅ Enterprise features
✅ Centralized
❌ Vendor lock-in
❌ Expensive
❌ Complex setup
Service Mesh Auth✅ Microservices-native
✅ Zero-trust
❌ Kubernetes only
❌ Steep learning curve
❌ Resource intensive

The authentication proxy strikes a balance: simple, effective, and flexible.

Limitations and Considerations

While powerful, the authentication proxy pattern has some limitations:

Not Suitable For:

  • Public APIs with API keys - Use API gateway patterns instead
  • Machine-to-machine authentication - Use client credentials flow directly
  • Scenarios requiring fine-grained authorization - Combine with application-level authorization

Security Considerations:

  • Header injection security: Ensure backend trusts only the proxy (network isolation or mutual TLS)
  • Session storage: Redis should be secured and backed up
  • High availability: Deploy multiple proxy instances behind a load balancer
  • Token refresh: Handled automatically by lua-resty-openidc

Getting Started

1. Clone the Repository

git clone https://github.com/please-openit/authentication-proxy
cd authentication-proxy

2. Configure Your Identity Provider

Create an OpenID Connect client in your identity provider (Keycloak, Auth0, etc.):

  • Client type: Confidential
  • Redirect URI: http://localhost/cb (adjust for production)
  • Note the client ID and client secret

3. Generate Configuration

./generate_config.py

Answer the prompts with your OIDC provider details.

4. Start the Proxy

docker-compose up -d

5. Access Your Application

Navigate to http://localhost - you’ll be redirected to login, then back to your application with authentication.

Conclusion

The authentication proxy pattern simplifies authentication integration dramatically:

  • No code changes in your application
  • Works with any language or framework
  • Separates concerns between security and development teams
  • Centralized authentication across multiple applications
  • Easy to deploy and maintain
  • Flexible authentication methods at the identity provider level
  • Lightweight and efficient resource usage

Our NGINX-based authentication proxy makes this pattern accessible to everyone. It’s open source, production-ready, and battle-tested.

Try it today: https://github.com/please-openit/authentication-proxy

At please-open.it, we believe authentication should be simple, secure, and separate from business logic. The authentication proxy embodies these principles.


Have questions or need help implementing this pattern? Reach out to us at please-open.it - we specialize in authentication and authorization architectures.