package security import ( "crypto/rand" "crypto/sha256" "encoding/base64" "fmt" ) const sessionTokenBytes = 32 // NewSessionToken returns a fresh opaque session token and its storage hash. The token is 32 random // bytes rendered as base64url and is the only secret that goes to the browser (inside the __Host- // cookie). The database stores only HashSessionToken(token), so a read of the sessions table never // yields a usable token and a stolen backup cannot be replayed as a live session. func NewSessionToken() (token string, tokenHash []byte, err error) { raw := make([]byte, sessionTokenBytes) if _, err := rand.Read(raw); err != nil { return "", nil, fmt.Errorf("security: read session token: %w", err) } token = base64.RawURLEncoding.EncodeToString(raw) return token, HashSessionToken(token), nil } // HashSessionToken returns the SHA 256 hash of a token, the form stored and looked up in the // database. func HashSessionToken(token string) []byte { sum := sha256.Sum256([]byte(token)) return sum[:] }