Files
upstream-mirror c60cc7ca75
Some checks failed
CI / Run tests and lints (push) Failing after 29s
CI / Build CLI docs (push) Has been skipped
CI / Run Playwright end-to-end tests (mysql) (push) Failing after 3s
CI / Run Playwright end-to-end tests (postgres) (push) Failing after 2s
CI / Run Playwright end-to-end tests (sqlite) (push) Failing after 3s
CodeQL / Analyze (go) (push) Failing after 4s
CodeQL / Analyze (javascript) (push) Failing after 3s
Docker Image Scanners / scanners (push) Failing after 4s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Run end-to-end tests (postgres) (push) Failing after 19s
CI / Run end-to-end tests (sqlite) (push) Failing after 2s
CI / Run Playwright end-to-end tests (cockroach) (push) Failing after 2s
CI / Draft newsletter (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
snapshot of https://github.com/ory/kratos.git (master, 2026-09-15; blobs > 131072B omitted)
2026-09-15 21:19:12 +05:00

199 lines
8.7 KiB
Go

// Copyright © 2026 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package deviceauthn
import (
"time"
)
// The valid values (Android, iOS) are pinned as an enum via the OpenAPI patch
// in .schema/openapi/patches/schema.yaml. (Detached from the doc comment to
// stay out of the generated API description.)
// DeviceType is the platform the key was enrolled on.
//
// "Android" and "iOS" use different attestation and signature encodings.
type DeviceType string
const (
DeviceTypeAndroid DeviceType = "Android"
DeviceTypeIOS DeviceType = "iOS"
)
// The valid values (initial, confirmed, locked) are pinned as an enum via the
// OpenAPI patch in .schema/openapi/patches/schema.yaml. (Detached from the doc
// comment to stay out of the generated API description.)
// KeyState is the lifecycle state of an enrolled key.
//
// Only a "confirmed" key can be used to authenticate. A "locked" key was
// disabled after too many wrong-PIN attempts; rotating its pin_secret unlocks
// it.
type KeyState string
const (
KeyStateInitial KeyState = "initial"
KeyStateConfirmed KeyState = "confirmed"
// KeyStateLocked marks a PIN key disabled after too many wrong-PIN attempts.
KeyStateLocked KeyState = "locked"
)
// The schema name stays UpperCamelCase on purpose: it is part of the published
// DeviceAuthn schema family (DeviceAuthnKey, DeviceType, KeyState, PINConfig),
// which all use UpperCamelCase. Renaming it to the lowerCamelCase the core
// naming convention prefers would be a breaking change to the public SDK and
// would split the family's casing, so the deliberate exception is kept.
// The valid values (pin, platform, none) are pinned as an enum via the OpenAPI
// patch in .schema/openapi/patches/schema.yaml. (Detached from the doc comment
// to stay out of the generated API description.)
// UserVerification records how the key's holder is verified at use time.
//
// "pin" means the holder proves knowledge of a PIN via pin_proof at login;
// "platform" means the device gates key use behind platform biometrics or an
// equivalent lock screen; "none" marks a possession-only key, usable as a
// second factor but never as the sole first factor. The empty value marks a
// legacy key from before user verification existed; such keys are rejected at
// login and must be re-enrolled.
type UserVerification string
const (
UserVerificationPIN UserVerification = "pin"
UserVerificationPlatform UserVerification = "platform"
// UserVerificationNone marks a possession-only key: usable as a second
// factor (step-up) but never as a first factor, which login enforces.
// Unlike JWT's alg=none the value strictly reduces what the key may do —
// it bypasses no verification. It covers keys deliberately enrolled
// without PIN or platform verification.
UserVerificationNone UserVerification = "none"
)
// PINConfig holds only scalar fields, so it is safe to shallow copy; the pointer
// on Key.PIN encodes optionality (nil means no PIN), not copy safety.
//
// The schema name stays UpperCamelCase: it belongs to the published DeviceAuthn
// schema family (DeviceAuthnKey, DeviceType, KeyState, UserVerification), and
// renaming it would break the public SDK.
// (Detached from the doc comment to stay out of the generated API description.)
// PINConfig is the per-key PIN state.
//
// The pin_secret field holds only the at-rest ciphertext; the plaintext
// exists transiently in server memory during verification and is invalidated
// once the key locks.
type PINConfig struct {
// pin_secret holds ciphertext of the kratos cipher (hex) and must never be
// returned to or logged by clients; the OpenAPI patch in
// .schema/openapi/patches/schema.yaml marks it write-only.
// (Detached from the doc comment to stay out of the API description.)
// The at-rest ciphertext of the pin_secret. It never leaves the server and
// is cleared once the key locks.
PINSecret string `json:"pin_secret"`
// A negative stored value must be a decode error instead of silently
// extending the attempt budget, hence uint. (Detached from the doc comment
// to stay out of the generated API description.)
// The number of consecutive wrong-PIN attempts so far; the key locks when
// it reaches the configured maximum (pin_max_attempts, default 5).
FailedAttempts uint `json:"failed_attempts"`
// When the pin_secret was first issued.
CreatedAt time.Time `json:"created_at"`
// When the pin_secret was last rotated. Omitted if the secret was never
// rotated.
RotatedAt time.Time `json:"rotated_at,omitzero"`
}
// Key is safe to shallow copy as long as the shared reference fields (PublicKey,
// PIN, Attestation, RelaxedAttestationExpiresAt) are treated as read-only:
// in-place mutation goes through a *Key into the backing slice, and the merge
// copies PIN rather than aliasing it. Kept out of the doc comment so it stays
// out of the generated API description.
// An enrolled device authentication (DeviceAuthn) key.
//
// Represents a hardware-backed signing key enrolled from a mobile device.
// The private key resides inside the device and never exists on the server.
//
// To list the identity's enrolled keys, fetch a settings flow: each key's
// remove button (a `ui.nodes` entry named `deviceauthn_remove` in group
// `deviceauthn`) carries the key, with its PIN state redacted, in the node
// label's `context`.
//
// swagger:model DeviceAuthnKey
type Key struct {
// We intentionally avoid storing the cryptographic algorithm a la JWT/TLS to
// avoid security issues and algorithm negotiation; a future v2 may use
// ML-DSA (post-quantum) once Android/iOS support it. (Detached from the doc
// comment to stay out of the generated API description.)
// The cryptography version of the key. Version 1 uses ECDSA with SHA-256
// on an elliptic curve (P-224, P-256, P-384, or P-521); further versions
// are reserved for future signature suites.
Version int `json:"version"`
// A human-readable name for the device, helping the user tell this key
// apart from others.
DeviceName string `json:"device_name"`
// The device's public key (an elliptic-curve key on P-224, P-256, P-384,
// or P-521 in version 1) in PKIX, ASN.1 DER (SubjectPublicKeyInfo) form,
// base64-encoded. Signatures are verified against this key.
//
// swagger:strfmt byte
PublicKey []byte `json:"public_key,omitempty"`
// The key's stable id, unique per identity. Submit it as the
// `client_key_id` when logging in with the key, deleting it, or rotating
// its pin_secret.
//
// The device can also compute the id without reading it back from the
// server: it is the lowercase-hex SHA-256 of `public_key` (the key's
// PKIX, ASN.1 DER encoding). Keys enrolled before the server derived the
// id keep their original client-chosen value, so prefer reading this
// field over recomputing it for older keys.
ClientKeyID string `json:"client_key_id"`
// When the key was enrolled. Only used for troubleshooting and UI.
CreatedAt time.Time `json:"created_at"`
// The platform the key was enrolled on, "Android" or "iOS". The two
// platforms use different attestation and signature encodings.
DeviceType DeviceType `json:"device_type"`
// Auto-confirmation vs. an admin confirmation call (for out-of-band KYC,
// e.g. a physical letter with a code) is not configurable yet; keys are
// auto-confirmed at creation time. (Detached from the doc comment to stay
// out of the generated API description.)
// The lifecycle state of the key. Only a "confirmed" key can be used to
// authenticate; a "locked" key was disabled after too many wrong-PIN
// attempts.
State KeyState `json:"state"`
// How the key's holder is verified at use time: "pin", "platform", or
// "none". Empty for legacy keys enrolled before user verification existed;
// such keys are rejected at login and must be re-enrolled.
UserVerification UserVerification `json:"user_verification,omitempty"`
// The per-key PIN state. Absent unless the key is PIN-protected.
PIN *PINConfig `json:"pin,omitempty"`
// The platform-specific attestation captured at enrollment. Useful for
// telling keys apart in the UI (e.g. OS version, manufacturer) and for
// forensics.
Attestation *Attestation `json:"attestation,omitempty"`
// Set only when the key's attestation chain was accepted under relaxed
// rules (software roots, expired certificates, software security level)
// rather than strict hardware attestation. Such keys are refused at login
// after this time, or immediately once relaxed attestation is turned off.
// Absent for hardware-attested keys that pass strict validation.
RelaxedAttestationExpiresAt *time.Time `json:"relaxed_attestation_expires_at,omitempty"`
}
// CredentialsDeviceAuthnConfig is the JSON shape stored in
// `identity_credentials.config` for the `deviceauthn` credential type.
type CredentialsDeviceAuthnConfig struct {
Credentials []Key `json:"credentials"`
}