// Copyright (c) 2026 Petr BalvĂ­n (https://petrbalvin.org) // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 // Package preview signs the shareable links that show an unpublished // post on the public API. The signature is an HMAC over the slug and an // expiry stamp, keyed with the admin session key, so a link can be // handed to a reviewer without granting them anything else. package preview import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "strconv" "strings" "time" ) // TTL is how long a preview link stays valid. const TTL = 7 * 24 * time.Hour // Token returns the preview token for slug, or "" when no session key // is configured: without a key the signature could not be verified, so // no link is offered at all. func Token(slug, sessionKey string, now time.Time) string { if slug == "" || sessionKey == "" { return "" } expiry := now.Add(TTL).Unix() return sign(slug, expiry, sessionKey) + "-" + strconv.FormatInt(expiry, 10) } // Valid reports whether token authorises a preview of slug. func Valid(token, slug, sessionKey string, now time.Time) bool { if token == "" || slug == "" || sessionKey == "" { return false } mac, stamp, found := strings.CutLast(token, "-") if !found { return false } expiry, err := strconv.ParseInt(stamp, 10, 64) if err != nil || now.Unix() > expiry { return false } return hmac.Equal([]byte(mac), []byte(sign(slug, expiry, sessionKey))) } func sign(slug string, expiry int64, sessionKey string) string { mac := hmac.New(sha256.New, []byte(sessionKey)) mac.Write([]byte(slug)) mac.Write([]byte("-")) mac.Write([]byte(strconv.FormatInt(expiry, 10))) return hex.EncodeToString(mac.Sum(nil))[:32] }