package oidcrp import ( "crypto/hmac" "crypto/sha256" "encoding/base64" "encoding/json" "net/http" "time" ) const LoginCookie = "__Host-authsvc_login" const LoginInfo = "tug login v1" const LoginProofLength = 32 const LoginProofInfo = "tug login proof v1" type loginCookie struct { Proof string `json:"p"` Expiry int64 `json:"x"` } func (rp *RP) decodeLogin(name, value string) (loginCookie, bool) { raw, err := rp.login.Open(name, value) if err != nil { return loginCookie{}, false } var c loginCookie if err := json.Unmarshal(raw, &c); err != nil { return loginCookie{}, false } proof, err := base64.RawURLEncoding.Strict().DecodeString(c.Proof) return c, err == nil && len(proof) == LoginProofLength && rp.now().Before(time.UnixMicro(c.Expiry)) } func (rp *RP) LoginProof(r *http.Request, state string) string { name := cookieName(LoginCookie, state) ck, err := r.Cookie(name) if err != nil { return "" } c, ok := rp.decodeLogin(name, ck.Value) if !ok { return "" } return c.Proof } func (rp *RP) ClearLogin(w http.ResponseWriter, state string) { http.SetCookie(w, cookie(cookieName(LoginCookie, state), "", -1)) } func (rp *RP) LoginProofFor(state, browser string) string { mac := hmac.New(sha256.New, rp.proofKey) mac.Write([]byte(state)) mac.Write([]byte{0}) mac.Write([]byte(browser)) return base64.RawURLEncoding.EncodeToString(mac.Sum(nil)) } func (rp *RP) SetLogin(w http.ResponseWriter, r *http.Request, state, proof string, expiry time.Time, evictLive bool) bool { now := rp.now() if limit := now.Add(time.Hour); expiry.After(limit) { expiry = limit } name := cookieName(LoginCookie, state) maxAge := int(expiry.Sub(now) / time.Second) if maxAge <= 0 { http.SetCookie(w, cookie(name, "", -1)) return false } live := liveCookies(w, r, LoginCookie+"_", now, func(c *http.Cookie) (time.Time, bool) { held, ok := rp.decodeLogin(c.Name, c.Value) return time.UnixMicro(held.Expiry), ok }) for evictLive && len(live) >= 4 { http.SetCookie(w, cookie(live[0].name, "", -1)) live = live[1:] } raw, _ := json.Marshal(loginCookie{Proof: proof, Expiry: expiry.UnixMicro()}) http.SetCookie(w, cookie(name, rp.login.Seal(name, raw), maxAge)) return true }