Getting Started
With WebAuthn
An Overview and Example
Nick Steele
@codekaiju
$whoami
● Principal R&D Engineer
○ Duo Labs (part of Cisco)
● W3C Invited Expert
● Sometimes outdoorsy
“Good security is not about what
you can prevent, but what you
can enable”
WebAuthn is a unicorn; it decreases
user friction and increases security.
Workshop Overview
● Setting up the code (5 Minutes)
● Introduction (10 Minutes)
● Credential Requests and Responses (15 - 20 Minutes)
● Unpacking Credential Responses (10 Minutes)
● Basic Validation Steps (15 Minutes)
● Ongoing Development (10 Minutes)
○ Resident Keys (Passwordless and… Username-less?)
○ Account Recovery
● Q&A (Remaining Time)
Key Takeaways
● High level understanding of the WebAuthn specification and terminology
● How WebAuthn registration and login requests are structured and why
● How to parse and validate Credential Responses
● What attestation is and when to handle it
● How WebAuthn and FIDO2 Authentication make a more secure web
Code Setup
We’re using webauthn.io
( Disclosure: I made it! With the help of many people! )
Software Requirements
Recommended Browsers
2 Libraries
Core Library
$ git clone https://github.com/duo-labs/webauthn.git
If you want to use Go
$ go get github.com/duo-labs/webauthn.io
$ cd $GOPATH/src/github.com/duo-labs/webauthn.io
$ go build -i; ./webauthn.io
If you want to use Docker
$ git clone https://github.com/duo-labs/webauthn.io.git
$ cd webauthn.io
$ docker build -t webauthn.io .
$ docker run --rm -p 9005:9005 webauthn.io
If you just want to follow along
$docker run --rm -p 9005:9005 duolabs/webauthn.io
http://localhost:9005
## Docker
$git clone https://github.com/duo-labs/webauthn.io.git
$cd webauthn.io
$docker build -t webauthn.io .
$docker run --rm -p 9005:9005 webauthn.io
## Golang
$go get github.com/duo-labs/webauthn.io
$cd $GOPATH/src/github.com/duo-labs/webauthn.io
$go build -i; ./webauthn.io
## Just the web app
$ docker run --rm -p 9005:9005 duolabs/webauthn.io
If you’re busy installing the requirements still...
Just go to webauthn.io
Introduction
CONFIDENTIAL INFORMATION PROPERTY OF DUO SECURITY, INC.
“In the beginning the Password was created.
This has made a lot of people very angry and
been widely regarded as a bad move.”
- Douglas Adams, kinda
CONFIDENTIAL INFORMATION PROPERTY OF DUO SECURITY, INC.
81% of breaches leveraged
either stolen and/or weak
passwords.
Source: 2017 Verizon Data Breach Investigations Report
CONFIDENTIAL INFORMATION PROPERTY OF DUO SECURITY, INC.
CONFIDENTIAL INFORMATION PROPERTY OF DUO SECURITY, INC.
CONFIDENTIAL INFORMATION PROPERTY OF DUO SECURITY, INC.
“Kind of a nightmare...”
- The guy who invented it RIP
CONFIDENTIAL INFORMATION PROPERTY OF DUO SECURITY, INC.
But we keep using them.
In fact, let’s use them
more.
CONFIDENTIAL INFORMATION PROPERTY OF DUO SECURITY, INC.
“The average… user has over
107 accounts registered to one
email address… In 2020, the
average number of accounts per
internet user will be 207”
- Dashlane, 2015
Guess the best solution is to buy a
password manager, right?
Definitely not get rid of passwords...
WebAuthn
WebAuthn is a JavaScript API. It defines
two methods, create and get, that can
be used to register and assert ownership of
credential key pairs for a given website.
● Developed by W3C and The FIDO Alliance
○ Includes companies like Google, Microsoft, Mozilla, Paypal, IBM, Yubico, and
Duo!
● Became a publicly recommended standard in March 2019
● Developed in part as a improvement upon the U2F standard
○ But includes many additional features and abilities
Introduction: WebAuthn
● It allows for the creation of strong, attested, and scoped
credentials.
● The credentials are public and private key pairs
● Created with cryptographically strong and sound math.
● The authenticator can attest that it created a credential
● The credential is scoped for a single website
Introduction: WebAuthn
Introduction: Terms
Authenticator Client Relying Party
WebAuthnCTAP2
Introduction: Terms
Authenticator Client Relying Party
Introduction: Terms
CTAP1👏 IS 👏 U2F
WebAuthnCTAP2
FIDO2 Authentication
Introduction: Terms
Registration
ex.com
“Create an account for ex.com”
Registration
ex.com
“Here is some data and a list of
parameters the credential should
have”
Registration
“Given these options, try making a Credential”
Registration
PublicKeyCredential
Registration
ex.com
JSON(PublicKeyCredential)
Registration
ex.com
“The data I sent you is signed correctly and you
followed my options!”
Registration
ex.com
+ ID
Login (Assertion)
ex.com
“Log me in to ex.com”
Login (Assertion)
ex.com
ID
“Assert that you own the private key for this
credential with the given ID by signing this data”
Login (Assertion)
“Given these options and ID, prove you own it”
Login (Assertion)
PublicKeyCredential
Login (Assertion)
ex.com
JSON(PublicKeyCredential)
Login (Assertion)
ex.com
“This data is properly signed and proves you
own the private key”
Requests and
Responses
The Registration Request
ex.com
“Here is some data and a list of
parameters the credential should
have”
webauthn/protocol/options.go
type PublicKeyCredentialCreationOptions struct {
Challenge Challenge
RelyingParty RelyingPartyEntity
User UserEntity
Parameters []CredentialParameter
AuthenticatorSelection AuthenticatorSelection
Timeout int
CredentialExcludeList []CredentialDescriptor
Attestation string
}
The Relying Party should
hand back a response that
looks like this
Credential Creation Options
webauthn/protocol/options.go
type PublicKeyCredentialCreationOptions struct {
Challenge Challenge
RelyingParty RelyingPartyEntity
User UserEntity
Parameters []CredentialParameter
AuthenticatorSelection AuthenticatorSelection
Timeout int
CredentialExcludeList []CredentialDescriptor
Attestation string
}
These 3 are required, the
rest are optional but
recommended.
Credential Creation Options
webauthn/protocol/options.go
type PublicKeyCredentialCreationOptions struct {
Challenge Challenge
RelyingParty RelyingPartyEntity
User UserEntity
Parameters []CredentialParameter
AuthenticatorSelection AuthenticatorSelection
Timeout int
CredentialExcludeList []CredentialDescriptor
Attestation string
}
● Generated by RP server-side.
● Helps prevent replay attacks
● Stored until the registration is
complete
○ As session data, in a DB, etc...
Challenge Parameter
webauthn/protocol/entities.go
type RelyingPartyEntity struct {
Name string // Organization Name most likely
Icon string // URL of a image/logo/avatar
ID string // usually the origin url (https://ex.com)
}
● Only the name is required
○ ID can be overridden
○ ex.com can create an account for
test.ex.com
○ not vice versa
● ID must have HTTPS
Relying Party Information
webauthn/protocol/entities.go
type UserEntity struct {
Name string // Readable name used by the RP
Icon string
DisplayName string // Readable name chosen by the user
ID []byte // The RP’s ID for the user.
}
● Name and ID are required
● Display Name is used for the
user’s notification
User Information
webauthn/protocol/options.go
type CredentialParameter struct {
Type string // Should be “public-key”
Algorithm string // A COSE Algorithm Identifier
}
● Only public-key is currently
defined as a type
● The algorithm field should be a
value defined in the IANA COSE
Registry
○ -7 for ES256, -257 for RS256, etc
Credential Parameters
webauthn/protocol/options.go
type AuthenticatorSelection struct {
// Attachment could be “platform” or “cross-platform”
AuthenticatorAttachment string
RequireResidentKey boolean // Should the key be Resident
// Should the user be verified?
// Can be “required”, “preferred”, or “discouraged”
UserVerificationRequiremnet string
}
● None are required
● User Verification defaults to
“preferred”
● Authentication attachment tells
us how it should be connected
○ “cross-platform” is “roaming”
○ “platform” is internal to the
device
● We’ll talk more about resident
keys later
Authenticator Selection
webauthn/protocol/options.go
type PublicKeyCredentialCreationOptions struct {
Challenge Challenge
RelyingParty RelyingPartyEntity
User UserEntity
Parameters []CredentialParameter
AuthenticatorSelection AuthenticatorSelection
Timeout int
CredentialExcludeList []CredentialDescriptor
Attestation string
}
● The amount of time to allow the
authenticator/user to respond
● Not required by the client
○ May actually be overridden
● Uses milliseconds
● Recommend 60 seconds
○ That’s 60000 milliseconds!
Timeout Parameter
webauthn/protocol/options.go
type PublicKeyCredentialCreationOptions struct {
Challenge Challenge
RelyingParty RelyingPartyEntity
User UserEntity
Parameters []CredentialParameter
AuthenticatorSelection AuthenticatorSelection
Timeout int
CredentialExcludeList []CredentialDescriptor
Attestation string
}
● Allows us to exclude an
authenticator if it contains a
credential described in this list
● This is helpful for registering
multiple authenticators
● We’ll talk more about the
CredentialDescriptor
object later.
Credential Exclusion List
webauthn/protocol/options.go
type PublicKeyCredentialCreationOptions struct {
Challenge Challenge
RelyingParty RelyingPartyEntity
User UserEntity
Parameters []CredentialParameter
AuthenticatorSelection AuthenticatorSelection
Timeout int
CredentialExcludeList []CredentialDescriptor
Attestation string
}
● Tells us if we want the
authenticator to attest the
credential
● Three conveyance types
○ Direct
○ Indirect
○ None
Attestation Conveyence
Registration Request
“Given these options, try making a Credential”
In JSON
Provided to
navigator.credentials.create()
Requested at line 105 in
webauthn.io/static/dist/js/webauthn.js
{
"challenge":"InlQbas7et/4C0DVVW9/4qzoz2NuyM9c=",
"rp": {
"name": "webauthn.io",
"id": "webauthn.io"
},
"user": {
"name": "testuser",
"displayName": "testuser",
"id": "9AUAAAAAAAAAAA=="
},
"pubKeyCredParams": [
{
"type": "public-key",
"alg": -7
},
...
],
"authenticatorSelection": {
"authenticatorAttachment": "platform",
"requireResidentKey": false,
"userVerification": "preferred"
},
"timeout": 60000,
"attestation": "direct"
}
Registration Request
Registration Request
PublicKeyCredential
Registration Response
ex.com
JSON(PublicKeyCredential)
In JSON
Provided from
navigator.credentials.create()
Returned at line 107 in
webauthn.io/static/dist/js/webauthn.js
{
"id":"AOB4OhswadyGM0GHREg...",
"rawId":"AOB4OhswadyGM0GHREg...",
"type":"public-key",
"response":{
"attestationObject":"o2NmbXRm...",
"clientDataJSON":"eyJjaGFsbG..."
}
}
Registration Response
ex.com
“The data I sent you is signed correctly and you
followed my options!”
Registration Response
ex.com
+ ID
Registration Response
Side-note: Extensions
In JSON
Provided to
navigator.credentials.create()
{
// In your Registration Options
“extensions”: {
“loc” : “true”
}
// Will ask for the authenticators geo-loc
}
Registration Request
with Extensions
In JSON
If you have the results you have to
parse them with this function.
// I create a credential with the extension args
credential = navigator.credentials.create(options)
extensions = credential.getClientExtensionResults()
// extensions contains a map of extension IDs
keyed to their results.
Registration Response
with Extensions
Extensions
● Depending on the extension, can be requested at
registration, login, or both
● Extensions can require input/output from the client and/or
the authententicator
● Most helpful extension right now is probably the
AppID extension for login
○ Allows for retro-support of legacy FIDO APIs
Login Requests and Responses
Login (Assertion)
ex.com
ID
“Assert that you own the private key for this
credential with the given ID by signing this data”
webauthn/protocol/options.go
type PublicKeyCredentialRequestOptions struct {
Challenge Challenge
Timeout int
RelyingPartyId string
AllowedCredentials []CredentialDescriptor
UserVerification string
}
The Relying Party should
hand back a response that
looks like this
Credential Assertion Options
webauthn/protocol/options.go
type PublicKeyCredentialRequestOptions struct {
Challenge Challenge
Timeout int
RelyingPartyId string
AllowedCredentials []CredentialDescriptor
UserVerification string
}
● Challenge is the only
requirement!
○ Same structure as prior
Challenge Parameter
webauthn/protocol/options.go
type PublicKeyCredentialRequestOptions struct {
Challenge Challenge
Timeout int
RelyingPartyId string
AllowedCredentials []CredentialDescriptor
UserVerification string
}
● Same as in Creation
Request
Timeout Parameter
webauthn/protocol/options.go
type PublicKeyCredentialRequestOptions struct {
Challenge Challenge
Timeout int
RelyingPartyId string
AllowedCredentials []CredentialDescriptor
UserVerification string
}
● The URL of the RP
○ Can be inferred by client
Relying Party ID Attribute
webauthn/protocol/options.go
type PublicKeyCredentialRequestOptions struct {
Challenge Challenge
Timeout int
RelyingPartyId string
AllowedCredentials []CredentialDescriptor
UserVerification string
}
● List of Credential
Descriptions to allow for
● Should contain the
credentials registered to
the user for an RP
Allowed Credential List
webauthn/protocol/options.go
type CredentialDescriptor struct {
Type string // Should be “public-key”
CredentialID []byte // Stored Credential ID
Transport []string // “usb”,“nfc”,“ble”,”internal”
// “lightning” transport added recently!
}
● Type will be “public-key”
● Credential ID is the stored ID
● What transports the
authenticator should use assert
the credential.
○ Internal should be used if the
authenticator is built in to the
device.
Credential Descriptor
webauthn/protocol/options.go
type PublicKeyCredentialRequestOptions struct {
Challenge Challenge
Timeout int
RelyingPartyId string
AllowedCredentials []CredentialDescriptor
UserVerification string
}
● Should the user be verified by
the authenticator?
○ Required
○ Preferred
○ Discouraged
● User Verification == “Human”
Verification
● Defaults to preferred
● If set to required, client will
exclude ineligible authenticators
User Verification Requirements
Login (Assertion)
“Given these options and ID, prove you own it”
In JSON
Provided to
navigator.credentials.get()
Requested at line 105 in
webauthn.io/static/dist/js/webauthn.js
{
"publicKey": {
"challenge": "DTea9k7tF9waXg0LTbOVOEvm0unHoo=",
"timeout": 60000,
"rpId": "webauthn.io",
"allowCredentials": [
{
"type": "public-key",
"id": "KWUeR0DoUAkpS5S3nHydoK..."
},
-- More Allowed Credentials -
-
{
"type": "public-key",
"id": "i7yvlHv0YMF3BK81VeqIdW..."
}
]
}
Assertion Request
Assertion Response
PublicKeyCredential
Assertion Response
ex.com
JSON(PublicKeyCredential)
In JSON
Provided from
navigator.credentials.get()
Returned at line 107 in
webauthn.io/static/dist/js/webauthn.js
{
"id":"AOB4OhswadyGM0GHREg...",
"rawId":"AOB4OhswadyGM0GHREg...",
"type":"public-key",
"response":{
"authenticatorData":"o2NmbXRm...",
"clientDataJSON":"eyJjaGFsbG...",
"signature":"oASLKdlOMEIBaqs...",
"userHandle":"9AUAAAA...",
}
}
Assertion Response
In JSON
Provided from
navigator.credentials.get()
Returned at line 107 in
webauthn.io/static/dist/js/webauthn.js
{
"id":"AOB4OhswadyGM0GHREg...",
"rawId":"AOB4OhswadyGM0GHREg...",
"type":"public-key",
"response":{
"authenticatorData":"o2NmbXRm...",
"clientDataJSON":"eyJjaGFsbG...",
"signature":"oASLKdlOMEIBaqs...",
"userHandle":"9AUAAAA...",
}
}
Assertion Response
Functions where we handle this data
● RequestNewCredential in webauthn.io/server/credential.go:17
○ Calls BeginRegistration in webauthn/webauthn/registration.go:19
● MakeNewCredential in webauthn.io/server/credential.go:66
○ Calls FinishRegistration in webauthn/webauthn/registration.go:105
● GetAssertion in webauthn.io/server/assertion.go:21
○ Calls BeginLogin in webauthn/webauthn/login.go:25
● MakeAssertion in webauthn.io/server/assertion.go:57
○ Calls FinishLogin in webauthn/webauthn/login.go:85
To Rebuild Your Code
## For Golang
## End the Go application (Ctrl-C)
$go run main.go
## For Docker
$docker down $DOCKER_ID // get ID with $docker ps
$docker build .
$docker run -p 9005:9005 --rm webauthn.io
Unpacking Responses
PublicKeyCredential
Registration
ex.com
JSON(PublicKeyCredential)
In JSON
Provided from
navigator.credentials.create()
Returned at line 107 in
webauthn.io/static/dist/js/webauthn.js
{
"id":"AOB4OhswadyGM0GHREg...",
"rawId":"AOB4OhswadyGM0GHREg...",
"type":"public-key",
"response":{
"attestationObject":"o2NmbXRm...",
"clientDataJSON":"eyJjaGFsbG..."
}
}
Registration Response
{
"id":"AOB4OhswadyGM0GHREg...",
"rawId":"AOB4OhswadyGM0GHREg...",
"type":"public-key",
"response":{
"attestationObject":"o2NmbXRm...",
"clientDataJSON":"eyJjaGFsbG..."
}
}
Registration Response
● Highlighted values contain all we
need
● URL Base 64 Encoded
● Parsed in webauthn/
○ webauthn/registration.go
○ protocol/credential.go
○ protocol/attestation.go
type CollectedClientData struct {
Type CeremonyType
Challenge string
Origin string
TokenBinding TokenBinding
}
Client Data
● Contains the type of event
(ceremony)
○ webauthn.create
○ Webauthn.get
● The initial challenge
● The origin URL
○ According to the authenticator
● Token binding helps us bind the
session
● But how do we trust it?
Attestation
protocol/attestation.go:56
Attestation and Attestation Objects
● Attestation Objects are packed in CBOR
○ Concise Binary Object Representation
● The Attestation Signature (most of the time) gives us proof
that the authenticator actually created the credential
● Up to the developer to check trust roots (with services like
FIDO’s Metadata Service)
● Read the spec or look in webauthn for instructions
Should I Handle or Request Attestation?
● Are you…
○ A bank?
○ A government agency?
○ Often attacked by nation states?
○ Excited to use the FIDO Metadata Service?
● If not, you probably don’t need to do attestation.
○ It depends on your threat model!
In JSON
Provided from
navigator.credentials.get()
Returned at line 107 in
webauthn.io/static/dist/js/webauthn.js
{
"id":"AOB4OhswadyGM0GHREg...",
"rawId":"AOB4OhswadyGM0GHREg...",
"type":"public-key",
"response":{
"authenticatorData":"o2NmbXRm...",
"clientDataJSON":"eyJjaGFsbG...",
"signature":"oASLKdlOMEIBaqs...",
"userHandle":"9AUAAAA...",
}
}
Assertion Response
{
"id":"AOB4OhswadyGM0GHREg...",
"rawId":"AOB4OhswadyGM0GHREg...",
"type":"public-key",
"response":{
"authenticatorData":"o2NmbXRm...",
"clientDataJSON":"eyJjaGFsbG...",
"signature":"oASLKdlOMEIBaqs...",
"userHandle":"9AUAAAA...",
}
}
Assertion Response
● Client Data remains the same format
● Authenticator data instead of
Attestation data
● Signature != Attestation Signature
● Parsed in webauthn/
○ protocol/assertion.go:49
Credential Validation
Validation in 3 parts
1. Validate the data from the RP (Client Data)
○ Request Type, Request Origin, Challenge, etc
2. Validate the data from the Authenticator
○ Signature, Attestation data, AAGUID
3. Validate the credential
○ Public Key Format, Signature
Validation
● Check out the verification methods in webauthn/
○ protocol/credential.go
○ protocol/attestation.go
○ protocol/assertion.go
ex.com
+ ID
Completing Registration Validation
Looking Forward
Resident Key Handling
● Gaining stable implementation in browsers
● Allows for mapping an RP ID to a credential, rather than to
a credential ID. Could also maybe support key syncing.
● The key is stored either on the authenticator and the
mapping is stored by the browser client, or the key is stored
(wrapped) outside of the authenticator.
○ Supporting “Incognito” tabs is being discussed
Account Recovery
Three main genres of recovery:
1. Alternate Credentials/Authenticators
○ Enrollment of multiple authenticators
○ Potentially the syncing of a single credential
2. Mutually Trusted/Delegated Authorities
○ A group that the relying party trusts to authenticate the user
3. Tokens of Identity
○ A token symbolizing a trust relationship between user and an MTA
○ i.e. State/National ID (non-digital)
4. Byzantine Relationships
Account Recovery
● Current guidance focuses mostly on Multiple authenticator enrollment
● Other methods I recommend (threat model dependent)
○ Push Messaging (Like duo, krypton, okta)
○ Email
○ If you’re an enterprise, help desk support flow is critical!
○ Quick linking
● Don’t fall back to passwords!
Account Recovery Prevention
● Use the API to find out if you can enroll new authenticators and relay
this through UI/UX.
● Potentially use quick links to add mobile devices and vice versa, if threat
model allows for it.
● Invest in user education! Account recovery can be more expensive than
educating users, especially if you’re a consumer-facing company.
FIDO2 Account Recovery Resources
● GDWG, CDWG, and EDWG are all thinking about this. If you’re a Fido
Alliance member, check out their work!
● I recommend checking out the login.gov webinar available through Fido’s
Youtube Account.
● I’m here if you need help!
Questions?
Thank you!
Twitter: @codekaiju
nsteele@duo.com

Getting Started With WebAuthn

Editor's Notes

  • #3 My name is Nick Steele
  • #4 I’m a researcher with Duo Labs
  • #5 One of my colleagues once said this to me and it’s great
  • #42 https://w3c.github.io/webauthn/#iface-pkcredential
  • #49 https://w3c.github.io/webauthn/#iface-pkcredential
  • #66 https://w3c.github.io/webauthn/#iface-pkcredential
  • #86 https://w3c.github.io/webauthn/#iface-pkcredential
  • #93 https://w3c.github.io/webauthn/#iface-pkcredential