A standalone reverse-proxy for passwordless Webauthn authentication. Supports hardware authenticators like Yubikey, Touch ID etc.
| Login | Registration |
|---|---|
![]() |
![]() |
# Run the proxy
docker run --rm -ti -p 8080:8080 quiq/webauthn_proxy:latest
# With custom config
docker run --rm -ti -p 8080:8080 -v /path/to/config:/opt/config:ro quiq/webauthn_proxy:latest
# Generate cookie secret for credentials.yml
docker run --rm --log-driver=none quiq/webauthn_proxy:latest -generate-secret# Run directly
go run .
# Build
go build -o webauthn_proxy . && chmod +x webauthn_proxy
./webauthn_proxy -v-
Configuration: Create
config/config.ymlwith your settings (see Configuration).rpOriginsis required, the proxy will not start without it. -
Credentials: Create your credentials file from the example:
cp config/credentials.yml.example config/credentials.yml
config/credentials.ymlis git-ignored, as it holds your cookie secrets and user credentials. -
Register: Visit
http://localhost:8080/webauthn/register -
Add User: Copy the generated credential to
credentials.ymland restart -
Login: Visit
http://localhost:8080/webauthn/login
rpDisplayName: "MyCompany" # Your organization name
rpID: "example.com" # Your domain
rpOrigins: # Allow-list of origins used to reach the proxy
- "https://service.example.com"rpOrigins is a strict allow-list and has no default. Requests whose origin is not listed
are rejected, so the Host header cannot be used to spoof an origin. The proxy refuses to
start if the list is empty.
serverAddress: Listen address (default:0.0.0.0)serverPort: Listen port (default:8080)testMode: Allow immediate login after registration (default:false)cookieSecure: Set the Secure flag on cookies (default:true, disable only for local plain HTTP testing)sessionSoftTimeoutSeconds: Session timeout (default: 28800 / 8 hours)
location / {
auth_request /webauthn/auth;
error_page 401 = /webauthn/login?redirect_url=$uri;
# ...
}
location /webauthn/ {
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_pass http://127.0.0.1:8080;
}location / {
auth_request /oauth2/auth;
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
ngx.redirect("/webauthn/login?redirect_url=" .. ngx.var.request_uri .. "&default_username=" .. ngx.var.email)
ngx.exit(ngx.HTTP_OK)
end
}
# ...
}
location /webauthn/ {
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:8080;
}
