Handle Web Authentication for Go apps that wish to implement a passwordless solution for users

WebAuthn Library

GoDoc Build Status Go Report Card

This library is meant to handle Web Authentication for Go apps that wish to implement a passwordless solution for users. While the specification is currently in Candidate Recommendation, this library conforms as much as possible to the guidelines and implementation procedures outlined by the document.

Demo at webauthn.io

An implementation of this library can be used at webauthn.io and the code for this website can be found in the Duo Labs repository webauthn-io.

Simplified demo

A simplified demonstration of this library can be found here. It includes a minimal interface and is great for quickly testing out the code. The associated blog post can be found here.

Quickstart

go get github.com/duo-labs/webauthn and initialize it in your application with basic configuration values.

Make sure your user model is able to handle the interface functions laid out in webauthn/user.go. This means also supporting the storage and retrieval of the credential and authenticator structs in webauthn/credential.go and webauthn/authenticator.go, respectively.

Initialize the request handler

import "github.com/duo-labs/webauthn/webauthn"

var (
    web *webauthn.WebAuthn
    err error
)

// Your initialization function
func main() {
    web, err = webauthn.New(&webauthn.Config{
        RPDisplayName: "Duo Labs", // Display Name for your site
        RPID: "duo.com", // Generally the FQDN for your site
        RPOrigin: "https://login.duo.com", // The origin URL for WebAuthn requests
        RPIcon: "https://duo.com/logo.png", // Optional icon URL for your site
    })
    if err != nil {
        fmt.Println(err)
    }
}

Registering an account

func BeginRegistration(w http.ResponseWriter, r *http.Request) {
    user := datastore.GetUser() // Find or create the new user  
    options, sessionData, err := web.BeginRegistration(&user)
    // handle errors if present
    // store the sessionData values 
    JSONResponse(w, options, http.StatusOK) // return the options generated
    // options.publicKey contain our registration options
}

func FinishRegistration(w http.ResponseWriter, r *http.Request) {
    user := datastore.GetUser() // Get the user  
    // Get the session data stored from the function above
    // using gorilla/sessions it could look like this
    sessionData := store.Get(r, "registration-session")
    parsedResponse, err := protocol.ParseCredentialCreationResponseBody(r.Body)
    credential, err := web.CreateCredential(&user, sessionData, parsedResponse)
    // Handle validation or input errors
    // If creation was successful, store the credential object
    JSONResponse(w, "Registration Success", http.StatusOK) // Handle next steps
}

Logging into an account

func BeginLogin(w http.ResponseWriter, r *http.Request) {
    user := datastore.GetUser() // Find the user
    options, sessionData, err := webauthn.BeginLogin(&user)
    // handle errors if present
    // store the sessionData values
    JSONResponse(w, options, http.StatusOK) // return the options generated
    // options.publicKey contain our registration options
}

func FinishLogin(w http.ResponseWriter, r *http.Request) {
    user := datastore.GetUser() // Get the user 
    // Get the session data stored from the function above
    // using gorilla/sessions it could look like this
    sessionData := store.Get(r, "login-session")
    parsedResponse, err := protocol.ParseCredentialRequestResponseBody(r.Body)
    credential, err := webauthn.ValidateLogin(&user, sessionData, parsedResponse)
    // Handle validation or input errors
    // If login was successful, handle next steps
    JSONResponse(w, "Login Success", http.StatusOK)
}

Modifying Credential Options

You can modify the default credential creation options for registration and login by providing optional structs to the BeginRegistration and BeginLogin functions.

Registration modifiers

You can modify the registration options in the following ways:

// Wherever you handle your WebAuthn requests
import "github.com/duo-labs/webauthn/protocol"
import "github.com/duo-labs/webauthn/webauthn"

var webAuthnHandler webauthn.WebAuthn // init this in your init function

