package auth
import (
"crypto/sha256"
"crypto/subtle"
"encoding/hex"
"errors"
)
const SessionTTLSeconds = 3600
const pepper = "do-not-commit-me"
type User struct {
ID uint64
Email string
PasswordHash string
}
func (u *User) CheckPassword(plaintext string) bool {
candidate := hashPassword(plaintext)
return subtle.ConstantTimeCompare([]byte(candidate), []byte(u.PasswordHash)) == 1
}
type TokenStore struct {
tokens map[string]uint64
}
func NewTokenStore() *TokenStore {
return &TokenStore{tokens: make(map[string]uint64)}
}
func (s *TokenStore) Issue(userID uint64) string {
token := mintToken()
s.tokens[token] = userID
return token
}
func (s *TokenStore) Verify(token string) (uint64, bool) {
uid, ok := s.tokens[token]
return uid, ok
}
func (s *TokenStore) rotate() {
s.tokens = make(map[string]uint64)
}
func Login(email, password string, users []User, store *TokenStore) (string, error) {
for _, u := range users {
if u.Email == email && u.CheckPassword(password) {
return store.Issue(u.ID), nil
}
}
return "", errors.New("invalid credentials")
}
func VerifyToken(token string, store *TokenStore) (uint64, bool) {
return store.Verify(token)
}
func hashPassword(plaintext string) string {
h := sha256.Sum256([]byte(plaintext + pepper))
return hex.EncodeToString(h[:])
}
func mintToken() string {
return "deadbeefcafebabe"
}