-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathcookies.go
140 lines (124 loc) · 3.63 KB
/
cookies.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package abclientstate
import (
"net/http"
"time"
"github.com/friendsofgo/errors"
"github.com/gorilla/securecookie"
"github.com/volatiletech/authboss/v3"
)
var (
defaultCookieList = []string{authboss.CookieRemember}
)
// CookieStorer writes and reads cookies to an underlying
// gorilla secure cookie storage.
//
// Because it embeds the SecureCookie piece this can be used
// as the cookie storage for your entire application (rather than
// only as a stub for authboss).
type CookieStorer struct {
Cookies []string
*securecookie.SecureCookie
// Defaults empty
Domain string
// Defaults to /
Path string
// Defaults to 1 month
MaxAge int
// Defaults to true
HTTPOnly bool
// Defaults to true
Secure bool
// Samesite defaults to 0 or "off"
SameSite http.SameSite
}
// NewCookieStorer constructor simply wraps the constructor for
// securecookie.New. The parameters are the hash key and the block key.
//
// The hash key is required to authenticate the cookie with HMAC (32 or 64 bytes)
//
// The block key is optional to encrypt the cookie value (set to nil to disable encryption)
// For AES (the default encryption algorithm) 16, 24, or 32 byte keys select AES-128,
// AES-192, AES-256
// respectively.
//
// Ensure you verify the security options for the cookie on the CookieStorer.
//
// This documentation was copied from securecookie.New and is prone to doc-rot. Please
// consult the documentation there too.
func NewCookieStorer(hashKey, blockKey []byte) CookieStorer {
return NewCookieStorerFromExisting(securecookie.New(hashKey, blockKey))
}
// NewCookieStorerFromExisting takes a preconfigured
// secure cookie instance and simply uses it.
//
// Ensure you verify the additional security options for the cookie on the
// CookieStorer. This method creates a cookie storer with the options tuned
// for high security by default.
func NewCookieStorerFromExisting(storage *securecookie.SecureCookie) CookieStorer {
return CookieStorer{
Cookies: defaultCookieList,
SecureCookie: storage,
Path: "/",
// 1 month in seconds
MaxAge: int((time.Hour * 730) / time.Second),
HTTPOnly: true,
Secure: true,
}
}
// ReadState from the request
func (c CookieStorer) ReadState(r *http.Request) (authboss.ClientState, error) {
cs := make(CookieState)
for _, cookie := range r.Cookies() {
for _, n := range c.Cookies {
if n == cookie.Name {
var str string
if err := c.SecureCookie.Decode(n, cookie.Value, &str); err != nil {
if e, ok := err.(securecookie.Error); ok {
// Ignore bad cookies, this means that the client
// may have bad cookies for a long time, but they should
// eventually be overwritten by the application.
if e.IsDecode() {
continue
}
}
return nil, err
}
cs[n] = str
}
}
}
return cs, nil
}
// WriteState to the responsewriter
func (c CookieStorer) WriteState(w http.ResponseWriter, state authboss.ClientState, ev []authboss.ClientStateEvent) error {
for _, ev := range ev {
switch ev.Kind {
case authboss.ClientStateEventPut:
encoded, err := c.SecureCookie.Encode(ev.Key, ev.Value)
if err != nil {
return errors.Wrap(err, "failed to encode cookie")
}
cookie := &http.Cookie{
Expires: time.Now().UTC().AddDate(1, 0, 0),
Name: ev.Key,
Value: encoded,
Domain: c.Domain,
Path: c.Path,
MaxAge: c.MaxAge,
HttpOnly: c.HTTPOnly,
Secure: c.Secure,
SameSite: c.SameSite,
}
http.SetCookie(w, cookie)
case authboss.ClientStateEventDel:
cookie := &http.Cookie{
MaxAge: -1,
Name: ev.Key,
Domain: c.Domain,
Path: c.Path,
}
http.SetCookie(w, cookie)
}
}
return nil
}