func beginRegistration() {
    // Updating the AuthenticatorSelection options. 
    // See the struct declarations for values
    authSelect := protocol.AuthenticatorSelection{        
		AuthenticatorAttachment: protocol.AuthenticatorAttachment("platform"),
		RequireResidentKey: protocol.ResidentKeyUnrequired(),
        UserVerification: protocol.VerificationRequired
    }

    // Updating the ConveyencePreference options. 
    // See the struct declarations for values
    conveyencePref := protocol.ConveyancePreference(protocol.PreferNoAttestation)

    user := datastore.GetUser() // Get the user  
    opts, sessionData, err webAuthnHandler.BeginRegistration(&user, webauthn.WithAuthenticatorSelection(authSelect), webauthn.WithConveyancePreference(conveyancePref))

    // Handle next steps
}

Login modifiers

You can modify the login options to allow only certain credentials:

// Wherever you handle your WebAuthn requests
import "github.com/duo-labs/webauthn/protocol"
import "github.com/duo-labs/webauthn/webauthn"

var webAuthnHandler webauthn.WebAuthn // init this in your init function

func beginLogin() {
    // Updating the AuthenticatorSelection options. 
    // See the struct declarations for values
    allowList := make([]protocol.CredentialDescriptor, 1)
    allowList[0] = protocol.CredentialDescriptor{
        CredentialID: credentialToAllowID,
        Type: protocol.CredentialType("public-key"),
    }

    user := datastore.GetUser() // Get the user  

    opts, sessionData, err := webAuthnHandler.BeginLogin(&user, webauthn.wat.WithAllowedCredentials(allowList))

    // Handle next steps
}

Acknowledgements

I could not have made this library without the work of Jordan Wright and the designs done for our demo site by Emily Rosen. When I began refactoring this library in December 2018, Koen Vlaswinkel's Golang WebAuthn library really helped set me in the right direction. A huge thanks to Alex Seigler for his continuing work on this WebAuthn library and many others. Thanks to everyone who submitted issues and pull requests to help make this library what it is today!

