Skip to content

Flexible and easily configurable oauth2 forward authentication service for use with traefik and nginx.

License

Notifications You must be signed in to change notification settings

mkuhlmann/forward-auth

Repository files navigation

forward-auth

GitHub Workflow Status

Highly flexible forward auth service for use with an oauth endpoint and a reverse proxy (e.g. traefik).

Configuration

forward-auth can be configurated in three ways, values are applied in following priority (low to high):

config.json < environment variables < query params

Please use UPPER_CASE in environment variables, lower_case otherwise. Note that listen_host, listen_port, app_key, cookie_name and cookie_age cannot be set via query params.

The following options are available:

Config Key Description Required Default
listen_host host to bind 0.0.0.0
listen_port port to bind 8080
app_key keys for cookie signing, passed to koajs
cookie_name Name of Cookie __auth
cookie_age Max age of cookie in seconds 604800 (7 days)
redirect_code HTTP status code to return, when redirectingbecause 302
authorize_url OAuth Authorization Request URL (spec)
token_url OAuth Access Token Endpoint
userinfo_url OpenID Connect UserInfo endpoint, must include sub field
client_id OAuth Client Id
client_secret OAuth Client Secret
allowed_users Comma-seperated list of allowed subs, empty = anyone []
scopes Comma-seperated OAuth Scopes id

When client is authenticated, forward_auth passes X-Auth-User with the sub and X-Auth-Info with the json encoded userinfo_url response, those may be passed to your application via the reverse proxy (see example below).

Usage

Example docker-compose.yml

version: '3.5'
  
services:
  traefik:
    image: traefik:2.2
    restart: always
    command:
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
    ports:
      - 80:80
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock

      
    forward_auth:
      image: mkuhlmann/forward-auth
      restart: unless-stopped
      environment:
        - APP_KEY=CHANGE_ME
        - AUTHORIZE_URL=https://example.com/oauth/authorize
        - TOKEN_URL=https://example.com/oauth/token
        - USERINFO_URL=https://example.com/oauth/userinfo
        - CLIENT_ID=clientid
        - CLIENT_SECRET=verysecret
    
    nginx:
      image: nginx:mainline-alpine
      networks:
        - proxy
      labels:
        - "traefik.enable=true"
        - "traefik.http.services.nginx.loadbalancer.server.port: 80"
        - "traefik.http.routers.nginx.entrypoints=web"
        - "traefik.http.routers.nginx.rule=Host(`private.example.com`)"
        - "traefik.http.middlewares.forward_auth.forwardauth.address=http://forward_auth:8080/auth?allowed_users=ALLOWED_USER_SUB"
        - "traefik.http.middlewares.forward_auth.forwardauth.authResponseHeaders=X-Auth-User,X-Auth-Info"

Example nginx config, be sure to set redirect_code to 403!

server {
	listen 443 ssl http2;
	listen [::]:443 ssl http2;
	server_name secret.example.com;

	location = /auth {
		internal;
		proxy_pass http://forward_auth:8080;
		proxy_intercept_errors on;
		proxy_set_header Host $host;
		proxy_set_header X-Real-IP $remote_addr;
		proxy_set_header X-Forwarded-Host $host;
		proxy_set_header X-Forwarded-Proto $scheme;
		proxy_set_header X-Forwarded-Uri $request_uri;

		proxy_pass_request_headers on;
		proxy_set_header Content-Length "";
	}

	location @auth_redirect {
		add_header Set-Cookie $auth_cookie;
		return 302 $auth_location;
	}

	location / {
		auth_request /auth;
		auth_request_set $auth_location $upstream_http_location;

		auth_request_set $auth_cookie $upstream_http_set_cookie;
		add_header Set-Cookie $auth_cookie;

		error_page 403 = @auth_redirect;
		error_page 401 = /no_auth;

		auth_request_set $auth_user  $upstream_http_x_auth_user;
		auth_request_set $auth_info  $upstream_http_x_auth_info;
		proxy_set_header X-Auth-User $auth_user;
		proxy_set_header X-Auth-Info $auth_info;

		proxy_buffering off;
		proxy_pass http://upstream;
		proxy_set_header Host $host;
		proxy_redirect http:// https://;
		proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
		proxy_set_header Upgrade $http_upgrade;
		proxy_set_header Connection $connection_upgrade;
	}

	location = /noauth {
		internal;
		add_header Content-Type text/plain;
		return 200 'unauthenticated';
	}
}

Contributing

Pull request are very welcome!