Package: nginxwebauthn-jv
NGINX + WebAuthn for your small scale web applicationsWhat is this for?If you run some small services on a public-facing server that you would like to protect (i.e. Jupyter of VS code-server) and have a Yubikey or similar, you can use this repository to add secure, public-key authentication to themwithout modifying the original service itself.How to set up?Runnginx -Vand check it is compiled withauth_request_moduleotherwise recompile it with--with-http_auth_request_moduleconfiguration parameter. Then set up NGINX to proxy your service, note that you will also need SSL because WebAuthn only works over HTTPS.server {
    server_name myserver.bennewhouse.com; # managed by Certbot

    # Redirect everything that begins with /auth to the authorization server
    location /nginx_fido_auth {
        proxy_pass http://127.0.0.1:8000;
    }

    # If the authorization server returns 401 Unauthorized, redirect to /atuh/login
    error_page 401 = @error401;
    location @error401 {
        return 302 /nginx_fido_auth/login$request_uri;
    }

    root /var/www/html;
    index index.html;
    location / {
        auth_request /nginx_fido_auth/check; # Ping /auth/check for every request, and if it returns 200 OK grant access
        # The next two lines are ptional config for authentication header needed by your application.
        # Replace X-SSL-CERT with any header your application expects to carry the user ID.
        auth_request_set $fido $upstream_http_fido_user;
        passenger_set_header 'X-SSL-CERT' $fido;

        # Here is where you would put other proxy_pass info to forward to Jupyter, etc. In this example I'm just serving raw HTML
    }

    # Override the application logout url with the fido auth logout url. Replace /users/logout with url where your application logout button points to.
    location /users/logout {
        return 302 /nginx_fido_auth/logout
    }

    listen [::]:443 ssl ; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/myserver.bennewhouse.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/myserver.bennewhouse.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}Reload NGINX with the aforementioned configuration. Next install the nginxwebauthn Python package as root -- dependencies needs to be installed globally to be able to add user credentials later. Other option is to change privileges to/opt/nginxwebauthnto be able to write there by regular user which may be a security flaw. Install script takes two arguments -- user and group for the script to be started as.sudo su
pip3 install nginxwebauthn-jv
nginxwebauthn-ubuntu-install.py user groupIfnginxwebauthn-ubuntu-install.pyfails for any reason you can try to replicate steps incat `which nginxwebauthn-install.py`. Basically it creates a directory structure in/opt/nginxwebauthnand configures systemd service to run thenginxwebauthn.pyscript as a daemon.How to use?Browse to your site with appended/nginx_fido_auth/registerin a browser that supports WebAuthn. Insert your security key when requested, and the page will tell you to run a command that looks like:sudo python3 nginx-webauthn.py save-client myserver.bennewhouse.com *big long base64 string* *big long base64 string* auth_nameReplaceauth_namewith something unique that will help you identify the credential and run that on the server. You only need to do this once per key. The credentials are stored in/opt/nginxwebauthn/credentials/auth_name. If you set up your NGINX to expect the HTTP header to distinguish the authenticated user put the header contents to/opt/nginxwebauthn/headers/auth_name. The header file is paired with credential file by file name.\ncharacters in headers are replaced with\tand sent back to the NGINX server.That's it! Navigating back to your website will now authenticate you using the key you just saved.How to build pip packagesudo python3 -m pip install --upgrade pip setuptools wheel
sudo -H pip3 install keyring
git clone https://github.com/jan-vitek/nginxwebauthn
cd nginxwebauthn
python3 setup.py bdist_wheelThen you can distribute the.whlfile fromdistto your server and install it withpip3 install nginxwebauthn_jv-*.whl. Or if you are registered to PyPI.org you can upload the package this way:cat << EOF > ~/.pypirc
[distutils] 
index-servers=pypi
[pypi] 
repository = https://upload.pypi.org/legacy/ 
username = your_pypi_username
EOF

python -m twine upload dist/*LimitationsThis uses the built-in python3 server, which isn't designed for high-volume. You'd want to port this to a uwsgi setup if you wanted to productionize it.Version 0.2New featuresconcurrent registrationsconcurrent authenticationsself registrationinvitationsConfiguration#/etc/nginxwebauthn.conf

[Global]
# after succesful token registration a form is displayed where user can fill in the token name and their personal information
# the token is then stored in /opt/nginxwebauthn/registrations
AllowPreregistration = false

# if FromEmail and AdminEmail are configured the user personal information is sent to the AdminEmail
#FromEmail = root@localhost
#AdminEmail = root@localhost
# SMTP server defaults to localhost
#SMTPServer = localhostSelf registrationsIf enabled users can identify themselves during the registration process. The token data are then stored in/opt/nginxwebauthn/registrationsand user information is sent to the admin email. The admin can later finish the registration process by moving the file to/opt/nginxwebauthn/credentialsInvitationsThe registration form can preload data for the inputs from the url params. The selected inputs can be hidden using thehiddenElementsparam. To later pair the registration data with your user there is a hidden fieldexternalId.The credential file is stored in/opt/nginxwebauthn/registrationswithexternalIdas a prefix.Fields:usernametokenNamenamephoneemailexternalIdExample URL:https://example.com/nginx_fido_auth/register?email=john.doe@example.com&externalId=1234uniqueToken&name=John Doe&phone=555123456&username=john.doe&hiddenElements=["username", "name", "phone", "email"]FAQWhy do I need to run thesave-clientcommand?This seemed easier than setting up a potentially insecure password so that you could authorize your key. Instead it asserts that you have shell access by requiring that you run a command.