auth: publish passkey foundations preview
verify / verify (push) Successful in 3m40s

This commit is contained in:
2026-08-21 17:33:00 -04:00
parent fb6bbd0dad
commit bfe6cfd29e
230 changed files with 44547 additions and 17 deletions
@@ -0,0 +1,4 @@
// Package memory handles a [metadata.Provider] implementation that solely exists in memory. It's intended as a basis
// for other providers and generally not recommended to use directly unless you're implementing your own logic to handle
// the download and potential caching of the MDS3 blob yourself.
package memory
@@ -0,0 +1,90 @@
package memory
import (
"github.com/google/uuid"
"github.com/go-webauthn/webauthn/metadata"
)
// Option describes an optional pattern for this provider.
type Option func(provider *Provider) (err error)
// WithMetadata provides the required metadata for the memory provider.
func WithMetadata(mds map[uuid.UUID]*metadata.Entry) Option {
return func(provider *Provider) (err error) {
provider.mds = mds
return nil
}
}
// WithValidateEntry requires that the provided metadata has an entry for the given authenticator to be considered
// valid. By default an AAGUID which has a zero value should fail validation if [WithValidateEntryPermitZeroAAGUID] is not
// provided with the value of true. Default is true.
func WithValidateEntry(require bool) Option {
return func(provider *Provider) (err error) {
provider.entry = require
return nil
}
}
// WithValidateEntryPermitZeroAAGUID is an option that permits a zero'd AAGUID from an attestation statement to
// automatically pass metadata validations. Generally helpful to use with [WithValidateEntry]. Default is false.
func WithValidateEntryPermitZeroAAGUID(permit bool) Option {
return func(provider *Provider) (err error) {
provider.entryPermitZero = permit
return nil
}
}
// WithValidateTrustAnchor when set to true enables the validation of the attestation statement against the trust anchor
// from the metadata. Default is true.
func WithValidateTrustAnchor(validate bool) Option {
return func(provider *Provider) (err error) {
provider.anchors = validate
return nil
}
}
// WithValidateStatus when set to true enables the validation of the attestation statements AAGUID against the desired
// and undesired [metadata.AuthenticatorStatus] lists. Default is true.
func WithValidateStatus(validate bool) Option {
return func(provider *Provider) (err error) {
provider.status = validate
return nil
}
}
// WithValidateAttestationTypes when set to true enables the validation of the attestation statements type against the
// known types the authenticator can produce. Default is true.
func WithValidateAttestationTypes(validate bool) Option {
return func(provider *Provider) (err error) {
provider.attestation = validate
return nil
}
}
// WithStatusUndesired provides the list of statuses which are considered undesirable for status report validation
// purposes. Should be used with [WithValidateStatus] set to true.
func WithStatusUndesired(statuses []metadata.AuthenticatorStatus) Option {
return func(provider *Provider) (err error) {
provider.undesired = statuses
return nil
}
}
// WithStatusDesired provides the list of statuses which are considered desired and will be required for status report
// validation purposes. Should be used with [WithValidateStatus] set to true.
func WithStatusDesired(statuses []metadata.AuthenticatorStatus) Option {
return func(provider *Provider) (err error) {
provider.desired = statuses
return nil
}
}
@@ -0,0 +1,93 @@
package memory
import (
"context"
"fmt"
"github.com/google/uuid"
"github.com/go-webauthn/webauthn/metadata"
)
// New returns a new memory Provider given a set of functional Option's.
func New(opts ...Option) (provider metadata.Provider, err error) {
p := &Provider{
undesired: metadata.DefaultUndesiredAuthenticatorStatuses(),
entry: true,
anchors: true,
status: true,
attestation: true,
}
for _, opt := range opts {
if err = opt(p); err != nil {
return nil, err
}
}
if p.mds == nil {
return nil, fmt.Errorf("memory metadata provider has not been initialized with metadata")
}
return p, nil
}
// Provider is a concrete implementation of the [metadata.Provider] that utilizes memory for validation. This provider is
// a simple one-shot that doesn't perform any locking, provide dynamic functionality, or download the metadata at any
// stage (it expects it's provided via one of the Option's).
type Provider struct {
mds map[uuid.UUID]*metadata.Entry
desired []metadata.AuthenticatorStatus
undesired []metadata.AuthenticatorStatus
entry bool
entryPermitZero bool
anchors bool
status bool
attestation bool
}
func (p *Provider) GetEntry(ctx context.Context, aaguid uuid.UUID) (entry *metadata.Entry, err error) {
if p.mds == nil {
return nil, metadata.ErrNotInitialized
}
var ok bool
if entry, ok = p.mds[aaguid]; ok {
return entry, nil
}
return nil, nil
}
func (p *Provider) GetValidateEntry(ctx context.Context) (require bool) {
return p.entry
}
func (p *Provider) GetValidateEntryPermitZeroAAGUID(ctx context.Context) (skip bool) {
return p.entryPermitZero
}
func (p *Provider) GetValidateTrustAnchor(ctx context.Context) (validate bool) {
return p.anchors
}
func (p *Provider) GetValidateStatus(ctx context.Context) (validate bool) {
return p.status
}
func (p *Provider) GetValidateAttestationTypes(ctx context.Context) (validate bool) {
return p.attestation
}
func (p *Provider) ValidateStatusReports(ctx context.Context, reports []metadata.StatusReport) (err error) {
if !p.status {
return nil
}
return metadata.ValidateStatusReports(reports, p.desired, p.undesired)
}
var (
_ metadata.Provider = (*Provider)(nil)
)
@@ -0,0 +1,236 @@
package memory
import (
"context"
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/go-webauthn/webauthn/metadata"
)
func TestNew(t *testing.T) {
id := uuid.New()
entry := &metadata.Entry{
AaGUID: id,
MetadataStatement: metadata.Statement{
Description: "Test Authenticator",
},
}
testCases := []struct {
name string
opts []Option
err string
}{
{
name: "ShouldSucceedWithMetadata",
opts: []Option{WithMetadata(map[uuid.UUID]*metadata.Entry{id: entry})},
},
{
name: "ShouldFailWithoutMetadata",
opts: nil,
err: "memory metadata provider has not been initialized with metadata",
},
{
name: "ShouldSucceedWithAllOptions",
opts: []Option{
WithMetadata(map[uuid.UUID]*metadata.Entry{id: entry}),
WithValidateEntry(false),
WithValidateEntryPermitZeroAAGUID(true),
WithValidateTrustAnchor(false),
WithValidateStatus(false),
WithValidateAttestationTypes(false),
WithStatusUndesired([]metadata.AuthenticatorStatus{metadata.Revoked}),
WithStatusDesired([]metadata.AuthenticatorStatus{metadata.FidoCertified}),
},
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
provider, err := New(tc.opts...)
if tc.err != "" {
assert.Nil(t, provider)
require.EqualError(t, err, tc.err)
} else {
require.NoError(t, err)
assert.NotNil(t, provider)
}
})
}
}
func TestProvider_GetEntry(t *testing.T) {
id := uuid.New()
missing := uuid.New()
entry := &metadata.Entry{
AaGUID: id,
MetadataStatement: metadata.Statement{
Description: "Test Authenticator",
},
}
provider, err := New(WithMetadata(map[uuid.UUID]*metadata.Entry{id: entry}))
require.NoError(t, err)
testCases := []struct {
name string
aaguid uuid.UUID
expected *metadata.Entry
}{
{
name: "ShouldReturnEntryWhenExists",
aaguid: id,
expected: entry,
},
{
name: "ShouldReturnNilWhenNotExists",
aaguid: missing,
expected: nil,
},
{
name: "ShouldReturnNilForNilUUID",
aaguid: uuid.Nil,
expected: nil,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
result, err := provider.GetEntry(context.Background(), tc.aaguid)
require.NoError(t, err)
assert.Equal(t, tc.expected, result)
})
}
}
func TestProvider_ConfigurationFlags(t *testing.T) {
testCases := []struct {
name string
opts []Option
expectedEntry bool
expectedPermitZero bool
expectedTrustAnchor bool
expectedStatus bool
expectedAttestationTypes bool
}{
{
name: "ShouldReturnDefaultFlags",
opts: []Option{
WithMetadata(map[uuid.UUID]*metadata.Entry{}),
},
expectedEntry: true,
expectedPermitZero: false,
expectedTrustAnchor: true,
expectedStatus: true,
expectedAttestationTypes: true,
},
{
name: "ShouldReturnCustomFlags",
opts: []Option{
WithMetadata(map[uuid.UUID]*metadata.Entry{}),
WithValidateEntry(false),
WithValidateEntryPermitZeroAAGUID(true),
WithValidateTrustAnchor(false),
WithValidateStatus(false),
WithValidateAttestationTypes(false),
},
expectedEntry: false,
expectedPermitZero: true,
expectedTrustAnchor: false,
expectedStatus: false,
expectedAttestationTypes: false,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
provider, err := New(tc.opts...)
require.NoError(t, err)
ctx := context.Background()
assert.Equal(t, tc.expectedEntry, provider.GetValidateEntry(ctx))
assert.Equal(t, tc.expectedPermitZero, provider.GetValidateEntryPermitZeroAAGUID(ctx))
assert.Equal(t, tc.expectedTrustAnchor, provider.GetValidateTrustAnchor(ctx))
assert.Equal(t, tc.expectedStatus, provider.GetValidateStatus(ctx))
assert.Equal(t, tc.expectedAttestationTypes, provider.GetValidateAttestationTypes(ctx))
})
}
}
func TestProvider_ValidateStatusReports(t *testing.T) {
testCases := []struct {
name string
opts []Option
reports []metadata.StatusReport
err string
}{
{
name: "ShouldPassWithNoUndesiredStatuses",
opts: []Option{
WithMetadata(map[uuid.UUID]*metadata.Entry{}),
WithValidateStatus(true),
},
reports: []metadata.StatusReport{{Status: metadata.FidoCertified}},
},
{
name: "ShouldFailWithUndesiredStatus",
opts: []Option{
WithMetadata(map[uuid.UUID]*metadata.Entry{}),
WithValidateStatus(true),
WithStatusUndesired([]metadata.AuthenticatorStatus{metadata.Revoked}),
},
reports: []metadata.StatusReport{{Status: metadata.Revoked}},
err: "The following undesired status reports were present: REVOKED",
},
{
name: "ShouldPassWhenStatusValidationDisabled",
opts: []Option{
WithMetadata(map[uuid.UUID]*metadata.Entry{}),
WithValidateStatus(false),
},
reports: []metadata.StatusReport{{Status: metadata.Revoked}},
},
{
name: "ShouldFailWithDesiredStatusAbsent",
opts: []Option{
WithMetadata(map[uuid.UUID]*metadata.Entry{}),
WithValidateStatus(true),
WithStatusDesired([]metadata.AuthenticatorStatus{metadata.FidoCertified}),
WithStatusUndesired(nil),
},
reports: []metadata.StatusReport{{Status: metadata.NotFidoCertified}},
err: "The following desired status reports were absent: FIDO_CERTIFIED",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
provider, err := New(tc.opts...)
require.NoError(t, err)
err = provider.ValidateStatusReports(context.Background(), tc.reports)
if tc.err != "" {
require.EqualError(t, err, tc.err)
} else {
assert.NoError(t, err)
}
})
}
}
func TestProvider_GetEntry_NilMDS(t *testing.T) {
p := &Provider{}
entry, err := p.GetEntry(context.Background(), uuid.New())
assert.Nil(t, entry)
require.EqualError(t, err, "metadata: not initialized")
}