Repository: go-webauthn/webauthn
Pull request #13 (2022-03-01 06:05:21)

feat: add resident key protocol option
Adds the residentKey protocol option which is part of Webauthn Level 2.


Patch:
diff --git a/protocol/authenticator.go b/protocol/authenticator.go
index c054718..b196035 100644
--- a/protocol/authenticator.go
+++ b/protocol/authenticator.go
@@ -50,7 +50,7 @@ type AttestedCredentialData struct {
 	CredentialPublicKey []byte `json:"public_key"`
 }
 
-// AuthenticatorAttachment https://www.w3.org/TR/webauthn/#platform-attachment
+// AuthenticatorAttachment https://www.w3.org/TR/webauthn/#dom-authenticatorselectioncriteria-authenticatorattachment
 type AuthenticatorAttachment string
 
 const (
@@ -65,6 +65,21 @@ const (
 	CrossPlatform AuthenticatorAttachment = "cross-platform"
 )
 
+// ResidentKeyRequirement https://www.w3.org/TR/webauthn/#dom-authenticatorselectioncriteria-residentkey
+type ResidentKeyRequirement string
+
+const (
+	// ResidentKeyRequirementDiscouraged indicates to the client we do not want a discoverable credential. This is the default.
+	ResidentKeyRequirementDiscouraged ResidentKeyRequirement = "discouraged"
+
+	// ResidentKeyRequirementPreferred indicates to the client we would prefer a discoverable credential.
+	ResidentKeyRequirementPreferred ResidentKeyRequirement = "preferred"
+
+	// ResidentKeyRequirementRequired indicates to the client we require a discoverable credential and that it should
+	// fail if the credential does not support this feature.
+	ResidentKeyRequirementRequired ResidentKeyRequirement = "required"
+)
+
 // Authenticators may implement various transports for communicating with clients. This enumeration defines
 // hints as to how clients might communicate with a particular authenticator in order to obtain an assertion
 // for a specific credential. Note that these hints represent the WebAuthn Relying Party's best belief as to
@@ -237,8 +252,8 @@ func ResidentKeyRequired() *bool {
 	return &required
 }
 
-// ResidentKeyUnrequired - Do not require that the private key be resident to the client device.
-func ResidentKeyUnrequired() *bool {
+// ResidentKeyNotRequired - Do not require that the private key be resident to the client device.
+func ResidentKeyNotRequired() *bool {
 	required := false
 	return &required
 }
diff --git a/protocol/options.go b/protocol/options.go
index 3b23762..ca70779 100644
--- a/protocol/options.go
+++ b/protocol/options.go
@@ -91,6 +91,11 @@ type AuthenticatorSelection struct {
 	// credentials. If the parameter is set to true, the authenticator MUST create a client-side-resident
 	// public key credential source when creating a public key credential.
 	RequireResidentKey *bool `json:"requireResidentKey,omitempty"`
+
+	// ResidentKey this member describes the Relying Party's requirements regarding resident
+	// credentials per Webauthn Level 2.
+	ResidentKey ResidentKeyRequirement `json:"residentKey,omitempty"`
+
 	// UserVerification This member describes the Relying Party's requirements regarding user verification for
 	// the create() operation. Eligible authenticators are filtered to only those capable of satisfying this
 	// requirement.
diff --git a/webauthn/registration.go b/webauthn/registration.go
index 4988d7e..74e2121 100644
--- a/webauthn/registration.go
+++ b/webauthn/registration.go
@@ -113,6 +113,19 @@ func WithAppIdExcludeExtension(appid string) RegistrationOption {
 	}
 }
 
+// WithResidentKeyRequirement sets both the resident key and require resident key protocol options. When
+func WithResidentKeyRequirement(requirement protocol.ResidentKeyRequirement) RegistrationOption {
+	return func(cco *protocol.PublicKeyCredentialCreationOptions) {
+		cco.AuthenticatorSelection.ResidentKey = requirement
+		switch requirement {
+		case protocol.ResidentKeyRequirementRequired:
+			cco.AuthenticatorSelection.RequireResidentKey = protocol.ResidentKeyRequired()
+		default:
+			cco.AuthenticatorSelection.RequireResidentKey = protocol.ResidentKeyNotRequired()
+		}
+	}
+}
+
 // 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) {


Test patch:
diff --git a/webauthn/authenticator_test.go b/webauthn/authenticator_test.go
index 643531c..1b2b7a2 100644
--- a/webauthn/authenticator_test.go
+++ b/webauthn/authenticator_test.go
@@ -127,12 +127,12 @@ func TestSelectAuthenticator(t *testing.T) {
 		{"Generate Correct Authenticator Selection",
 			args{
 				att: "platform",
-				rrk: p.ResidentKeyUnrequired(),
+				rrk: p.ResidentKeyNotRequired(),
 				uv:  "preferred",
 			},
 			p.AuthenticatorSelection{
 				AuthenticatorAttachment: p.Platform,
-				RequireResidentKey:      p.ResidentKeyUnrequired(),
+				RequireResidentKey:      p.ResidentKeyNotRequired(),
 				UserVerification:        p.VerificationPreferred,
 			},
 		},