# modules/auth/passkey

```go
import "apistock.dev/modules/auth/passkey"
```

Package passkey provides passkeys (WebAuthn) for apps: registration and sign-in ceremonies checked against the relying party's ID and allowed origins, credential records to store, and the association files native apps need (ADR-0044). It wraps github.com/go-webauthn/webauthn, so apps never import its types.

A ceremony has two steps. Begin returns options for the client (navigator.credentials.create or get) and a state the app keeps on the server; Finish takes that state and the client's response, and verifies the response.

Stability: pre-1.0 (ADR-0015).

## Constants

### const CeremonyTTL, …

```go
const (
	// CeremonyTTL is how long a started ceremony can be finished.
	CeremonyTTL = 5 * time.Minute
	// UserHandleSize is the size of a user handle, the account identifier
	// stored in passkeys.
	UserHandleSize = 64
)
```


## Variables

### var ErrInvalidConfig, …

```go
var (
	// ErrInvalidConfig reports a configuration that can't be used.
	ErrInvalidConfig = errors.New("passkey: invalid configuration")
	// ErrInvalidResponse reports a response that fails verification: a
	// wrong origin, relying party, challenge or signature, user verification
	// missing, an expired ceremony, or an unknown credential.
	ErrInvalidResponse = errors.New("passkey: invalid passkey response")
	// ErrUnknownCredential is returned by a lookup function for a credential
	// or user handle it doesn't know.
	ErrUnknownCredential = errors.New("passkey: unknown credential")
	// ErrCloneWarning reports an authenticator whose signature counter didn't
	// increase: the passkey may have been copied. FinishLogin returns it with
	// the verified credential.
	ErrCloneWarning = errors.New("passkey: signature counter didn't increase")
)
```

