Skip to content

Commit 677f7ac

Browse files
feat(protocol): credential mediation
This adds support for credential mediation.
1 parent 7e1caf7 commit 677f7ac

File tree

4 files changed

+57
-7
lines changed

4 files changed

+57
-7
lines changed

protocol/authenticator.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,36 @@ type AttestedCredentialData struct {
5757
CredentialPublicKey []byte `json:"public_key"`
5858
}
5959

60+
// CredentialMediationRequirement represents mediation requirements for clients. When making a request via get(options)
61+
// or create(options), developers can set a case-by-case requirement for user mediation by choosing the appropriate
62+
// CredentialMediationRequirement enum value.
63+
//
64+
// See https://www.w3.org/TR/credential-management-1/#mediation-requirements
65+
type CredentialMediationRequirement string
66+
67+
const (
68+
// MediationSilent indicates user mediation is suppressed for the given operation. If the operation can be performed
69+
// without user involvement, wonderful. If user involvement is necessary, then the operation will return null rather
70+
// than involving the user.
71+
MediationSilent CredentialMediationRequirement = "silent"
72+
73+
// MediationOptional indicates if credentials can be handed over for a given operation without user mediation, they
74+
// will be. If user mediation is required, then the user agent will involve the user in the decision.
75+
MediationOptional CredentialMediationRequirement = "optional"
76+
77+
// MediationConditional indicates for get(), discovered credentials are presented to the user in a non-modal dialog
78+
// along with an indication of the origin which is requesting credentials. If the user makes a gesture outside of
79+
// the dialog, the dialog closes without resolving or rejecting the Promise returned by the get() method and without
80+
// causing a user-visible error condition. If the user makes a gesture that selects a credential, that credential is
81+
// returned to the caller. The prevent silent access flag is treated as being true regardless of its actual value:
82+
// the conditional behavior always involves user mediation of some sort if applicable credentials are discovered.
83+
MediationConditional CredentialMediationRequirement = "conditional"
84+
85+
// MediationRequired indicates the user agent will not hand over credentials without user mediation, even if the
86+
// prevent silent access flag is unset for an origin.
87+
MediationRequired CredentialMediationRequirement = "required"
88+
)
89+
6090
// AuthenticatorAttachment represents the IDL enum of the same name, and is used as part of the Authenticator Selection
6191
// Criteria.
6292
//

protocol/options.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ import (
55
)
66

77
type CredentialCreation struct {
8-
Response PublicKeyCredentialCreationOptions `json:"publicKey"`
8+
Response PublicKeyCredentialCreationOptions `json:"publicKey"`
9+
Mediation CredentialMediationRequirement `json:"mediation,omitempty"`
910
}
1011

1112
type CredentialAssertion struct {
12-
Response PublicKeyCredentialRequestOptions `json:"publicKey"`
13+
Response PublicKeyCredentialRequestOptions `json:"publicKey"`
14+
Mediation CredentialMediationRequirement `json:"mediation,omitempty"`
1315
}
1416

1517
// PublicKeyCredentialCreationOptions represents the IDL of the same name.

