A standalone reverse-proxy to enforce Webauthn authentication

WebAuthn Proxy Login Page

A standalone reverse-proxy to enforce Webauthn authentication. It can be inserted in front of sensitive services or even chained with other proxies (e.g. OAuth, MFA) to enable a layered security model.

Webauthn is a passwordless, public key authentication mechanism that allows the use of hardware-based authenticators such as Yubikey, Apple Touch ID or Windows Hello. You can learn more about Webauthn here.

Goals

We specifically built this proxy to fit into our ecosystem and we hope that it might be useful for other teams. Our aim was to make a Webauthn module that was configurable and manageable using standard DevOps tools (in our case Docker and Ansible) and which could be easily inserted into our existing service deployments behind a reverse proxy like NGinx/OpenResty, and chained with other similar security proxies that we use such as OAuth2 Proxy.

Getting Started

First thing you will need to do is build the project. See instructions below for building the Go code directly, or using Docker. It is also available on on Dockerhub if you don't want to build it yourself.

Next, copy the config.yml file from the sample_config directory and modify it to meet your needs. By default the proxy will look for this file in /opt/webauthn_proxy but you can override this by setting the WEBAUTHN_PROXY_CONFIGPATH environment variable to the directory where you've stored the file.

You will also need a credentials.yml file, which is a simple YAML file with key-value pairs of username to credential. The credential is a base64 encoded JSON object which is output during the registration process. You can start with an empty credentials file until you've registered your first user, the path to this file is one of the values in config.yml.

Now you can start the proxy. See instructions below for running it directly, or using Docker. Once it's started you can register a user by going to http://localhost:8080/webauthn/register (assuming you used 8080 as the server port). Enter a username and then click Register. You will be prompted to select an authenticator to register, which is a browser dependent operation (see below). After following the prompts, you will be given a username/credential combination. You should add this entry to the credentials file and restart the proxy.

WebAuthn Proxy Registration

After registration you can go to http://localhost:8080/webauthn/login to log in. Enter the same username you registered and click Login. You will be prompted to provide your authenticator device. Again follow the prompts and you should be successfully authenticated.

At this point you have it running locally. To configure it to work in your environment you will need to configure your webserver or reverse-proxy to make calls to it in order to authenticate. You can use the /webauthn/auth endpoint to check if the caller is currently authenticated, and /webauthn/login (with optional redirect_url and default_username URL parameters) for the the user to login. See instructions below for examples of configuration with NGinx and OpenResty.

Supported Browsers and Authenticators

Firefox and Chrome have been tested and work well, there is some differences in their supported authentication methods. You can some helpful info here and here. Note that you can register multiple different authenticators for a single user, which can be helpful for contingencies such as lost or broken devices.

Other browsers have not been tested but likely will function just fine if they support Webauthn; please feel free to open a pull request to this document with your own testing details.

Building

Golang

go build -o webauthn_proxy && chmod +x webauthn_proxy

Docker

docker build -t webauthn_proxy:latest .

Or to pull from Dockerhub use:

docker pull quiq/webauthn_proxy

Running

Golang

WEBAUTHN_PROXY_CONFIGPATH=${PWD} ./webauthn_proxy

Docker

docker run -p 8080:8080 -it -v /path/to/webauthn_proxy/config.yml:/opt/webauthn_proxy/config.yml -v /path/to/webauthn_proxy/credentials.yml:/opt/webauthn_proxy/credentials.yml webauthn_proxy:latest

Using

You can configure this as an authentication reverse-proxy using the sample configuration for NGinx or Openresty below. Other proxies and webservers haven't been tested currently but they should work and if you have done so please feel free to open a pull request to this document with details.

NGinx

location / {
        auth_request /webauthn/auth;
        error_page 401 = /webauthn/login?redirect_url=$uri;

        # ... 
}

# WebAuthn Proxy.
location /webauthn/ {
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $host;
        proxy_pass http://127.0.0.1:8080;
}
location /webauthn_static/ {
        proxy_pass http://127.0.0.1:8080;
}

OpenResty (example of chaining WebAuthn proxy with OAuth2 Proxy)

location / {
        auth_request /oauth2/auth;

        # Get the email from oauth2 proxy to prepolulate with the redirect below
        auth_request_set $email $upstream_http_x_auth_request_email;
        error_page 401 = /oauth2/start?rd=$uri;  
        access_by_lua_block {
                local http = require "resty.http"
                local h = http.new()
                h:set_timeout(5 * 1000)
                local url = "http://127.0.0.1:8080/webauthn/auth"
                ngx.req.set_header("X-Forwarded-Proto", ngx.var.scheme)
                ngx.req.set_header("Host", ngx.var.host)
                local res, err = h:request_uri(url, {method = "GET", headers = ngx.req.get_headers()})
                if err or not res or res.status ~= 200 then
                        # Redirect to webauthn login, with email as the default username
                        ngx.redirect("/webauthn/login?redirect_url=" .. ngx.var.request_uri .. "&default_username=" .. ngx.var.email)
                        ngx.exit(ngx.HTTP_OK)
                end
        }  

        # ...
}

