Using Nextcloud as an authentication backend

I have a static website that is currently protected using HTTP authentication with the basic scheme.

I’d like to use Nextcloud as an authentication source, and I currently run nginx as the front web server.

tl;dr Here is the nginx configuration:

http {
	proxy_cache_path /tmp/nginx_cache levels=1:2 keys_zone=nextcloud_auth:1m max_size=1g inactive=60m use_temp_path=off;
	
	server {
		listen 443 ssl;
		
		location /protected {
			auth_request /auth;
		}
		location = /auth {
			internal;
			proxy_pass https://nextcloud.example.com/remote.php/dav/files/;
			proxy_pass_request_body off;
			proxy_set_header Content-Length "";
			proxy_set_header X-Original-URI $request_uri;
			
			proxy_cache_valid any      10m;
			proxy_cache nextcloud_auth;
			proxy_cache_key "$http_authorization";
			
			proxy_ignore_headers Cache-Control;
			proxy_ignore_headers    X-Accel-Expires;
			proxy_ignore_headers    Expires;
			proxy_ignore_headers    Vary;
			
			proxy_ignore_headers    Set-Cookie;
			proxy_hide_header       Set-Cookie;
			proxy_set_header        Cookie "";
		}
	}
}

And now for the explanations.

Global

It uses the auth_request directive that call an external resources, and examine the response.
The /auth path is defined as an internal subrequest, with some parameters, to make it works.

Remote endpoint

I didn’t find any better endpoint than /remote.php/dav/files/ It does not contains the user, it works with basic authentication scheme.

Basic parameters

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

They just disable every content, to be sure the request is as minimal as possible.

Caching

This is by far the toughest setting.

Why?

In my first tries, I was happy with the previous settings.
Until I realized that for every request, the Nextcloud endpoint was called.
It means that for each resources (image, CSS, JS), the server was annoyed with a PHP request,
that result in a lot of PHP parsing and loading, maybe database access and more.

This is completely unneeded: we already know the result from the first call.

Usually, the server set a cookie, and that really speeds up the process, along with many other advantages.
But in my case, it’s a global authorisation, so we can completely cache the result of the first request.

How

I let you read the documentation
of the cache handling, but basically, we need to strip every headers sent by Nextcloud,
that are usually useful, but that make nginx skip the caching, for good reasons:

proxy_ignore_headers    Cache-Control;
proxy_ignore_headers    X-Accel-Expires;
proxy_ignore_headers    Expires;
proxy_ignore_headers    Vary;

proxy_ignore_headers    Set-Cookie;
proxy_hide_header       Set-Cookie;
proxy_set_header        Cookie "";

And the real caching can now takes place. It requires two parts:

  1. global settings for a cache path
  2. local settings for caching our request

The first part is done using:

proxy_cache_path /tmp/nginx_cache levels=1:2 keys_zone=nextcloud_auth:1m max_size=1g inactive=60m use_temp_path=off;

It has to be in the http section. I used sane defaults, as mentioned in the
documentation,
but maybe you need to adapt at least the path.

The second part composed of the remaining directives:

proxy_cache_valid any      10m;
proxy_cache nextcloud_auth;
proxy_cache_key "$http_authorization";

It simply cache any results for 10 minutes, using the Authorization HTTP header as the key.
Since this header contains everything we need, it fits.

Result

Unsurprisingly, the first request is a bit long. After all, instead of a standard htpasswd file lookup,
it has to load some PHP libraries with some round-trip to authentication backends.

Surprisingly, once the authentication is cached, it is faster than the standard htpasswd file lookup.
Not by far, but on my Intel Atom server, I load a whole page in 500ms (using Firefox DevTools) instead of 600ms.

And of course, I have a single source of authentication for my static website.

Further steps

  • Does Nextcloud provides a better endpoint for external authentication?
  • Is there any way to test for a group instead of a single user?
  • I don’t know if cookies would be useful, and how to set it. nginx has a way to read and set headers,
    but as far as I tested, I can only access to the first Set-Cookie header.
2 Likes