mirror of
https://github.com/juanfont/headscale.git
synced 2026-09-30 11:59:39 +09:00
393dd3e2d9
API keys, pre-auth keys and OAuth clients/tokens share one table and verify path. Secrets carry 256 bits of crypto/rand entropy, so a SHA-256 digest needs no stretching; bcrypt/argon2id rows rehash on use until 0.32.
91 lines
3.0 KiB
Go
91 lines
3.0 KiB
Go
package types
|
|
|
|
import (
|
|
"time"
|
|
)
|
|
|
|
// CredentialKind discriminates rows of the credentials table.
|
|
type CredentialKind string
|
|
|
|
const (
|
|
CredentialAPIKey CredentialKind = "api"
|
|
CredentialPreAuthKey CredentialKind = "authkey"
|
|
CredentialOAuthClient CredentialKind = "oauth_client" //nolint:gosec // discriminator value, not a credential
|
|
CredentialOAuthToken CredentialKind = "oauth_token" //nolint:gosec // discriminator value, not a credential
|
|
)
|
|
|
|
// Credential is the unified storage model for every authenticatable secret:
|
|
// API keys, pre-auth keys, OAuth clients, and OAuth access tokens. Each row is
|
|
// discriminated by [Credential.Kind]. Only a hash of the secret is stored:
|
|
// SHA-256, or a legacy bcrypt/Argon2id hash until the next authentication.
|
|
// Identifier is the public, indexed lookup value — the 12-char prefix for API
|
|
// keys, pre-auth keys and access tokens, and the client id for OAuth clients —
|
|
// and is unique within a kind.
|
|
//
|
|
// Per-kind fields are sparse by design: Reusable/Ephemeral/Used apply to
|
|
// pre-auth keys, LastSeen to API keys, Scopes to OAuth credentials, ClientID
|
|
// links an OAuth token to its issuing client, and Tags to pre-auth keys and
|
|
// OAuth credentials.
|
|
type Credential struct {
|
|
ID uint64 `gorm:"primary_key"`
|
|
Kind CredentialKind `gorm:"not null;index:idx_credentials_identifier,unique,priority:1"`
|
|
Identifier string `gorm:"index:idx_credentials_identifier,unique,priority:2"`
|
|
Hash []byte
|
|
|
|
// UserID records the owning (user-owned pre-auth key, API key) or creating
|
|
// (tagged pre-auth key, OAuth client) user. Deleting the user nulls it.
|
|
UserID *uint
|
|
User *User `gorm:"constraint:OnDelete:SET NULL;"`
|
|
|
|
Description string
|
|
|
|
Scopes []string `gorm:"serializer:json"`
|
|
Tags []string `gorm:"serializer:json"`
|
|
|
|
Reusable bool
|
|
Ephemeral bool `gorm:"default:false"`
|
|
Used bool `gorm:"default:false"`
|
|
|
|
LastSeen *time.Time
|
|
|
|
// ClientID links an OAuth access token (Kind == CredentialOAuthToken) back to
|
|
// the Identifier of its issuing OAuth client.
|
|
ClientID string
|
|
|
|
CreatedAt *time.Time
|
|
Expiration *time.Time
|
|
Revoked *time.Time
|
|
}
|
|
|
|
// IsTagged reports whether this credential carries tags. For a pre-auth key
|
|
// credential, a node registered with it becomes a tagged node.
|
|
func (c *Credential) IsTagged() bool {
|
|
return len(c.Tags) > 0
|
|
}
|
|
|
|
// AsCredential projects a pre-auth key back onto a [Credential] (kind
|
|
// authkey), used to set a node's AuthKey association during registration from a
|
|
// pre-auth key projection.
|
|
func (pak *PreAuthKey) AsCredential() *Credential {
|
|
if pak == nil {
|
|
return nil
|
|
}
|
|
|
|
return &Credential{
|
|
ID: pak.ID,
|
|
Kind: CredentialPreAuthKey,
|
|
Identifier: pak.Prefix,
|
|
Hash: pak.Hash,
|
|
UserID: pak.UserID,
|
|
User: pak.User,
|
|
Description: pak.Description,
|
|
Reusable: pak.Reusable,
|
|
Ephemeral: pak.Ephemeral,
|
|
Used: pak.Used,
|
|
Tags: pak.Tags,
|
|
CreatedAt: pak.CreatedAt,
|
|
Expiration: pak.Expiration,
|
|
Revoked: pak.Revoked,
|
|
}
|
|
}
|