Files
enkly-ai/pkg/authsupport/token.go
enkly 79ce8dc4c0
Some checks failed
Go CI Pipeline / build_and_test (push) Failing after 22m0s
Init Project
2026-10-30 18:06:55 +01:00

59 lines
2.1 KiB
Go

package authsupport
import (
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"fmt"
"math/big"
)
// tokenBytes is the amount of entropy in a raw token before encoding —
// 32 bytes (256 bits) is comfortably enough that guessing is infeasible.
const tokenBytes = 32
// NewToken returns a fresh, URL-safe random token (for password-reset
// links and session cookies) and the hex-encoded SHA-256 hash of it that
// should be persisted in its place. Only the hash ever touches the
// database — the raw token lives solely in the email link or the
// browser's cookie, so a DB dump alone can't be replayed.
func NewToken() (raw string, hash string, err error) {
b := make([]byte, tokenBytes)
if _, err := rand.Read(b); err != nil {
return "", "", fmt.Errorf("auth: generate token: %w", err)
}
raw = base64.RawURLEncoding.EncodeToString(b)
return raw, HashToken(raw), nil
}
// codeSpace is how many distinct 6-digit codes NewCode can produce
// (000000 through 999999).
const codeSpace = 1_000_000
// NewCode returns a fresh random 6-digit numeric code — as a
// zero-padded string, so "007123" round-trips correctly — for emailing
// as an email-verification code, and the hex-encoded SHA-256 hash of it
// that should be persisted in its place. Unlike NewToken's 256 bits of
// entropy, a 6-digit code is deliberately guessable-by-brute-force if
// nothing else stops repeated attempts; callers must pair it with an
// attempt limit and a short expiry (see internal/db.VerifyEmailCode and
// verifyCodeTTL) rather than relying on the code space alone.
func NewCode() (raw string, hash string, err error) {
n, err := rand.Int(rand.Reader, big.NewInt(codeSpace))
if err != nil {
return "", "", fmt.Errorf("auth: generate code: %w", err)
}
raw = fmt.Sprintf("%06d", n.Int64())
return raw, HashToken(raw), nil
}
// HashToken hashes a raw token or code the same way NewToken/NewCode do,
// so a value received back from the client (a reset link's ?token=, a
// session cookie, or a typed verification code) can be looked up by its
// hash.
func HashToken(raw string) string {
sum := sha256.Sum256([]byte(raw))
return hex.EncodeToString(sum[:])
}