I wish there were better authentication options with Nginx.
The ngx_http_auth_request_module is limited: First, it assumes that the authentication agent doesn't need to talk to the user. Second, it doesn't cache the authentication.
Perhaps nginx might instead check all requests for a particular signed cookie, verify the signature, if the signature matches, verify that the cookie isn't too old, and then unpack variables from the cookie that the application server might want, such as REMOTE_USER. It seems nginx would then want to freshen-up the cookie.
If the cookie doesn't exist, signature doesn't match, or the cookie has expired, then, nginx should proxy the request to a delegate... but, it should return the results of that delegation directly to the user agent. It'd be the job of the delegate to set/sign the cookie with the information needed when authentication succeeds.
In this way, the authentication agent has full control over the process (so it doesn't have to be in nginx), and, heavyweight authentication is cached.
EDIT: Thanks mixedbit -- you're correct that nginx will forward 3xx onto the client. However, I recall patches are needed to support headers; and, without 200 going to the client, how do you support LDAP form authentication? Even so, an extra sub-request to authenticate each request is still heavyweight.
Unless you want to use Nginx as an SSL-offloading proxy for a bunch of internal apps that you want to protect from the public but your apps themselves don't use the session in any way? Yes, we can use Lua and effectively write our own, but one of the reasons I've considered Apache again is that there's now a plugin for OAuth 2 + OpenID Connect ;-) https://github.com/pingidentity/mod_auth_openidc
You'll lose some of the benefits of Nginx at that point, since part of why people like Nginx is how it handles connections, proxying and caching. And the internal apps aren't always mine to maintain, e.g. Apple's Xcode server.
But yeah, there are options in Apache-land, my post was more that nginx could eventually gain those options too :)
In some cases it would be useful to perform session handling like this in nginx. I like the idea of building a reverse proxy which handles authentication and sessions, in front of a backend web page which wasn't designed to handle it.
Something like giving access to an old internal intranet without having to change the app.
this reminds me of a government agency that permits access by IP addresses whitelisted in IIS. Can't put fancy caching or load balancing in front because it can't understand X-Forwarded-For, etc.
tl;dr build your authentication into your app, not the web server layer.
There are plenty of upsides to decoupling authentication from your app codebases. For instance, in an enterprise where you have a single sign-on solution implemented as a web server module and a mix of third party and bespoke web apps.
I think no one argues that decoupling isn't the way, in fact that's exactly what others are saying too, but in the proper way. Make an authentication (micro)service and call it from your webapp. Proxy should proxy, auth service should manage (and cache, keep up to date) credentials, role associations and group memberships, web app should serve web pages (based on the business logic coded into it).
I've been using a pubcookie module (http://www.vitki.net/book/page/pubcookie-module-nginx) to do authentication across multiple subdomains (x.example.org, y.example.org, z.example.org). The idea being, you only have to authenticate to one of them in order to access any of them.
However, the module hasn't been updated in forever, and to build it in recent versions of nginx, I have to turn certain CFLAGS off (i.e. Werror).
Does ngx_http_auth_request_module seem like it could do pubcookie's job? Or perhaps, can I approach this problem using ngx_lua?
This isn't correct, you can use auth_request when authentication agent needs to talk to the user. I can't even see how it could be used without such communication.
For some reason, I thought this behaviour made it to the upstream, till I re-read the official ngx_http_auth_request documentation and realized it doesn't pass through 3xx or headers other than WWW-Authenticate:
The ngx_http_auth_request_module module (1.5.4+) implements
client authorization based on the result of a subrequest.
If the subrequest returns a 2xx response code, the access
is allowed. If it returns 401 or 403, the access is
denied with the corresponding error code. Any other
response code returned by the subrequest is considered
an error. For the 401 error, the client also receives
the “WWW-Authenticate” header from the subrequest response.
No, I was thinking about the original auth_request. For cookies based authentication you need to turn off authorization for login pages (because every visitor should be allowed to access login pages) and pass login requests directly to your auth backend. The auth backend can then verify password, set cookies etc. auth_request failures 401, 403 can also be configured to show login page to the user.
Perhaps nginx might instead check all requests for a particular signed cookie, verify the signature, if the signature matches, verify that the cookie isn't too old, and then unpack variables from the cookie that the application server might want, such as REMOTE_USER. It seems nginx would then want to freshen-up the cookie.
If the cookie doesn't exist, signature doesn't match, or the cookie has expired, then, nginx should proxy the request to a delegate... but, it should return the results of that delegation directly to the user agent. It'd be the job of the delegate to set/sign the cookie with the information needed when authentication succeeds.
In this way, the authentication agent has full control over the process (so it doesn't have to be in nginx), and, heavyweight authentication is cached.
EDIT: Thanks mixedbit -- you're correct that nginx will forward 3xx onto the client. However, I recall patches are needed to support headers; and, without 200 going to the client, how do you support LDAP form authentication? Even so, an extra sub-request to authenticate each request is still heavyweight.