Securing API routes with OAuth2 and nginx auth_request

When an API is build in parts, it’s nice to be able to build services without embedding authorization access inside each of them. It allows a better separation of concerns. In that pattern, the services shall call the auth server to check if user is authorized to access the services.

Nginx allows to do that with auth_request.

I had a bit of pain to connect all the dots but eventually it ends up adding only two lines of configuration to any route that you want to secure.

  1. Checking if the request is authorized with auth_request.
  2. Returning the error message from the auth_request by proxying that request on a 401 by displaying a custom error page with error_page 401.

Both request are done on a /auth route that proxy the auth server.

Authorize request

auth_request checks the authentification status and returns 401 unauthorized or 200 authorized. We need to pass Authorization header so that the bearer token is provided to the auth server. We do this with :

proxy_pass_header Authorization;
proxy_set_header Authorization $http_authorization;

We probably don’t need post data when checking auth so we do not forward this content to the auth server with :

proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;

Returning authorization response

If the auth_request response was a 401 then we show the result of the /auth request as an error page.

error_page 401 =401 /auth;

The full nginx config (with PHP-FPM )

server {
listen 80 default_server;
server_name _;
    index index.php;
rewrite_log on;
root /var/www/html/public;
location / {
auth_request /auth;
error_page 401 =401 /auth;
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
# With php5-fpm:
fastcgi_index index.php;
fastcgi_connect_timeout 3s;
fastcgi_read_timeout 60s;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include /etc/nginx/fastcgi_params;
}
location = /auth {
proxy_pass http://api/v1/oauth2/user;
proxy_pass_header Authorization;
proxy_set_header Authorization $http_authorization;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
}
}