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

Overview

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!

Comments
  • 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]

    opened by james-d-elliott 15
  • 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"}),
    	)
    
    opened by james-d-elliott 10
  • 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?

    opened by gnur 8
  • 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.

    bug 
    opened by aseigler 8
  • 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)

    opened by livio-a 7
  • 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.

    opened by Juneezee 6
  • 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.

    opened by trusch 6
  • How does navigator.credentials.create interact with the authenticator?

    How does navigator.credentials.create interact with the authenticator?

    The WebAuthn spec says

    Clients can communicate with authenticators using a variety of mechanisms. For example, a client MAY use a client device-specific API to communicate with an authenticator which is physically bound to a client device. On the other hand, a client can use a variety of standardized cross-platform transport protocols such as Bluetooth (see §5.10.4 Authenticator Transport Enumeration (enum AuthenticatorTransport)) to discover and communicate with cross-platform attached authenticators.

    I don't understand exactly how this works though. My understanding is, the Relying Party's JavaScript executes navigator.credentials.create in the user agent. How does the browser know what authenticator to use (whether it's bluetooth, nfc, etc.)? What if there are multiple bluetooth devices available? Does the browser itself perform the connection over a web api?

    Does the authenticator need to be "installed" or registered in some way into the web browser via an add-on? If I wanted to just use another operating system process as the authenticator, as I think in this example, what would be the communication method between the browser's JS and the external process?

    opened by holycleugh 6
  • signature verification for basic attestation broken

    signature verification for basic attestation broken

    handleBasicAttestation attempts to convert a COSE algorithm identifier (alg in the code below) to x509.SignatureAlgorithm (unless the device seems to be a YubiKey).

    These values obviously don't align, so all verifications fail with

    x509: cannot verify signature: algorithm unimplemented

    https://github.com/duo-labs/webauthn/blob/1500f40bdab7800964d135283af03fab3a3c46c0/protocol/attestation_packed.go#L92-L96

    This code should map the COSE ID to one of the x509 constants instead.

    grep shows similar code in attestation_tpm.go and attestation_androidkey.go.

    opened by pschultz 5
  • upgrade cfssl and cbor deps

    upgrade cfssl and cbor deps

    The upgrade to cfssl allows webauthn to be included in modules that themselves already depend on recent grpc (which removed the naming pkg), because new cfssl depends on new certificate-transparency-go which depends on new etcd which now works with the latest grpc.

    module google.golang.org/[email protected] found (v1.44.0), but does not contain package google.golang.org/grpc/naming
    

    The cbor update seems like a good idea; it's had various bugs fixed, appears to have more fuzz testing done against it too now.

    I also fixed a nil panic on the TestMetadataTOCParsing test, which was already failing but the panic obscured why. The failures are all of the form unmarshalMDSTOC() got Get "https://fidoalliance.co.nz/mds/pki/MDSROOT.crt": dial tcp: lookup fidoalliance.co.nz: No address associated with hostname, wanted <nil>.

    opened by gburt 4
  • Project maintained?

    Project maintained?

    I see this project has several good PRs waiting to be merged, including security updates.

    Is this project still maintained? If not, it might be better to mark it as read-only, so people know they must move on. Looking at the project "Network" graph, I see a fork from authelia that has several of these PRs merged.

    opened by jgimenez 4
  • Bazel build fails after webauthn went to Go 1.18

    Bazel build fails after webauthn went to Go 1.18

    We use duo-labs/webauthn in our product. Our Bazel build fails with the following error since commit 41e0a0431b9cfc20067e537f6b3146a624745030 on Aug 13, 2022. I checked and seems the Go version has been upgraded to 1.18 plus some other dependency changes. Not sure if it is compatibility issue.

    INFO: Invocation ID: 1869f449-7320-4f0e-b72b-ae43c45d1423 INFO: Analyzed target //myprodcut:myprodcut (2 packages loaded, 10 targets configured). INFO: Found 1 target... ERROR: /private/var/tmp/_bazel_my.user/cbac05759584d101e53f170500f16b04/external/com_github_duo_labs_webauthn/protocol/webauthncose/BUILD.bazel:3:11: GoCompilePkg external/com_github_duo_labs_webauthn/protocol/webauthncose/webauthncose.a failed: (Exit 1): sandbox-exec failed: error executing command (cd /private/var/tmp/_bazel_my.user/cbac05759584d101e53f170500f16b04/sandbox/darwin-sandbox/2892/execroot/mydir && \ exec env - \ CGO_ENABLED=1 \ GOARCH=amd64 \ GOOS=darwin \ GOPATH='' \ GOROOT=external/go_sdk \ GOROOT_FINAL=GOROOT \ PATH=/usr/bin:external/llvm_toolchain/bin:/bin \ TMPDIR=/var/folders/jy/s9spzp8d529b2h_56n0hdjm00000gn/T/ \ /usr/bin/sandbox-exec -f /private/var/tmp/_bazel_my.user/cbac05759584d101e53f170500f16b04/sandbox/darwin-sandbox/2892/sandbox.sb /var/tmp/_bazel_my.user/install/35477ebf9ca1f9d07e9e2a657bdd526f/process-wrapper '--timeout=0' '--kill_delay=15' bazel-out/darwin-opt-exec-2B5CBBC6/bin/external/go_sdk/builder compilepkg -sdk external/go_sdk -installsuffix darwin_amd64 -src external/com_github_duo_labs_webauthn/protocol/webauthncose/ed25519.go -src external/com_github_duo_labs_webauthn/protocol/webauthncose/ed25519_go112.go -src external/com_github_duo_labs_webauthn/protocol/webauthncose/webauthncose.go -embedroot '' -embedroot bazel-out/darwin-fastbuild/bin -embedlookupdir external/com_github_duo_labs_webauthn/protocol/webauthncose -arc 'github.com/duo-labs/webauthn/protocol/googletpm=github.com/duo-labs/webauthn/protocol/googletpm=bazel-out/darwin-fastbuild/bin/external/com_github_duo_labs_webauthn/protocol/googletpm/googletpm.x' -arc 'github.com/duo-labs/webauthn/protocol/webauthncbor=github.com/duo-labs/webauthn/protocol/webauthncbor=bazel-out/darwin-fastbuild/bin/external/com_github_duo_labs_webauthn/protocol/webauthncbor/webauthncbor.x' -arc 'golang.org/x/crypto/ed25519=golang.org/x/crypto/ed25519=bazel-out/darwin-fastbuild/bin/external/org_golang_x_crypto/ed25519/ed25519.x' -importpath github.com/duo-labs/webauthn/protocol/webauthncose -p github.com/duo-labs/webauthn/protocol/webauthncose -package_list bazel-out/darwin-opt-exec-2B5CBBC6/bin/external/go_sdk/packages.txt -o bazel-out/darwin-fastbuild/bin/external/com_github_duo_labs_webauthn/protocol/webauthncose/webauthncose.a -x bazel-out/darwin-fastbuild/bin/external/com_github_duo_labs_webauthn/protocol/webauthncose/webauthncose.x -nogo bazel-out/darwin-opt-exec-2B5CBBC6-ST-eda1e837b650/bin/bazel/go/static_analysis_actual_/static_analysis_actual -gcflags '' -asmflags '') compilepkg: //go:build comment without // +build comment Target //myprodcut:myprodcut failed to build INFO: Elapsed time: 0.706s, Critical Path: 0.18s INFO: 2 processes: 2 internal. INFO: Build Event Protocol files produced successfully. FAILED: Build did NOT complete successfully

    Not sure what has changed on /webauthn/protocol/webauthncose either. I also checked the Bazel file in the tmp dir for webauthncose and nothing unusual is there. No more error message on the output.

    opened by amirhood 1
  • Inquiry regarding the use of test data.

    Inquiry regarding the use of test data.

    Hello, I would like to politely ask if your test data can be used for some of our test cases (https://github.com/line/line-fido2-server). I already know it's a BSD-3 license, but I still wanted to ask.

    https://github.com/duo-labs/webauthn/blob/00c9fb5711f57e43becab7ef8dd6551180ef883a/protocol/attestation_tpm_test.go#L33-L34

    opened by kj84park 2
  • README needs to be updated

    README needs to be updated

    The README says this project implements the "Candidate Recommendation" of WebAuthn, and is giving some potential consumers of this repo pause that the code is not ready for production. I'm pretty sure this library is at least compliant with L2, with some L3 features being introduced lately as well.

    Can someone take a stab at revamping the README to signal more confidently that this is acceptable for use for more than hobby projects? Unless it's not in which case we should be explicit with what kinds of projects this is suitable and unsuitable for.

    opened by MasterKale 2
  • replace CFSSL with stripped down version

    replace CFSSL with stripped down version

    As mentioned in #76 I created a fork of CFSSL that has just enough functionality for this project. So the incredibly large amount of dependencies of CFSSL is avoided (to be precise, only the dependency golang.org/x/crypto remains.).

    This pull reqest basically changes the import to my fork and I cleaned up go.mod afterwards with go mod tidy.

    opened by Isolus 0
  • TPM attestation improvements

    TPM attestation improvements

    • Switch to using go-tpm library for TPM certInfo/pubArea things
    • Add TPM tests for certInfo/pubArea
    • Disable metadata tests which are currently broken due to missing fidoalliance.co.nz domain
    opened by aseigler 0
Owner
Duo Labs
Duo Labs is the security research team at Duo Security.
Duo Labs
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

Miroslav Šedivý 5 Jul 27, 2022
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

Miroslav Šedivý 5 Jul 27, 2022
Authelia: an open-source authentication and authorization server providing two-factor authentication

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

Streato 0 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

Paul Greenberg 553 Sep 21, 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.

Dinesh Bhattarai 0 Aug 5, 2022
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,

GitHub CLI 344 Sep 9, 2022
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

Authelia 14.2k Sep 26, 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

Sanad Haj Yahya 399 Sep 22, 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

Mark Bates 3.8k Sep 20, 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.

Keratin 1.1k Sep 21, 2022
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

Shayan 2 Feb 6, 2022
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

Vouch 2k Sep 19, 2022
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

BuzzFeed 3k Sep 28, 2022
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

yinhuanyi 1 Nov 17, 2021
: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

Alexander F. Rødseth 464 Sep 9, 2022
Open source RBAC library. Associate users with roles and permissions.

ℹ️ This package is completely open source and works independently from Permify. Associate users with roles and permissions This package allows you to

Permify 114 Sep 8, 2022
A demo using go and redis to implement a token manager

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

Yuki Chen 0 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

null 1 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.

Henrique Dias 18 Sep 4, 2022