package auth import ( "crypto/rand" "crypto/sha256" "time" "gardomatic.kleiax.de/internal/platform/validate" ) const ( // ScopeActivation identifies account activation tokens. ScopeActivation = "activation" // ScopeAuthentication identifies bearer authentication tokens. ScopeAuthentication = "authentication" // ScopePasswordReset identifies password reset tokens. ScopePasswordReset = "password-reset" ) // Token carries a one-time plaintext token and the hash persisted by storage. type Token struct { Plaintext string `json:"token"` Hash []byte `json:"-"` UserID int `json:"-"` Expiry time.Time `json:"expiry"` Scope string `json:"-"` } // NewToken creates a cryptographically random token for a user and scope. func NewToken(userID int, ttl time.Duration, scope string) Token { token := Token{ Plaintext: rand.Text(), UserID: userID, Expiry: time.Now().Add(ttl), Scope: scope, } hash := sha256.Sum256([]byte(token.Plaintext)) token.Hash = hash[:] return token } // Validate adds token consistency errors to v. func (tk Token) Validate(v *validate.Validator) { ValidateTokenPlaintext(v, tk.Plaintext) } // ValidateTokenPlaintext checks the expected format of a user-supplied token. func ValidateTokenPlaintext(v *validate.Validator, tokenPlaintext string) { v.Check(tokenPlaintext != "", "token", "must be provided") v.Check(len(tokenPlaintext) == 26, "token", "must be 26 bytes long") }