From c1327296a823ea4cfafb879dc658392a20e52ac1 Mon Sep 17 00:00:00 2001 From: James Elliott Date: Sat, 12 Feb 2022 21:58:59 +1100 Subject: [PATCH] feat: add with setters for appid related extensions This automatically includes the specified appid if one of the credentials contains a credential with type fido-u2f. --- protocol/attestation_u2f.go | 2 +- protocol/credential.go | 10 +++++++--- protocol/extensions.go | 5 +++++ protocol/options.go | 3 +++ webauthn/credential.go | 10 ++++++++++ webauthn/login.go | 16 ++++++++++++++++ webauthn/registration.go | 16 ++++++++++++++++ 7 files changed, 58 insertions(+), 4 deletions(-) diff --git a/protocol/attestation_u2f.go b/protocol/attestation_u2f.go index 194fad2..8465e85 100644 --- a/protocol/attestation_u2f.go +++ b/protocol/attestation_u2f.go @@ -11,7 +11,7 @@ import ( "github.com/go-webauthn/webauthn/protocol/webauthncose" ) -var u2fAttestationKey = "fido-u2f" +var u2fAttestationKey = CredentialTypeFIDOU2F func init() { RegisterAttestationFormat(u2fAttestationKey, verifyU2FFormat) diff --git a/protocol/credential.go b/protocol/credential.go index c1e3ff9..9404dbd 100644 --- a/protocol/credential.go +++ b/protocol/credential.go @@ -198,11 +198,11 @@ func (ppkc ParsedPublicKeyCredential) GetAppID(authExt AuthenticationExtensions, // If the credential does not have the correct attestation type it is assumed to NOT be a fido-u2f credential. // https://w3c.github.io/webauthn/#sctn-fido-u2f-attestation - if credentialAttestationType != "fido-u2f" { + if credentialAttestationType != CredentialTypeFIDOU2F { return "", nil } - if clientValue, ok = ppkc.ClientExtensionResults["appid"]; !ok { + if clientValue, ok = ppkc.ClientExtensionResults[ExtensionAppID]; !ok { return "", nil } @@ -214,7 +214,7 @@ func (ppkc ParsedPublicKeyCredential) GetAppID(authExt AuthenticationExtensions, return "", nil } - if value, ok = authExt["appid"]; !ok { + if value, ok = authExt[ExtensionAppID]; !ok { return "", ErrBadRequest.WithDetails("Session Data does not have an appid but Client Output indicates it should be set") } @@ -224,3 +224,7 @@ func (ppkc ParsedPublicKeyCredential) GetAppID(authExt AuthenticationExtensions, return appID, nil } + +const ( + CredentialTypeFIDOU2F = "fido-u2f" +) diff --git a/protocol/extensions.go b/protocol/extensions.go index 0cfa2ed..f925eb2 100644 --- a/protocol/extensions.go +++ b/protocol/extensions.go @@ -6,3 +6,8 @@ package protocol // (https://www.w3.org/TR/webauthn/#sctn-defined-extensions). type AuthenticationExtensionsClientOutputs map[string]interface{} + +const ( + ExtensionAppID = "appid" + ExtensionAppIDExclude = "appidExclude" +) diff --git a/protocol/options.go b/protocol/options.go index 89e7f51..3b23762 100644 --- a/protocol/options.go +++ b/protocol/options.go @@ -49,6 +49,9 @@ type CredentialDescriptor struct { CredentialID []byte `json:"id"` // The authenticator transports that can be used Transport []AuthenticatorTransport `json:"transports,omitempty"` + + // The AttestationType from the Credential. Used internally only. + AttestationType string `json:"-"` } // CredentialParameter is the credential type and algorithm diff --git a/webauthn/credential.go b/webauthn/credential.go index 899c3db..9aaab3a 100644 --- a/webauthn/credential.go +++ b/webauthn/credential.go @@ -21,6 +21,16 @@ type Credential struct { Authenticator Authenticator } +// ToCredentialDescriptor converts a Credential into a protocol.CredentialDescriptor. +func (c Credential) ToCredentialDescriptor() (descriptor protocol.CredentialDescriptor) { + return protocol.CredentialDescriptor{ + CredentialID: c.ID, + Transport: c.Transport, + Type: protocol.PublicKeyCredentialType, + AttestationType: c.AttestationType, + } +} + // MakeNewCredential will return a credential pointer on successful validation of a registration response func MakeNewCredential(c *protocol.ParsedCredentialCreationData) (*Credential, error) { newCredential := &Credential{ diff --git a/webauthn/login.go b/webauthn/login.go index 44860a6..2cbd881 100644 --- a/webauthn/login.go +++ b/webauthn/login.go @@ -89,6 +89,22 @@ func WithAssertionExtensions(extensions protocol.AuthenticationExtensions) Login } } +// WithAppIdExtension automatically includes the specified appid if the AllowedCredentials contains a credential +// with the type `fido-u2f`. +func WithAppIdExtension(appid string) LoginOption { + return func(cco *protocol.PublicKeyCredentialRequestOptions) { + for _, credential := range cco.AllowedCredentials { + if credential.AttestationType == protocol.CredentialTypeFIDOU2F { + if cco.Extensions == nil { + cco.Extensions = map[string]interface{}{} + } + + cco.Extensions[protocol.ExtensionAppID] = appid + } + } + } +} + // Take the response from the client and validate it against the user credentials and stored session data func (webauthn *WebAuthn) FinishLogin(user User, session SessionData, response *http.Request) (*Credential, error) { parsedResponse, err := protocol.ParseCredentialRequestResponse(response) diff --git a/webauthn/registration.go b/webauthn/registration.go index 99e0d75..4988d7e 100644 --- a/webauthn/registration.go +++ b/webauthn/registration.go @@ -97,6 +97,22 @@ func WithExtensions(extension protocol.AuthenticationExtensions) RegistrationOpt } } +// WithAppIdExcludeExtension automatically includes the specified appid if the CredentialExcludeList contains a credential +// with the type `fido-u2f`. +func WithAppIdExcludeExtension(appid string) RegistrationOption { + return func(cco *protocol.PublicKeyCredentialCreationOptions) { + for _, credential := range cco.CredentialExcludeList { + if credential.AttestationType == protocol.CredentialTypeFIDOU2F { + if cco.Extensions == nil { + cco.Extensions = map[string]interface{}{} + } + + cco.Extensions[protocol.ExtensionAppIDExclude] = appid + } + } + } +} + // Take the response from the authenticator and client and verify the credential against the user's credentials and // session data. func (webauthn *WebAuthn) FinishRegistration(user User, session SessionData, response *http.Request) (*Credential, error) {