# OAuth2 Proxy.
location = /oauth2/auth {
        internal;
        proxy_pass http://127.0.0.1:4180;
}
location /oauth2/ {
        proxy_pass http://127.0.0.1:4180;
}

# WebAuthn Proxy.
location /webauthn/ {
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $host;
        proxy_pass http://127.0.0.1:8080;
}
location /webauthn_static/ {
        proxy_pass http://127.0.0.1:8080;
}

Important Configuration Options

All configuration options have a sensible default value and thus can be left off except rpID and rpDisplayName, which you must provide. There are a few important options that you should be aware though:

rpDisplayName: Can be anything you want, a descriptive name of the "relying party", usually organization name.

rpID: Should be set to the domain that your services operate under, for example if you want to secure your CI system and code repositories at https://ci.example.com and https://code.example.com, you should set rpID to simply example.com. This will allow both sites to share the same set of credentials. Note: Credentials created while running the proxy with one rpID are not usable under another.

rpOrigins: If left empty, the proxy will dynamically allow requests to any origin, otherwise it will only allow the configured origins. For example, if you only want this proxy to support https://ci.example.com and https://code.example.com, use the following configuration:

rpOrigins:
  - https://ci.example.com
  - https://code.example.com

Otherwise, if you wanted it to work for any service under example.com, you could simply leave rpOrigins out of your config.

serverAddress: The address the proxy should listen on. Typically this would be 127.0.0.1 if you are running it locally or behind another webserver or proxy, or 0.0.0.0 if you are running in Docker or wanted to expose it directly to the world.

testMode: By setting this value to true, a user will be able to authenticate immediately after they have registered without any intervention from a system administrator, until the proxy is restarted. This is useful for testing, but we highly recommend you set this property to false in production, otherwise users will be able to register themselves and then immediately authenticate.

All Configuration Options

Option Description Default
credentialFile Path and filename for where credentials are stored /opt/webauthn_proxy/credentials.yml
rpDisplayName Display name of relying party
rpID ID of the relying party, usually the domain the proxy and callers live under
rpOrigins Array of full origins used for accessing the proxy, including port if not 80/443, e.g. http://service.example.com:8080. All Origins
serverAddress Address the proxy server should listen on (usually 127.0.0.1 or 0.0.0.0) 127.0.0.1
serverPort Port the proxy server should listen on 8080
sessionSoftTimeoutSeconds Length of time logins are valid for, in seconds 28800 (8 hours)
sessionHardTimeoutSeconds Max length of logged in session, as calls to /webauthn/auth reset the session timeout 86400 (24 hours)
staticPath Path on disk to static assets /static/
testMode When set to true, users can authenticate immediately after registering. Useful for testing, but generally not safe for production. false
usernameRegex Regex for validating usernames ^.*$

Thanks!

Similar Resources

Aegis To KeePass - A simple tool to convert exported (and encrypted) Aegis database to standalone KeePass

ATP - Aegis to KeePass A simple tool to convert exported (and encrypted) Aegis d

Aug 28, 2022

Gotp - Generate TOTP codes from standalone TOTP secret keys

⏲️ GOTP Generate TOTP codes from standalone TOTP secrets. Usage $ gotp TOTPSECRE

Mar 22, 2022

Dbt-postgres-proxy - Proxy server which intercepts and compiles dbt queries on the fly

Dbt-postgres-proxy - Proxy server which intercepts and compiles dbt queries on the fly

dbt-postgres-proxy A reverse proxy for postgres which compiles queries in flight

Mar 4, 2022

Go-Guardian is a golang library that provides a simple, clean, and idiomatic way to create powerful modern API and web authentication.

❗ Cache package has been moved to libcache repository Go-Guardian Go-Guardian is a golang library that provides a simple, clean, and idiomatic way to

Dec 23, 2022

Go login handlers for authentication providers (OAuth1, OAuth2)

Go login handlers for authentication providers (OAuth1, OAuth2)

gologin Package gologin provides chainable login http.Handler's for Google, Github, Twitter, Facebook, Bitbucket, Tumblr, or any OAuth1 or OAuth2 auth

Dec 30, 2022

Package goth provides a simple, clean, and idiomatic way to write authentication packages for Go web applications.

Goth: Multi-Provider Authentication for Go Package goth provides a simple, clean, and idiomatic way to write authentication packages for Go web applic

