auth

package
v0.14.6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 21, 2026 License: MIT Imports: 18 Imported by: 0

Documentation

Overview

Package auth implements credential primitives: password hashing and verification (argon2id, bcrypt, legacy rustdesk-api md5), client JWTs, opaque session tokens, TOTP, recovery codes and login rate limiting.

Index

Constants

View Source
const (
	APITokenPrefix = "rdp_"

	// APITokenLen is the length of a well-formed API token.
	APITokenLen = len(APITokenPrefix) + apiTokenChars
	// APITokenDisplayLen is how many leading characters are kept for display.
	APITokenDisplayLen = len(APITokenPrefix) + 6
)

Personal API tokens: "rdp_" followed by apiTokenChars base62 characters (~238 bits of entropy).

View Source
const MaxPasswordBytes = 1024

MaxPasswordBytes bounds password input to keep hashing cost predictable. It is far above anything a human types; there is no 32-char cap (legacy bug).

View Source
const RecoveryCodeCount = 10

RecoveryCodeCount is the number of codes issued per enrolment.

Variables

View Source
var DefaultArgon2Params = Argon2Params{Memory: 64 * 1024, Time: 2, Threads: 2, SaltLen: 16, KeyLen: 32}

DefaultArgon2Params follow the OWASP/RFC 9106 guidance for interactive logins on a small server (64 MiB, 2 passes, 2 lanes).

View Source
var DefaultBackoffConfig = BackoffConfig{Free: 3, Base: time.Second, Max: 15 * time.Minute, Forget: time.Hour}

DefaultBackoffConfig: 3 free attempts, then 1s, 2s, 4s ... capped at 15 min.

View Source
var ErrEmptyPassword = errors.New("auth: empty password")

ErrEmptyPassword is returned by Hash for an empty password.

View Source
var ErrInvalidToken = errors.New("auth: invalid token")

ErrInvalidToken is returned for any malformed, mis-signed or expired token.

View Source
var ErrPasswordTooLong = errors.New("auth: password too long")

ErrPasswordTooLong is returned by Hash for inputs above MaxPasswordBytes.

View Source
var ErrTOTPInvalid = errors.New("auth: invalid TOTP code")

ErrTOTPInvalid is returned for a wrong, malformed or replayed code.

Functions

func BearerToken

func BearerToken(header string) (string, bool)

BearerToken extracts the token from an Authorization header value. The scheme must be "Bearer" (case-insensitive) followed by a single space and a non-empty token; anything else yields "", false.

func HashRecoveryCode

func HashRecoveryCode(code string) []byte

HashRecoveryCode returns the digest stored in recovery_codes.code_sha256.

func IsAPIToken added in v0.4.0

func IsAPIToken(s string) bool

IsAPIToken reports whether s is a well-formed personal API token.

func NormalizeRecoveryCode

func NormalizeRecoveryCode(code string) string

NormalizeRecoveryCode lowercases and strips separators/whitespace so users may type codes with or without dashes.

func TOTPCode

func TOTPCode(secret string, t time.Time) (string, error)

TOTPCode returns the code for secret at t (used by tests and tooling).

func ValidateTOTP

func ValidateTOTP(secret, code string, now time.Time, lastStep int64) (int64, error)

ValidateTOTP checks code against secret at time now, allowing ±1 step of clock skew. lastStep is the last step accepted for this user (0 if none); a code whose step is <= lastStep is rejected as a replay. On success the matched step is returned and must be persisted as the new lastStep.

Types

type Algo

type Algo string

Algo identifies a stored password hash format. Values match users.password_algo.

const (
	AlgoNone      Algo = ""
	AlgoArgon2id  Algo = "argon2id"
	AlgoBcrypt    Algo = "bcrypt"
	AlgoLegacyMD5 Algo = "legacy_md5_rustdesk_api"
)

Supported password hash formats.

func DetectAlgo

func DetectAlgo(hash string) Algo

DetectAlgo sniffs the format of a stored hash.

type Argon2Params

type Argon2Params struct {
	Memory  uint32 // KiB
	Time    uint32
	Threads uint8
	SaltLen uint32
	KeyLen  uint32
}

Argon2Params are the argon2id cost parameters for new hashes.

type Backoff

type Backoff struct {
	// contains filtered or unexported fields
}

Backoff is an in-memory Limiter with per-key exponential backoff.

func NewBackoff

func NewBackoff(cfg BackoffConfig, now func() time.Time) *Backoff

NewBackoff returns an in-memory exponential backoff limiter. now may be nil.

func (*Backoff) Allow

func (b *Backoff) Allow(key string) (bool, time.Duration)

Allow implements Limiter.

func (*Backoff) Failure

func (b *Backoff) Failure(key string)

Failure implements Limiter.

func (*Backoff) Len

func (b *Backoff) Len() int

Len returns the number of tracked keys.

func (*Backoff) Prune

func (b *Backoff) Prune()

Prune removes keys whose history is older than Forget.

func (*Backoff) RunPruner

func (b *Backoff) RunPruner(ctx context.Context, interval time.Duration)

RunPruner calls Prune every interval until ctx is done.