webauthn/login.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,16 @@ type DiscoverableUserHandler func(rawID, userHandle []byte) (user User, err erro
3131
//
3232
// Specification: §5.5. Options for Assertion Generation (https://www.w3.org/TR/webauthn/#dictionary-assertion-options)
3333
func (webauthn *WebAuthn) BeginLogin(user User, opts ...LoginOption) (*protocol.CredentialAssertion, *SessionData, error) {
34+
return webauthn.BeginMediatedLogin(user, "", opts...)
35+
}
36+
37+
// BeginDiscoverableLogin begins a client-side discoverable login, previously known as Resident Key logins.
38+
func (webauthn *WebAuthn) BeginDiscoverableLogin(opts ...LoginOption) (*protocol.CredentialAssertion, *SessionData, error) {
39+
return webauthn.beginLogin(nil, nil, "", opts...)
40+
}
41+
42+
// BeginMediatedLogin is similar to BeginLogin however it also allows specifying a credential mediation requirement.
43+
func (webauthn *WebAuthn) BeginMediatedLogin(user User, mediation protocol.CredentialMediationRequirement, opts ...LoginOption) (*protocol.CredentialAssertion, *SessionData, error) {
3444
credentials := user.WebAuthnCredentials()
3545

3646
if len(credentials) == 0 { // If the user does not have any credentials, we cannot perform an assertion.
@@ -43,15 +53,16 @@ func (webauthn *WebAuthn) BeginLogin(user User, opts ...LoginOption) (*protocol.
4353
allowedCredentials[i] = credential.Descriptor()
4454
}
4555

46-
return webauthn.beginLogin(user.WebAuthnID(), allowedCredentials, opts...)
56+
return webauthn.beginLogin(user.WebAuthnID(), allowedCredentials, mediation, opts...)
4757
}
4858

49-
// BeginDiscoverableLogin begins a client-side discoverable login, previously known as Resident Key logins.
50-
func (webauthn *WebAuthn) BeginDiscoverableLogin(opts ...LoginOption) (*protocol.CredentialAssertion, *SessionData, error) {
51-
return webauthn.beginLogin(nil, nil, opts...)
59+
// BeginDiscoverableMediatedLogin begins a client-side discoverable login with a mediation requirement, previously known
60+
// as Resident Key logins.
61+
func (webauthn *WebAuthn) BeginDiscoverableMediatedLogin(mediation protocol.CredentialMediationRequirement, opts ...LoginOption) (*protocol.CredentialAssertion, *SessionData, error) {
62+
return webauthn.beginLogin(nil, nil, mediation, opts...)
5263
}
5364

54-
func (webauthn *WebAuthn) beginLogin(userID []byte, allowedCredentials []protocol.CredentialDescriptor, opts ...LoginOption) (assertion *protocol.CredentialAssertion, session *SessionData, err error) {
65+
func (webauthn *WebAuthn) beginLogin(userID []byte, allowedCredentials []protocol.CredentialDescriptor, mediation protocol.CredentialMediationRequirement, opts ...LoginOption) (assertion *protocol.CredentialAssertion, session *SessionData, err error) {
5566
if err = webauthn.Config.validate(); err != nil {
5667
return nil, nil, fmt.Errorf(errFmtConfigValidate, err)
5768
}
@@ -62,6 +73,7 @@ func (webauthn *WebAuthn) beginLogin(userID []byte, allowedCredentials []protoco
6273
UserVerification: webauthn.Config.AuthenticatorSelection.UserVerification,
6374
AllowedCredentials: allowedCredentials,
6475
},
76+
Mediation: mediation,
6577
}
6678

6779
for _, opt := range opts {

webauthn/registration.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ type RegistrationOption func(*protocol.PublicKeyCredentialCreationOptions)
2121

2222
// BeginRegistration generates a new set of registration data to be sent to the client and authenticator.
2323
func (webauthn *WebAuthn) BeginRegistration(user User, opts ...RegistrationOption) (creation *protocol.CredentialCreation, session *SessionData, err error) {
24+
return webauthn.BeginMediatedRegistration(user, "", opts...)
25+
}
26+
27+
// BeginMediatedRegistration is similar to BeginRegistration however it also allows specifying a credential mediation requirement.
28+
func (webauthn *WebAuthn) BeginMediatedRegistration(user User, mediation protocol.CredentialMediationRequirement, opts ...RegistrationOption) (creation *protocol.CredentialCreation, session *SessionData, err error) {
2429
if err = webauthn.Config.validate(); err != nil {
2530
return nil, nil, fmt.Errorf(errFmtConfigValidate, err)
2631
}
@@ -64,6 +69,7 @@ func (webauthn *WebAuthn) BeginRegistration(user User, opts ...RegistrationOptio
6469
AuthenticatorSelection: webauthn.Config.AuthenticatorSelection,
6570
Attestation: webauthn.Config.AttestationPreference,
6671
},
72+
Mediation: mediation,
6773
}
6874

6975
for _, opt := range opts {

0 commit comments

Comments
 (0)