Dec 29, 2022

HTTP Authentication middlewares

goji/httpauth httpauth currently provides HTTP Basic Authentication middleware for Go. It is compatible with Go's own net/http, goji, Gin & anything t

Dec 23, 2022

[DEPRECATED] Go package authcookie implements creation and verification of signed authentication cookies.

Package authcookie import "github.com/dchest/authcookie" Package authcookie implements creation and verification of signed authentication cookies. Co

Dec 22, 2022

Basic and Digest HTTP Authentication for golang http

HTTP Authentication implementation in Go This is an implementation of HTTP Basic and HTTP Digest authentication in Go language. It is designed as a si

Dec 22, 2022
Comments
  • Fix potential vulnerability in use of the rand.Seed

    Fix potential vulnerability in use of the rand.Seed

    According to webauthn spec. the challange needs to be hard to guess otherwise the protocol is vulnerable to reply attack see: https://www.w3.org/TR/webauthn-2/#sctn-cryptographic-challenges

    In order to prevent replay attacks, the challenges MUST contain enough entropy to make guessing them infeasible. Challenges SHOULD therefore be at least 16 bytes long.

    Initating with the random seed with time makes it easy to guess after a restart. The space of seeds can be quickly enumerated, because time has lager resultion than nanosecods. See this more details: https://github.com/Quiq/webauthn_proxy

  • Demo proxy not working

    Demo proxy not working

    Hello,

    I was trying out your example proxy implementation to learn more about WebAuthn in general and running the Docker version of it in an Ubuntu Linux VM. When I try to browse to the stated registration URL (lhttp://localhost/webauthn/register) the service does nothing and the browser just hangs.

    Based on your documentation it seems I am using it as instructed. Is there anything else I need to configure that would prevent this from working?

    Thanks for any assistance, Brent

  • Add support for verifying attestation type and trust anchors

    Add support for verifying attestation type and trust anchors

    https://www.w3.org/TR/webauthn/#sctn-attestation-types

    https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-metadata-service-v2.0-id-20180227.html

A reverse proxy that provides authentication with Google, Github or other providers.
A reverse proxy that provides authentication with Google, Github or other providers.

A reverse proxy and static file server that provides authentication using Providers (Google, GitHub, and others) to validate accounts by email, domain

Jan 1, 2023
an stateless OpenID Connect authorization server that mints ID Tokens from Webauthn challenges

Webauthn-oidc Webauthn-oidc is a very minimal OIDC authorization server that only supports webauthn for authentication. This can be used to bootstrap

Nov 6, 2022
Authelia: an open-source authentication and authorization server providing two-factor authentication
Authelia: an open-source authentication and authorization server providing two-factor authentication

Authelia is an open-source authentication and authorization server providing two

Jan 5, 2022
Authentication Plugin for implementing Form-Based, Basic, Local, LDAP, OpenID Connect, OAuth 2.0, SAML Authentication
Authentication Plugin for implementing Form-Based, Basic, Local, LDAP, OpenID Connect, OAuth 2.0, SAML Authentication

Authentication Plugin for implementing Form-Based, Basic, Local, LDAP, OpenID Connect, OAuth 2.0, SAML Authentication

Jan 8, 2023
A simple passwordless authentication middleware that uses only email as the authentication provider
A simple passwordless authentication middleware that uses only email as the authentication provider

email auth A simple passwordless authentication middleware that uses only email as the authentication provider. Motivation I wanted to restrict access

Jul 27, 2022
Authorization and authentication. Learning go by writing a simple authentication and authorization service.

Authorization and authentication. Learning go by writing a simple authentication and authorization service.

Aug 5, 2022
A very simple HTTP reverse proxy that checks that requests contain a valid secret as a bearer token

bearproxy -- Authorization enforcing HTTP reverse proxy Bearproxy is a very simple HTTP reverse proxy that checks that requests contain a valid secret

Nov 11, 2021
An authentication proxy for Google Cloud managed databases
An authentication proxy for Google Cloud managed databases

db-auth-gateway An authentication proxy for Google Cloud managed databases. Based on the ideas of cloudsql-proxy but intended to be run as a standalon

Dec 5, 2022
A simple passwordless proxy authentication middleware using email.
A simple passwordless proxy authentication middleware using email.

email proxy auth A simple passwordless proxy authentication middleware that uses only email as the authentication provider. Motivation I wanted to res

Jul 27, 2022
A standalone, specification-compliant, OAuth2 server written in Golang.
A standalone, specification-compliant,  OAuth2 server written in Golang.

Go OAuth2 Server This service implements OAuth 2.0 specification. Excerpts from the specification are included in this README file to describe differe

Dec 28, 2022