Errors returned by the package. Check them with [errors.Is](https://pkg.go.dev/errors#Is).

## Functions

### func AndroidOrigin

```go
func AndroidOrigin(fp [sha256.Size]byte) string
```

AndroidOrigin returns the origin an Android app signed with the certificate fingerprint fp presents.

### func AppleAppSiteAssociation

```go
func AppleAppSiteAssociation(appIDs []string) []byte
```

AppleAppSiteAssociation returns the apple-app-site-association document that lets iOS apps use passkeys for the relying party, or nil without apps. Serve it at /.well-known/apple-app-site-association with the JSON content type.

### func AssetLinks

```go
func AssetLinks(apps []AndroidApp) []byte
```

AssetLinks returns the Digital Asset Links statements that let Android apps use passkeys for the relying party, or nil without apps. Serve it at /.well-known/assetlinks.json.

### func CheckOrigin

```go
func CheckOrigin(rpID, origin string) error
```

CheckOrigin reports whether origin can use passkeys for rpID: https (or http on localhost), with no path, on rpID or a subdomain of it.

### func FormatFingerprint

```go
func FormatFingerprint(fp [sha256.Size]byte) string
```

FormatFingerprint writes fp as AB:CD:…, the form assetlinks.json uses.

### func NewUserHandle

```go
func NewUserHandle() []byte
```

NewUserHandle returns a random user handle.

### func ParseAppleAppIDs

```go
func ParseAppleAppIDs(spec string) ([]string, error)
```

ParseAppleAppIDs parses comma-separated TEAMID.bundle.id identifiers, such as ABCDE12345.com.example.app.

## Types

### type AndroidApp

```go
type AndroidApp struct {
	// Package is the app's package name (applicationId).
	Package string
	// Fingerprints are the SHA-256 fingerprints of its signing certificates.
	Fingerprints [][sha256.Size]byte
}
```

AndroidApp is an Android app allowed to use passkeys for the relying party.

#### func ParseAndroidApps

```go
func ParseAndroidApps(spec string) ([]AndroidApp, error)
```

ParseAndroidApps parses comma-separated package=fingerprint entries, such as com.example.app=SHA256:AB:CD:…; several fingerprints of one app are joined with +. Fingerprints are 32 bytes in hex, with or without colons and the SHA256: prefix.

### type Ceremony

```go
type Ceremony struct {
	// Options are for the client: pass them to navigator.credentials.create
	// or navigator.credentials.get ({"publicKey": ...}).
	Options json.RawMessage
	// State stays on the server until Finish: it holds the challenge.
	State []byte
}
```

Ceremony is a started ceremony.

### type Config

```go
type Config struct {
	// RPID is the relying party ID: the site's registrable domain, such as
	// example.com, or localhost in development.
	RPID string
	// RPDisplayName names the app in passkey prompts.
	RPDisplayName string
	// Origins are the browser origins allowed to use passkeys, such as
	// https://app.example.com. Each must be https (or http://localhost), on
	// RPID or a subdomain of it.
	Origins []string
	// AppleAppIDs are TEAMID.bundle.id identifiers of iOS apps.
	AppleAppIDs []string
	// AndroidApps are Android apps allowed to use passkeys.
	AndroidApps []AndroidApp
}
```

Config describes the relying party.

### type Credential

```go
type Credential struct {
	// ID is the credential ID, unique across the relying party.
	ID []byte
	// Record is the verified credential record: store it and pass it back
	// unchanged in User.Credentials.
	Record []byte
	// AAGUID identifies the authenticator model.
	AAGUID []byte
	// BackupEligible and BackupState report a passkey that can be, and is,
	// synced between devices.
	BackupEligible bool
	BackupState    bool
	// SignCount is the authenticator's signature counter; synced passkeys
	// report 0.
	SignCount uint32
}
```

Credential is a passkey to store.

### type Service

```go
type Service struct {
	// contains filtered or unexported fields
}
```

Service runs passkey ceremonies. It is safe for concurrent use.

#### func New

```go
func New(cfg Config) (*Service, error)
```

New checks cfg and returns a Service. It returns [ErrInvalidConfig](#ErrInvalidConfig).

#### func (*Service) BeginDiscoverableLogin

```go
func (s *Service) BeginDiscoverableLogin() (Ceremony, error)
```

BeginDiscoverableLogin starts a passwordless sign-in: the client picks a passkey, which names its account.

#### func (*Service) BeginRegistration

```go
func (s *Service) BeginRegistration(u User) (Ceremony, error)
```

BeginRegistration starts adding a passkey to u: a discoverable credential with user verification, attestation none, excluding u's passkeys.

#### func (*Service) BeginUserLogin

```go
func (s *Service) BeginUserLogin(u User) (Ceremony, error)
```

BeginUserLogin starts a sign-in limited to u's passkeys, such as a second factor after a password.

#### func (*Service) Config

```go
func (s *Service) Config() Config
```

Config returns the relying party configuration.

#### func (*Service) FinishLogin

```go
func (s *Service) FinishLogin(state, response []byte, lookup func(userHandle, credentialID []byte) (User, error)) (User, Credential, error)
```

FinishLogin verifies the client's response to a sign-in. lookup returns the account for a user handle and credential ID, or [ErrUnknownCredential](#ErrUnknownCredential); a lookup error other than that is returned as is. It returns the account and the credential with its updated counter and flags, which the app stores. It returns [ErrInvalidResponse](#ErrInvalidResponse), or [ErrCloneWarning](#ErrCloneWarning) with a verified credential whose counter didn't increase.

#### func (*Service) FinishRegistration

```go
func (s *Service) FinishRegistration(u User, state, response []byte) (Credential, error)
```

FinishRegistration verifies the client's response to a registration started for u, and returns the credential to store. It returns [ErrInvalidResponse](#ErrInvalidResponse).

### type User

```go
type User struct {
	// Handle is the account's user handle ([NewUserHandle]), never its
	// database ID.
	Handle []byte
	// Name and DisplayName appear in passkey prompts, such as the email
	// address.
	Name        string
	DisplayName string
	// Credentials are the account's stored passkeys.
	Credentials []Credential
}
```

User is an account taking part in a ceremony.

