Documentation
¶
Overview ¶
Package devicekey is the client side of AuthKit's device-key protocol, for CLIs and machines. A machine holds an Ed25519 key, enrolls it once with a code emailed to the account (plus the account's second factor, when it has one), then signs in with it for short access tokens. There is no refresh token: signing a fresh challenge is the refresh.
A device key signs domain || 0x00 || challenge, where challenge is the server's raw 32-byte challenge. The domain separates enrollment from login, so neither signature can be replayed as the other.
The package depends only on the standard library and iam.
Index ¶
- Constants
- func Message(domain string, challenge []byte) []byte
- func SignCapability(key crypto.Signer, deviceKeyID string, c Capability) (string, error)
- func SignEnrollment(key crypto.Signer, challenge string) (string, error)
- func SignLogin(key crypto.Signer, challenge string) (string, error)
- type Capability
- type Client
- func (c *Client) BeginEnrollment(ctx context.Context, email string, key ed25519.PublicKey, label string) (Enrollment, error)
- func (c *Client) FinishEnrollment(ctx context.Context, e Enrollment, key crypto.Signer, ...) (Session, error)
- func (c *Client) List(ctx context.Context, token string) ([]iam.DeviceKey, error)
- func (c *Client) Login(ctx context.Context, id string, key crypto.Signer) (Session, error)
- func (c *Client) Logout(ctx context.Context, token string) error
- func (c *Client) Revoke(ctx context.Context, token, id string) error
- func (c *Client) RevokeOthers(ctx context.Context, token string) error
- type Enrollment
- type SecondFactorRequired
- type Session
Constants ¶
const ( // CapabilityType is a capability's JOSE typ. CapabilityType = "authkit-capability+jwt" // CapabilityClaim is the jwt-bearer assertion claim that carries one. CapabilityClaim = "capability" // MaxCapabilityLifetime bounds a capability's ExpiresAt from now. MaxCapabilityLifetime = 24 * time.Hour )
Capabilities: a device key signs, offline, what a workload may do for its user. The workload embeds the capability in its jwt-bearer assertion (claim CapabilityClaim) and gets an access token for Audience that carries exactly those operations until the capability expires. AuthKit verifies the signature, that the key is live and the user's, and that the workload proves the key the capability names.
const ( EnrollmentDomain = "authkit.device-key-enrollment/1" LoginDomain = "authkit.device-key-login/1" )
Signing domains. AuthKit verifies with these same constants.
Variables ¶
This section is empty.
Functions ¶
func SignCapability ¶ added in v1.10.0
SignCapability signs c with the Ed25519 key enrolled as deviceKeyID and returns the compact JWT.
func SignEnrollment ¶
SignEnrollment signs an enrollment challenge as it arrives on the wire (base64url) and returns the base64url signature the finish request carries.
Types ¶
type Capability ¶ added in v1.10.0
type Capability struct {
// UserID (sub) is the device key's user.
UserID string
// Audience (aud) is the resource server's identifier, as the
// authorization server declares it.
Audience string
// WorkloadThumbprint (cnf.jkt) is the RFC 7638 thumbprint of the
// workload's P-256 key, the key its assertion and DPoP proofs use.
WorkloadThumbprint string
// AuthorizationDetails are the operations: an RFC 9396 JSON array whose
// types the client declares.
AuthorizationDetails json.RawMessage
// ID (jti) redeems once; "" makes a random one.
ID string
// IssuedAt (iat) is zero for now.
IssuedAt time.Time
// ExpiresAt (exp) is required, at most MaxCapabilityLifetime ahead.
ExpiresAt time.Time
// Claims are other claims, such as the host's run id.
Claims map[string]any
}
Capability is what a device key lets a workload do for its user.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client speaks the device-key protocol to one AuthKit mount. A refusal is an iam.Error decoded from AuthKit's error envelope (iam.AsError, errors.Is against the iam sentinels); a device-key route answers 404 with no code when the host has not enabled device keys.
func NewClient ¶
NewClient returns a Client for the AuthKit JSON API at baseURL: the mount's origin and API prefix, such as "https://example.com/api/v1". A nil hc uses http.DefaultClient.
func (*Client) BeginEnrollment ¶
func (c *Client) BeginEnrollment(ctx context.Context, email string, key ed25519.PublicKey, label string) (Enrollment, error)
BeginEnrollment asks AuthKit to email a code to email for enrolling key. label names the machine to the account owner (at most 128 bytes). A new address creates the account where registration is open.
func (*Client) FinishEnrollment ¶
func (c *Client) FinishEnrollment(ctx context.Context, e Enrollment, key crypto.Signer, code, secondFactor string) (Session, error)
FinishEnrollment proves the emailed code and possession of key (the enrollment's key), enrolls it and signs it in. secondFactor is "" until a *SecondFactorRequired asks for one. The session's token also proves the account's email, which RevokeOthers requires: re-enrolling a key already enrolled on the account is how a machine obtains that proof.
func (*Client) List ¶
List returns the account's live device keys with a device-key access token: the device keys of GET /me/sign-in-keys. That view has no public key or revocation time, so PublicKey and RevokedAt are unset.
func (*Client) Logout ¶ added in v0.149.0
Logout signs the machine out: it revokes the token's own key. It is retry-safe.
type Enrollment ¶
type Enrollment struct {
ID string
Challenge string
PublicKey ed25519.PublicKey
ExpiresAt time.Time
}
Enrollment is a pending enrollment: BeginEnrollment's answer, finished with the emailed code before ExpiresAt. It holds no secret and may be persisted between the two calls.
type SecondFactorRequired ¶
type SecondFactorRequired struct {
Method string
// contains filtered or unexported fields
}
SecondFactorRequired is FinishEnrollment's answer when the account has a second factor: retry with the same enrollment and emailed code plus that factor's code. Method is "totp", "sms" (AuthKit has just sent the code) or "backup_code"; the account's email factor never counts, since it reads the mailbox the enrollment code went to. It wraps the decoded iam.Error.
func (*SecondFactorRequired) Error ¶
func (e *SecondFactorRequired) Error() string
func (*SecondFactorRequired) Unwrap ¶
func (e *SecondFactorRequired) Unwrap() error