A Brief guide to Forward Auth

19 May 2024

Intoduction

Forward Auth is a simple authentication mechanism that is commonly used in reverse proxies such as Traefik, Caddy and Nginx to authenticate users before they reach the desired service. It moves the security out of the application being accessed and into a dedicated Authentication service (which often also handles OAuth).

The service being protected must trust that the environment has been correctly set up as anyone that can circumvent the proxy can fake authentication. The benefit to this approach is that it allows the application to focus on its core functionality rather than Authentication. This makes for a very simple SSO (Single Sign On) solution without the service needing to implement OAuth/OIDC or LDAP support.

The downside to forward auth is that there is no RFC or Specification that can be referenced to implement it, as it seems to have been copied and adapted from one service to another. From my rather small amount of research, it seems that the first implementation was Nginx’s http auth request module, which acted in a very similar way.

Implementaion

The actual “protocol” implementation is quite simple in nature. There are 3 key agents:

  • The Service being protected
  • The Authentication Service that verifies the user
  • The Reverse Proxy which coordinates the forward auth

Upon recieving the HTTP request the reverse proxy will first forward the exact message to the authentication service. If the request is “authenticated” the auth service responds with a 2XX status (and some additional headers) and the reverse proxy will then send on the original request with the additional headers attached.

If however the request is not authenticated the authentication service will respond with any non 2XX status. This indicates to the reverse proxy that the request should not be forwarded on to the service.

It is important to note here the difference between Nginx’s original implementation and Traefik/Caddy’s. Nginx expected specifically a 401/403 status code upon failure and considered anything else as an error. Whereas the newer services just return whatever non 2XX http response directly to the client. This means that the authentication service can respond with a 302 redirecting the user to a login page upon failed authentication.

Flowchart of an Authenticated Forward Auth

Flowchart of an Unauthenticated Forward Auth

After the user has logged in to the Auth service it should then redirect the user back to the original request url which will then trigger the Authenticated flow with the valid credentials and provide access to the service.

The headers added by the Authentication service give the service being protected the nessecary information to identify the user. This is commonly done through the Remote-User and Remote-Email headers although others are possible too. Here is a list of the common ones:

HeaderDescription
Remote-UserThe username of the authenticated user
Remote-EmailThe email of the authenticated user
Remote-GroupsA list of groups the user is in
Remote-NameThe display name of the authenticated user

Upsides and Downsides

One of the biggest upsides to forward auth is that it is very simple to implement by any service. It offers a simple alternative to the more complex standards out there today. Since there is no standard or RFC it is very much up to the implementations to decide how they want to authenticate the user. The most common way is to use a Cookie saved to the client which is then read and verified on each request.

This makes for a simple implementation but it also means that the cookie will need to be set on the base domain (as the auth & protected service will likely be on different subdomains). The fact that it means logging into 1 service logs you into all forward_auth services hosted under the same domain which can be either an upside or downside depending on the desired behavior.