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
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
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.
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.
An authentication proxy is a reverse proxy that:
- Intercepts all incoming requests before they reach your application
- Authenticates users via an identity provider (like Keycloak, Auth0, Okta)
- Manages sessions independently from your application
- Injects user information as HTTP headers to your backend
- Proxies authenticated requests to your application
Your application receives pre-authenticated requests with user information in headers—no authentication code required.
We’ve built an authentication proxy based on NGINX + OpenResty with Lua scripting that provides enterprise-grade authentication for any backend application.
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
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:#fffComponents:
- 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)
- User accesses the application (e.g.,
https://app.example.com/dashboard) - Proxy checks authentication:
- Session exists? → Continue to step 5
- No session? → Redirect to identity provider
- User logs in at the identity provider (Keycloak, etc.)
- Proxy receives tokens and creates a session in Redis
- Proxy injects headers from token claims:
X-USER: user-id-123 X-EMAIL: user@example.com X-NAME: john.doe X-ROLES: admin,editor - Request forwarded to backend with user context
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
The authentication proxy pattern works for almost any application:
- 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!)
- Monoliths
- Microservices
- Serverless functions (with some adaptations)
- Static sites with API backends
- Third-party applications (SaaS tools you can’t modify)
- 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
One of the biggest advantages of this pattern is the clear separation between authentication and application logic.
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:#000Problems:
- Developers must learn authentication protocols
- Authentication bugs affect the entire application
- Difficult to change authentication methods
- Hard to test business logic in isolation
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:2pxBenefits:
- 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
| Responsibility | Authentication Proxy | Application 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
Since authentication is handled by the proxy, you have complete freedom to configure it at the identity provider level:
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.
Choose any authentication method:
- Username/password
- Social login (Google, Facebook, GitHub)
- Enterprise SSO (SAML, LDAP, Active Directory)
- Passwordless (magic links, WebAuthn)
- Certificate-based authentication
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
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.
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-USERfromsubclaim) - 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
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
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:#fffRequirements:
- Redis instance (for sessions)
- Identity provider (Keycloak, Auth0, etc.)
- SSL/TLS certificates (handled by load balancer or NGINX)
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
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.
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-USERheader - Added MFA without touching legacy code
Result: Modern authentication for a 10-year-old application in 2 days.
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.
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-USERheader - Corporate SSO (Keycloak with LDAP) handles authentication
Result: SSO integration without modifying GitLab.
| Approach | Pros | Cons |
|---|---|---|
| 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.
While powerful, the authentication proxy pattern has some limitations:
- 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
- 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
git clone https://github.com/please-openit/authentication-proxy
cd authentication-proxy
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
./generate_config.py
Answer the prompts with your OIDC provider details.
docker-compose up -d
Navigate to http://localhost - you’ll be redirected to login, then back to your application with authentication.
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.