func (*Backoff) Success

func (b *Backoff) Success(key string)

Success implements Limiter.

type BackoffConfig

type BackoffConfig struct {
	// Free is the number of failures tolerated before any delay applies.
	Free int
	// Base is the delay after the first failure beyond Free; it doubles with
	// every further failure.
	Base time.Duration
	// Max caps the delay.
	Max time.Duration
	// Forget drops a key's history after this long without failures.
	Forget time.Duration
}

BackoffConfig parameterises Backoff.

type ClientClaims

type ClientClaims struct {
	UserID uint32 `json:"user_id"`
	jwt.RegisteredClaims
}

ClientClaims are the claims of a RustDesk client token. The JSON shape is exactly {"user_id": <u32>, "exp": <unix seconds>}, matching legacy rustdesk-api and the Rust `Claims { user_id: u32, exp: usize }` that hbbs verifies with `jsonwebtoken` (HS256, exp required) when MUST_LOGIN is on.

func ParseUnverified

func ParseUnverified(token string) (ClientClaims, error)

ParseUnverified extracts claims without checking the signature or expiry. Only for the legacy importer (classifying old tokens); never for auth.

type ClientJWT

type ClientJWT struct {
	// contains filtered or unexported fields
}

ClientJWT mints and verifies client JWTs with the shared HS256 key (RUSTDESK_API_JWT_KEY, raw bytes).

func NewClientJWT

func NewClientJWT(key []byte, now func() time.Time) (*ClientJWT, error)

NewClientJWT returns a ClientJWT for key. now may be nil (time.Now).

func (*ClientJWT) Mint

func (c *ClientJWT) Mint(userID uint32, exp time.Time) (string, error)

Mint returns a token for userID expiring at exp (truncated to seconds). The header is the jsonwebtoken/golang-jwt default {"alg":"HS256","typ":"JWT"}.

func (*ClientJWT) Verify

func (c *ClientJWT) Verify(token string) (ClientClaims, error)

Verify checks signature (HS256 only), presence and validity of exp, and a non-zero user_id. It does not consult the database; callers must also check the auth_tokens row (revocation, sliding expiry).

type Hasher

type Hasher struct {
	Params Argon2Params
}

Hasher hashes and verifies passwords.

func NewHasher

func NewHasher() *Hasher

NewHasher returns a Hasher using DefaultArgon2Params.

func (*Hasher) Hash

func (h *Hasher) Hash(password string) (string, Algo, error)

Hash returns an argon2id PHC string ($argon2id$v=19$m=..,t=..,p=..$salt$key).

func (*Hasher) NeedsRehash

func (h *Hasher) NeedsRehash(hash string) bool

NeedsRehash reports whether a (successfully verified) hash should be replaced by a fresh argon2id hash with the current parameters.

func (*Hasher) Verify

func (h *Hasher) Verify(hash, password string) bool

Verify checks password against a stored hash of any supported format. An empty or unrecognised hash never matches, and an empty password never matches.

type Limiter

type Limiter interface {
	// Allow reports whether an attempt for key may proceed now. When it may
	// not, retryAfter is how long the caller must wait.
	Allow(key string) (ok bool, retryAfter time.Duration)
	// Failure records a failed attempt, extending the backoff for key.
	Failure(key string)
	// Success clears the failure history for key.
	Success(key string)
}

Limiter throttles repeated failures per key (e.g. "ip:1.2.3.4", "user:alice"). Implementations must be safe for concurrent use; the in-memory Backoff can later be swapped for a shared store.

type OpaqueToken

type OpaqueToken struct {
	// Plain is handed to the client exactly once (cookie, URL, ...).
	Plain string
	// Hash is sha256(Plain), the only form persisted.
	Hash []byte
}

OpaqueToken is a freshly generated bearer secret and its storage digest.

func NewAPIToken added in v0.4.0

func NewAPIToken() (OpaqueToken, error)

NewAPIToken generates a personal API token and its sha256 digest.

func NewOpaqueToken

func NewOpaqueToken() (OpaqueToken, error)

NewOpaqueToken generates a random 256-bit token (unpadded base64url) for panel sessions, CSRF secrets, share links and OAuth states.

type RecoveryCode

type RecoveryCode struct {
	// Plain is shown to the user once, formatted "xxxx-xxxx-xxxx-xxxx".
	Plain string
	// Hash is sha256 of the normalised code (80 bits of entropy, so a fast
	// hash is sufficient).
	Hash []byte
}

RecoveryCode is a one-time 2FA bypass code and its storage digest.

func GenerateRecoveryCodes

func GenerateRecoveryCodes(n int) ([]RecoveryCode, error)

GenerateRecoveryCodes returns n fresh recovery codes.

type TOTPKey

type TOTPKey struct {
	// Secret is the base32 seed; store it AEAD-encrypted.
	Secret string
	// URL is the otpauth:// URI to render as a QR code.
	URL string
}

TOTPKey is a newly generated TOTP secret for enrolment.

func GenerateTOTP

func GenerateTOTP(issuer, account string) (TOTPKey, error)

GenerateTOTP creates a new TOTP seed for account (usually the username).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL