passkey.go RawHistoryFolder
1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3package domain
4
5import (
6 "fmt"
7 "time"
8)
9
10const (
11 // MaxPasskeys is the most passkeys the owner may register.
12 MaxPasskeys = 20
13 // MaxPasskeyIDLen is the longest credential id, in bytes (the WebAuthn
14 // limit).
15 MaxPasskeyIDLen = 1023
16 // MaxPasskeyNameLen is the longest passkey name, in bytes.
17 MaxPasskeyNameLen = 64
18 // DefaultPasskeyName names a passkey registered without a name.
19 DefaultPasskeyName = "passkey"
20 // PasskeyCeremonyTTL is how long a registration or a sign-in may take, from
21 // its begin to its finish. The WebAuthn adapter enforces it on the
22 // challenge and the use case on the ceremony it keeps.
23 PasskeyCeremonyTTL = 2 * time.Minute
24)
25
26// Passkey is a WebAuthn credential that can sign the owner in. It holds the
27// public key only.
28type Passkey struct {
29 // ID is the credential id: at most MaxPasskeyIDLen bytes, never empty.
30 ID []byte
31 Name string
32 // PublicKey is the COSE public key.
33 PublicKey []byte
34 AttestationType string
35 Transports []string
36 // SignCount is the last counter the authenticator reported; 0 for
37 // authenticators that have none (synced passkeys).
38 SignCount uint32
39 // BackupEligible and BackupState are the WebAuthn flags: the credential
40 // can be backed up, and currently is (a synced passkey).
41 BackupEligible bool
42 BackupState bool
43 CreatedAt time.Time
44 LastUsedAt time.Time // zero until first used
45}
46
47// PasskeyName is a validated passkey name: one line of plain text, 1 to
48// MaxPasskeyNameLen bytes, not blank. Only ParsePasskeyName builds a non-zero
49// value.
50type PasskeyName struct{ s string }
51
52// ParsePasskeyName validates s with the rules of the other one-line texts: valid
53// UTF-8, no control or bidirectional override character, and not blank.
54func ParsePasskeyName(s string) (PasskeyName, error) {
55 if len(s) > MaxPasskeyNameLen {
56 return PasskeyName{}, &TextError{Reason: fmt.Sprintf("is longer than %d bytes (%d)", MaxPasskeyNameLen, len(s))}
57 }
58 if reason := textProblem(s, false, MaxPasskeyNameLen); reason != "" {
59 return PasskeyName{}, &TextError{Reason: reason}
60 }
61 if isBlank(s) {
62 return PasskeyName{}, &TextError{Reason: "is empty"}
63 }
64 return PasskeyName{s: s}, nil
65}
66
67// String returns the name.
68func (n PasskeyName) String() string { return n.s }