This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
// Package authwebauthn provides storage-neutral, passkey-only WebAuthn
|
||||
// ceremonies. It owns relying-party policy, bounded single-use ceremony state,
|
||||
// credential lifecycle, and recovery tokens while delegating protocol parsing
|
||||
// and signature verification to a pinned WebAuthn implementation.
|
||||
package authwebauthn
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"gamertan.com/web/auth"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrCeremonyNotFound = errors.New("authwebauthn: ceremony not found")
|
||||
ErrCredentialNotFound = errors.New("authwebauthn: credential not found")
|
||||
ErrEnrollmentNotFound = errors.New("authwebauthn: enrollment token not found")
|
||||
ErrCredentialFloor = errors.New("authwebauthn: the required credential floor cannot be crossed")
|
||||
ErrLastCredential = errors.New("authwebauthn: the last credential cannot be removed remotely")
|
||||
ErrOperationBinding = errors.New("authwebauthn: operation binding does not match")
|
||||
ErrPasskeyReadiness = errors.New("authwebauthn: at least two passkeys are required")
|
||||
ErrUnsupportedCredential = errors.New("authwebauthn: credential algorithm is unsupported")
|
||||
)
|
||||
|
||||
const (
|
||||
CeremonyRegistration = "registration"
|
||||
CeremonyLogin = "login"
|
||||
CeremonyApproval = "approval"
|
||||
)
|
||||
|
||||
type Credential struct {
|
||||
ID []byte
|
||||
UserID string
|
||||
Label string
|
||||
Data json.RawMessage
|
||||
CreatedAt time.Time
|
||||
LastUsedAt time.Time
|
||||
}
|
||||
|
||||
// CredentialSummary is the non-secret credential metadata applications may
|
||||
// show to an authenticated account owner. It intentionally excludes the
|
||||
// stored public-key document and user identifier.
|
||||
type CredentialSummary struct {
|
||||
ID []byte
|
||||
Label string
|
||||
CreatedAt time.Time
|
||||
LastUsedAt time.Time
|
||||
}
|
||||
|
||||
type EnrollmentToken struct {
|
||||
Digest [32]byte
|
||||
UserID string
|
||||
CreatedAt time.Time
|
||||
ExpiresAt time.Time
|
||||
}
|
||||
|
||||
type Ceremony struct {
|
||||
Digest [32]byte
|
||||
Kind string
|
||||
UserID string
|
||||
Label string
|
||||
SessionData json.RawMessage
|
||||
BindingDigest [32]byte
|
||||
CreatedAt time.Time
|
||||
ExpiresAt time.Time
|
||||
}
|
||||
|
||||
type BeginResult struct {
|
||||
CeremonyToken string `json:"ceremony_token"`
|
||||
PublicKey json.RawMessage `json:"public_key"`
|
||||
ExpiresAt time.Time `json:"expires_at"`
|
||||
}
|
||||
|
||||
type Authentication struct {
|
||||
SessionToken string
|
||||
Principal auth.Principal
|
||||
CredentialID []byte
|
||||
CloneWarning bool
|
||||
}
|
||||
|
||||
type Approval struct {
|
||||
User auth.User
|
||||
CredentialID []byte
|
||||
BindingDigest [32]byte
|
||||
CloneWarning bool
|
||||
ApprovedAt time.Time
|
||||
}
|
||||
|
||||
// Repository persists passkey-specific state. Implementations must consume
|
||||
// enrollment tokens and ceremonies atomically and must perform recovery and
|
||||
// credential removal invariants in transactions.
|
||||
type Repository interface {
|
||||
CreatePasskeyUser(context.Context, auth.User, EnrollmentToken, auth.AuditEvent) error
|
||||
UserByID(context.Context, string) (auth.User, error)
|
||||
UserByIdentifier(context.Context, string) (auth.User, error)
|
||||
UserByCredentialID(context.Context, []byte) (auth.User, error)
|
||||
CredentialsByUserID(context.Context, string) ([]Credential, error)
|
||||
SaveCredential(context.Context, Credential, auth.AuditEvent) error
|
||||
UpdateCredential(context.Context, Credential) error
|
||||
DeleteCredential(context.Context, string, []byte, int, auth.AuditEvent) error
|
||||
CredentialCount(context.Context, string) (int, error)
|
||||
CreateCeremony(context.Context, Ceremony) error
|
||||
TakeCeremony(context.Context, [32]byte, time.Time) (Ceremony, error)
|
||||
ConsumeEnrollmentToken(context.Context, [32]byte, time.Time) (auth.User, error)
|
||||
RecoverUser(context.Context, string, EnrollmentToken, auth.AuditEvent) (auth.User, error)
|
||||
}
|
||||
|
||||
func BindingDigest(value []byte) [32]byte { return sha256.Sum256(value) }
|
||||
Reference in New Issue
Block a user