Owner
Duo Labs
Duo Labs is the security research team at Duo Security.
Duo Labs
Comments
  • Project Future

    Project Future

    With the webauthn.io demo site no longer being backed by this library, it may be time to move the project to another organization (perhaps https://github.com/go-webauthn) and establish a new demo site. Thoughts?

  • feat: appid extension in assertion challenge response

    feat: appid extension in assertion challenge response

    This change allows FinishLogin to process appid requests if the appid key in the extensions map has a value. The extensions map is expected as a root member of the JSON object sent to the endpoint.

    Closes #106

    Co-authored-by: Clément Michaud [email protected]

  • appid extension: u2f registered keys compat

    appid extension: u2f registered keys compat

    So I'm experimenting with this library. Currently the issue is with the appid extension enabled it's claiming the device registered via u2f is not familiar. Newly registered keys work with my config, but the old FIDO ones do not. This issue occurs when navigator.credentials.get is called in either Chrome or Firefox. Code snips:

    Creating the handler:

    	if webauthnHandler, err = webauthn.New(&webauthn.Config{
    		RPID:                  "webauthn.example.com",
    		RPDisplayName:         "Webauthn",
    		RPOrigin:              "https://webauthn.example.com",
    		AttestationPreference: protocol.PreferIndirectAttestation,
    	}); err != nil {
    		panic(err)
    	}
    

    Credential added the users credentials:

    	keyID, _ := base64.StdEncoding.DecodeString(encodedKeyHandleIDHere)
    	pubkey, _ := base64.StdEncoding.DecodeString(encodedPubKeyHere)
    
    	credential := webauthn.Credential{
    		ID:              keyID,
    		PublicKey:       pubkey,
    		AttestationType: "fido-u2f", // Also tried with this commented.
    		Authenticator: webauthn.Authenticator{
    			SignCount: 0,
    			AAGUID:    []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    		},
    	}
    

    Creating the assertion:

    	assertion, data, err := webauthnHandler.BeginLogin(user,
    		webauthn.WithUserVerification(body.UserVerification),
    		webauthn.WithAssertionExtensions(map[string]interface{}{"appid": "https://webauthn.example.com"}),
    	)
    
  • Revoke dependency leads to broken import of google.golang.org/grpc/naming

    Revoke dependency leads to broken import of google.golang.org/grpc/naming

    A long chain of dependencies is introduced via revoke's CT support. This currently leads to a broken module dependency for GRPC:

            github.com/duo-labs/webauthn/protocol imports
            github.com/duo-labs/webauthn/metadata imports
            github.com/cloudflare/cfssl/revoke imports
            github.com/cloudflare/cfssl/helpers imports
            github.com/google/certificate-transparency-go imports
            go.etcd.io/etcd imports
            github.com/coreos/etcd/etcdmain imports
            github.com/coreos/etcd/proxy/grpcproxy imports
            google.golang.org/grpc/naming: module google.golang.org/grpc@latest found (v1.30.0), but does not contain package google.golang.org/grpc/naming
    

    Edit: It looks like revoke's validation is needed here. Any suggestions for fixing this dependency issue?

  • inconsistency between devices / hostnames

    inconsistency between devices / hostnames

    I'm using this library to add webauthn to a pet project. On my test domain, it was working just fine. I can register both my macbook pro (2017) and my phone (galaxy s9+), when I deploy it to my "production" environment, I can register my macbook, but I cannot register my phone.

    When I try to register my phone I get the following error:

    Error finding cert issued to correct hostname: x509: certificate signed by unknown authority.

    Test domain is similar to: tst.tmp.example.nl, production domain is similar to: prod.example.xyz.

    Is there any reason why a different end point would not be allowed?

  • Update webauthn.js

    Update webauthn.js

    https://github.com/sweetalert2/sweetalert2/wiki/Migration-from-SweetAlert-to-SweetAlert2

    Seems that this library had a major breaking change which has broken WebAuthn.io demo site. I believe this will fix it, worked for me.

  • "Critical" severity shows up from Dependency Scan

    When running Dependency Scan we see Critical severities related to CVE-2020-26892.

    Turns out this stems from the "github.com/cloudflare/cfssl" library. There seems to be only a single use of the library via github.com/cloudflare/cfssl/revoke (in the github.com/duo-labs/webauthn/metadata package) and that API does not use the function that raises the CVE.

    Given that its the lone use, implementing that fairly small code (in relation to the library) directly into webauthn and removing "github.com/cloudflare/cfssl" would get rid of the Critical severity (and some "High" severities as well) seems like a good option (the other option would be to fix the library which might be more involved).

    Is that a PR you'd consider ?

  • exchange jwt library

    exchange jwt library

    dgrijalva/jwt-go is no longer maintained: https://github.com/dgrijalva/jwt-go/issues/462 this PR exchanges dgrijalva/jwt-go (3.2.0) with github.com/golang-jwt/jwt (3.2.2)

  • build: update to Go 1.18

    build: update to Go 1.18

    This PR introduces two changes:

    1. Updates the go directive in go.mod file by running go mod tidy -go=1.17 to enable module graph pruning and lazy module loading.
    2. Update GitHub Actions go.yml to Go 1.17

    Note 1: This PR does not prevent users with earlier Go versions from successfully building packages from this module.

    Note 2: The additional require directive is used to record indirect dependencies for Go 1.17 or higher, see https://go.dev/ref/mod#go-mod-file-go.

  • AAGUID is always zero'd out during registration and authentication

    AAGUID is always zero'd out during registration and authentication

    I am using a YubiKey 5c with the webauth.io demo app, I am requesting direct attestation, however the AAGUID field is always zero'd out. The expected value is ee882879-721c-4913-9775-3dfcce97072a per https://support.yubico.com/hc/en-us/articles/360016648959-YubiKey-Hardware-FIDO2-AAGUIDs

    I'm not sure if this is a bug or if I am going something incorrectly.

  • please provide minimal working example

    please provide minimal working example

    I like to use webauthn.io as reference implementation, but its quiet specific in some cases, especially the frontend is not really minimal (using jquery etc.)

    It would be nice to have a minimal working example to compare the own implementation against.

    Due to the fact that the standard is not very old the error handling in chrome is not very nice, and I only get a DomException whenever I try to navigator.credentials.create(). With a minimal example I could at least compare my options and the the options from the reference implementation.

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
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
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
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 library for performing OAuth Device flow and Web application flow in Go client apps.
A library for performing OAuth Device flow and Web application flow in Go client apps.

oauth A library for Go client applications that need to perform OAuth authorization against a server, typically GitHub.com. Traditionally,

Dec 30, 2022
The Single Sign-On Multi-Factor portal for web apps
The Single Sign-On Multi-Factor portal for web apps

Authelia is an open-source authentication and authorization server providing two-factor authentication and single sign-on (SSO) for your applications

Jan 8, 2023
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
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
Authentication service that keeps you in control without forcing you to be an expert in web security.
Authentication service that keeps you in control without forcing you to be an expert in web security.

Authentication service that keeps you in control without forcing you to be an expert in web security.

Jan 1, 2023
A simple authentication web application in Golang (using jwt)

Simple Authentication WebApp A simple authentication web app in Go (using JWT) Routes Path Method Data /api/v1/auth/register POST {"firstname":,"lastn

Feb 6, 2022
an SSO and OAuth / OIDC login solution for Nginx using the auth_request module
an SSO and OAuth / OIDC login solution for Nginx using the auth_request module

Vouch Proxy An SSO solution for Nginx using the auth_request module. Vouch Proxy can protect all of your websites at once. Vouch Proxy supports many O

Jan 4, 2023
sso, aka S.S.Octopus, aka octoboi, is a single sign-on solution for securing internal services
sso, aka S.S.Octopus, aka octoboi, is a single sign-on solution for securing internal services

sso See our launch blog post for more information! Please take the SSO Community Survey to let us know how we're doing, and to help us plan our roadma

Jan 5, 2023
A single sign-on solution based on go-oauth2 / oauth2 and gin-gonic/gin

A single sign-on solution based on go-oauth2 / oauth2 and gin-gonic/gin

Nov 17, 2021
A demo using go and redis to implement a token manager

使用go-redis实现一个令牌管理器 需求描述 假设我们当前的所有服务需要一个第三方的认证,认证形式为:在发送请求的时候带上第三方颁发的令牌,该令牌具有一个时效性 第三方的令牌可以通过某个接口获取,但是该接口做了单位时间内的同一ip的请求频率的限制,因此在并发的场景下,我们需要控制令牌获取接口的频

Oct 19, 2021
It is a JWT based implement of identity server.

JWTAuth 安裝說明 基本需求 安裝 docker 服務 安裝 OpenSSL 安裝指令 建立 OS 系統的 jwtauth 帳號 sudo useradd -m jwtauth 給予 JWTAuth 帳號可以操作 docker 的權限 sudo usermod -aG docker jwtau

Aug 10, 2022
This repository contains a set of tools to help you implement IndieAuth, both server and client, in Go.

This repository contains a set of tools to help you implement IndieAuth, both server and client, in Go.

Nov 26, 2022
OauthMicroservice-cassandraCluster - Implement microservice of oauth using golang and cassandra to store user tokens

implement microservice of oauth using golang and cassandra to store user tokens

Jan 24, 2022
:closed_lock_with_key: Middleware for keeping track of users, login states and permissions

Permissions2 Middleware for keeping track of users, login states and permissions. Online API Documentation godoc.org Features and limitations Uses sec

Dec 31, 2022