Raw
 1package oidcrp
 2
 3import (
 4	"crypto/hmac"
 5	"crypto/sha256"
 6	"encoding/base64"
 7	"encoding/json"
 8	"net/http"
 9	"time"
10)
11
12const LoginCookie = "__Host-authsvc_login"
13const LoginInfo = "tug login v1"
14const LoginProofLength = 32
15const LoginProofInfo = "tug login proof v1"
16
17type loginCookie struct {
18	Proof  string `json:"p"`
19	Expiry int64  `json:"x"`
20}
21
22func (rp *RP) decodeLogin(name, value string) (loginCookie, bool) {
23	raw, err := rp.login.Open(name, value)
24	if err != nil {
25		return loginCookie{}, false
26	}
27	var c loginCookie
28	if err := json.Unmarshal(raw, &c); err != nil {
29		return loginCookie{}, false
30	}
31	proof, err := base64.RawURLEncoding.Strict().DecodeString(c.Proof)
32	return c, err == nil && len(proof) == LoginProofLength && rp.now().Before(time.UnixMicro(c.Expiry))
33}
34
35func (rp *RP) LoginProof(r *http.Request, state string) string {
36	name := cookieName(LoginCookie, state)
37	ck, err := r.Cookie(name)
38	if err != nil {
39		return ""
40	}
41	c, ok := rp.decodeLogin(name, ck.Value)
42	if !ok {
43		return ""
44	}
45	return c.Proof
46}
47
48func (rp *RP) ClearLogin(w http.ResponseWriter, state string) {
49	http.SetCookie(w, cookie(cookieName(LoginCookie, state), "", -1))
50}
51
52func (rp *RP) LoginProofFor(state, browser string) string {
53	mac := hmac.New(sha256.New, rp.proofKey)
54	mac.Write([]byte(state))
55	mac.Write([]byte{0})
56	mac.Write([]byte(browser))
57	return base64.RawURLEncoding.EncodeToString(mac.Sum(nil))
58}
59
60func (rp *RP) SetLogin(w http.ResponseWriter, r *http.Request, state, proof string, expiry time.Time, evictLive bool) bool {
61	now := rp.now()
62	if limit := now.Add(time.Hour); expiry.After(limit) {
63		expiry = limit
64	}
65	name := cookieName(LoginCookie, state)
66	maxAge := int(expiry.Sub(now) / time.Second)
67	if maxAge <= 0 {
68		http.SetCookie(w, cookie(name, "", -1))
69		return false
70	}
71	live := liveCookies(w, r, LoginCookie+"_", now, func(c *http.Cookie) (time.Time, bool) {
72		held, ok := rp.decodeLogin(c.Name, c.Value)
73		return time.UnixMicro(held.Expiry), ok
74	})
75	for evictLive && len(live) >= 4 {
76		http.SetCookie(w, cookie(live[0].name, "", -1))
77		live = live[1:]
78	}
79	raw, _ := json.Marshal(loginCookie{Proof: proof, Expiry: expiry.UnixMicro()})
80	http.SetCookie(w, cookie(name, rp.login.Seal(name, raw), maxAge))
81	return true
82}