'Nginx - custom login page

I'm trying to secure my webapp by using nginx base authentication.

What I'm looking for is a way to force the browser to show my custom html login page instead of the default login popup but still handle the authorization process.

I try to omit the 'WWW-Authenticate' header and the popup wasn't display but I've no idea how to force the browser to add the 'Authorization' header for each request.

hereby nginx.conf:

location /{
            auth_basic "Restricted";
            auth_basic_user_file htpasswd;
            proxy_pass http://tomcat:8080/;
            error_page 401  /login.html;
    }

    location = /login.html {
            root html;
    more_clear_headers 'WWW-Authenticate';              
    }


Solution 1:[1]

*has* to be a better way to accomplish this, but i managed to do it with X-Accel-Redirect and php, here's how:

i wanted to have a custom login page for folder /foo/ (and recursively all its content)

... first i renamed the on-disk folder /var/www/html/foo to /var/www/html/internal_foo , then i added to nginx config:

location ~ /internal_foo {
        internal;
        # even if this is the default root, the internal-directive seems to prevent inheriting the root directive, so have to explicitly specify it here...
        root /var/www/html;
}
location ~ /foo {
        try_files "" /foo.php$is_args$args;
}

then i created a /var/www/html/foo.php with the contents:

<?php
// warning: script vulnerable to timing attack which can disclose existence of files. (realpath() is not constant-time)
if(is_authorised()){
    $translated="/internal_foo/".urldecode(substr($_SERVER['REQUEST_URI'],strlen("/foo/")));
        $file=__DIR__.$translated;
        $file=realpath($file);
        if(false===$file){http_response_code(404);exit();}
        if(0!==strpos($file,__DIR__.DIRECTORY_SEPARATOR."internal_foo")){
                // probably a hacker attempting ../../../etc/passwd schenanigans.
                http_response_code(400);
                exit();
        }
        header("X-accel-redirect: ".$translated);
}else{
        show_loginpage();
}

and voila, if a user is authorized, the request is sent to nginx, otherwise show_loginpage(); is executed (where you can put your custom login page), mission accomplished :)

Solution 2:[2]

You need to add proxy_intercept_errors on; or you will not be able to define error_page. Otherwise NGINX just passes the HTTP 401 response back to the client.

location /{
    auth_basic "Restricted";
    auth_basic_user_file htpasswd;
    proxy_pass http://tomcat:8080/;
    proxy_intercept_errors on;
    error_page 401  /login.html;
}

location = /login.html {
    root html;
    more_clear_headers 'WWW-Authenticate';              
}

Syntax: proxy_intercept_errors on | off;
Default: proxy_intercept_errors off;
Context: http, server, location
Determines whether proxied responses with codes greater than or equal to 300 should be passed to a client or be intercepted and redirected to nginx for processing with the error_page directive.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Marc