package auth import ( "crypto/rand" "crypto/sha256" "encoding/base64" "encoding/hex" "sync" "time" ) const tokenBytes = 32 type SessionStore struct { mutex sync.Mutex ttl time.Duration expirations map[string]time.Time } func NewSessionStore(ttl time.Duration) *SessionStore { return &SessionStore{ttl: ttl, expirations: make(map[string]time.Time)} } func (store *SessionStore) Create() (string, error) { buffer := make([]byte, tokenBytes) if _, err := rand.Read(buffer); err != nil { return "", err } token := base64.RawURLEncoding.EncodeToString(buffer) store.mutex.Lock() defer store.mutex.Unlock() now := time.Now() store.removeExpired(now) store.expirations[hashToken(token)] = now.Add(store.ttl) return token, nil } func (store *SessionStore) Valid(token string) bool { if token == "" { return false } key := hashToken(token) store.mutex.Lock() defer store.mutex.Unlock() expiresAt, found := store.expirations[key] if !found { return false } if time.Now().After(expiresAt) { delete(store.expirations, key) return false } return true } func (store *SessionStore) Delete(token string) { store.mutex.Lock() defer store.mutex.Unlock() delete(store.expirations, hashToken(token)) } func (store *SessionStore) removeExpired(now time.Time) { for key, expiresAt := range store.expirations { if now.After(expiresAt) { delete(store.expirations, key) } } } func hashToken(token string) string { sum := sha256.Sum256([]byte(token)) return hex.EncodeToString(sum[:]) }