-
Notifications
You must be signed in to change notification settings - Fork 251
Expand file tree
/
Copy pathcookies.go
More file actions
138 lines (119 loc) · 4.22 KB
/
Copy pathcookies.go
File metadata and controls
138 lines (119 loc) · 4.22 KB
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
package oauth2
import (
"context"
"net/http"
"time"
)
const (
// DefaultCookieName is the name of the stored cookie
DefaultCookieName = "session"
)
var _ Authenticator = &cookie{}
// cookie represents the location and expiration time of new cookies.
type cookie struct {
Name string // Name is the name of the cookie stored on the browser
Lifespan time.Duration // Lifespan is the expiration date of the cookie. 0 means session cookie
Inactivity time.Duration // Inactivity is the length of time a token is valid if there is no activity
Secure bool // Secure controls whether the cookie is sent only over HTTPS
Now func() time.Time
Tokens Tokenizer
}
// NewCookieJWT creates an Authenticator that uses cookies for auth
func NewCookieJWT(secret string, lifespan, inactivity time.Duration, secure bool) Authenticator {
// Server interprets a token duration longer than the cookie lifespan as
// a token that was issued by a server with a longer auth-duration and is
// thus invalid, as a security precaution. So, inactivity must be set to
// be less than lifespan.
if lifespan > 0 && inactivity > lifespan {
inactivity = lifespan / 2 // half of the lifespan ensures tokens can be refreshed once.
}
return &cookie{
Name: DefaultCookieName,
Lifespan: lifespan,
Inactivity: inactivity,
Secure: secure,
Now: DefaultNowTime,
Tokens: &JWT{
Secret: secret,
Now: DefaultNowTime,
},
}
}
// Validate returns Principal of the Cookie if the Token is valid.
func (c *cookie) Validate(ctx context.Context, r *http.Request) (Principal, error) {
cookie, err := r.Cookie(c.Name)
if err != nil {
return Principal{}, ErrAuthentication
}
return c.Tokens.ValidPrincipal(ctx, Token(cookie.Value), c.Lifespan)
}
// Extend will extend the lifetime of the Token by the Inactivity time. Assumes
// Principal is already valid.
func (c *cookie) Extend(ctx context.Context, w http.ResponseWriter, p Principal) (Principal, error) {
// Refresh the token by extending its life another Inactivity duration
p, err := c.Tokens.ExtendedPrincipal(ctx, p, c.Inactivity)
if err != nil {
return Principal{}, ErrAuthentication
}
// Creating a new token with the extended principal
token, err := c.Tokens.Create(ctx, p)
if err != nil {
return Principal{}, ErrAuthentication
}
// Cookie lifespan can be indirectly figured out by taking the token's
// issued at time and adding the lifespan setting The token's issued at
// time happens to correspond to the cookie's original issued at time.
exp := p.IssuedAt.Add(c.Lifespan)
// Once the token has been extended, write it out as a new cookie.
c.setCookie(w, string(token), exp)
return p, nil
}
// Authorize will create cookies containing token information. It'll create
// a token with cookie.Duration of life to be stored as the cookie's value.
func (c *cookie) Authorize(ctx context.Context, w http.ResponseWriter, p Principal) error {
// Principal will be issued at Now() and will expire
// c.Inactivity into the future
now := c.Now()
p.IssuedAt = now
p.ExpiresAt = now.Add(c.Inactivity)
token, err := c.Tokens.Create(ctx, p)
if err != nil {
return err
}
// The time when the cookie expires
exp := now.Add(c.Lifespan)
c.setCookie(w, string(token), exp)
return nil
}
// setCookie creates a cookie with value expiring at exp and writes it as a cookie into the response
func (c *cookie) setCookie(w http.ResponseWriter, value string, exp time.Time) {
// Cookie has a Token baked into it
cookie := http.Cookie{
Name: DefaultCookieName,
Value: value,
HttpOnly: true,
Path: "/",
SameSite: http.SameSiteStrictMode,
Secure: c.Secure,
}
// Only set a cookie to be persistent (endure beyond the browser session)
// if auth duration is greater than zero
if c.Lifespan > 0 {
cookie.Expires = exp
}
http.SetCookie(w, &cookie)
}
// Expire returns a cookie that will expire an existing cookie
func (c *cookie) Expire(w http.ResponseWriter) {
// to expire cookie set the time in the past
cookie := http.Cookie{
Name: DefaultCookieName,
Value: "none",
HttpOnly: true,
Path: "/",
Expires: c.Now().Add(-1 * time.Hour),
SameSite: http.SameSiteStrictMode,
Secure: c.Secure,
}
http.SetCookie(w, &cookie)
}