marky

Lightweight bookmarking + plaintext archive
Log | Files | Refs | README | LICENSE

auth.go (1928B)


      1 package internal
      2 
      3 import (
      4 	"crypto/hmac"
      5 	"crypto/sha256"
      6 	"encoding/base64"
      7 	"net/http"
      8 	"os"
      9 	"strconv"
     10 	"strings"
     11 	"time"
     12 )
     13 
     14 var secretKey []byte
     15 
     16 func init() {
     17 	if k := os.Getenv("MARKY_SECRET"); k != "" {
     18 		secretKey = []byte(k)
     19 	} else {
     20 		secretKey = []byte("dev-secret-change-me")
     21 	}
     22 }
     23 
     24 // SetSession sets a signed cookie identifying the user ID.
     25 func SetSession(w http.ResponseWriter, userID int64) {
     26 	exp := time.Now().Add(30 * 24 * time.Hour).Unix()
     27 	payload := strconv.FormatInt(userID, 10) + ":" + strconv.FormatInt(exp, 10)
     28 	sig := sign(payload)
     29 	val := payload + ":" + sig
     30 	http.SetCookie(w, &http.Cookie{
     31 		Name:     "marky_session",
     32 		Value:    base64.StdEncoding.EncodeToString([]byte(val)),
     33 		Path:     "/",
     34 		HttpOnly: true,
     35 		SameSite: http.SameSiteLaxMode,
     36 		MaxAge:   int(30 * 24 * time.Hour / time.Second),
     37 	})
     38 }
     39 
     40 // ClearSession removes the session cookie.
     41 func ClearSession(w http.ResponseWriter) {
     42 	http.SetCookie(w, &http.Cookie{Name: "marky_session", Value: "", Path: "/", MaxAge: -1})
     43 }
     44 
     45 // CurrentUserID extracts the user ID from the signed cookie, if valid.
     46 func CurrentUserID(r *http.Request) (int64, bool) {
     47 	c, err := r.Cookie("marky_session")
     48 	if err != nil || c.Value == "" {
     49 		return 0, false
     50 	}
     51 	raw, err := base64.StdEncoding.DecodeString(c.Value)
     52 	if err != nil {
     53 		return 0, false
     54 	}
     55 	parts := strings.Split(string(raw), ":")
     56 	if len(parts) != 3 {
     57 		return 0, false
     58 	}
     59 	payload := parts[0] + ":" + parts[1]
     60 	if !hmac.Equal([]byte(sign(payload)), []byte(parts[2])) {
     61 		return 0, false
     62 	}
     63 	exp, err := strconv.ParseInt(parts[1], 10, 64)
     64 	if err != nil || exp < time.Now().Unix() {
     65 		return 0, false
     66 	}
     67 	uid, err := strconv.ParseInt(parts[0], 10, 64)
     68 	if err != nil || uid <= 0 {
     69 		return 0, false
     70 	}
     71 	return uid, true
     72 }
     73 
     74 func sign(s string) string {
     75 	mac := hmac.New(sha256.New, secretKey)
     76 	mac.Write([]byte(s))
     77 	sum := mac.Sum(nil)
     78 	return base64.StdEncoding.EncodeToString(sum[:16])
     79 }