-
Notifications
You must be signed in to change notification settings - Fork 350
/
grantcookie.go
128 lines (110 loc) · 3.19 KB
/
grantcookie.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
package auth
import (
"encoding/base64"
"encoding/json"
"net/http"
"time"
"github.com/zalando/skipper/secrets"
"golang.org/x/oauth2"
)
type cookie struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
Expiry time.Time `json:"expiry,omitempty"`
}
func decodeCookie(cookieHeader string, config OAuthConfig) (c *cookie, err error) {
var eb []byte
if eb, err = base64.StdEncoding.DecodeString(cookieHeader); err != nil {
return
}
var encryption secrets.Encryption
if encryption, err = config.Secrets.GetEncrypter(secretsRefreshInternal, config.SecretFile); err != nil {
return
}
var b []byte
if b, err = encryption.Decrypt(eb); err != nil {
return
}
err = json.Unmarshal(b, &c)
return
}
func (c *cookie) isAccessTokenExpired() bool {
now := time.Now()
return now.After(c.Expiry)
}
// extractCookie removes and returns the OAuth Grant token cookie from a HTTP request.
// The function supports multiple cookies with the same name and returns
// the best match (the one that decodes properly).
// The client may send multiple cookies if a parent domain has set a
// cookie of the same name.
// The grant token cookie is extracted so it does not get exposed to untrusted downstream
// services.
func extractCookie(request *http.Request, config OAuthConfig) (cookie *cookie, err error) {
old := request.Cookies()
new := make([]*http.Cookie, 0, len(old))
for i, c := range old {
if c.Name == config.TokenCookieName {
cookie, _ = decodeCookie(c.Value, config)
if cookie != nil {
new = append(new, old[i+1:]...)
break
}
}
new = append(new, c)
}
if cookie != nil {
request.Header.Del("Cookie")
for _, c := range new {
request.AddCookie(c)
}
return cookie, nil
}
return nil, http.ErrNoCookie
}
// createDeleteCookie creates a cookie, which instructs the client to clear the grant
// token cookie when used with a Set-Cookie header.
func createDeleteCookie(config OAuthConfig, host string) *http.Cookie {
return &http.Cookie{
Name: config.TokenCookieName,
Value: "",
Path: "/",
Domain: extractDomainFromHost(host, 1),
MaxAge: -1,
Secure: true,
HttpOnly: true,
}
}
func createCookie(config OAuthConfig, host string, t *oauth2.Token) (*http.Cookie, error) {
c := cookie{
AccessToken: t.AccessToken,
RefreshToken: t.RefreshToken,
Expiry: t.Expiry,
}
b, err := json.Marshal(c)
if err != nil {
return nil, err
}
encryption, err := config.Secrets.GetEncrypter(secretsRefreshInternal, config.SecretFile)
if err != nil {
return nil, err
}
eb, err := encryption.Encrypt(b)
if err != nil {
return nil, err
}
b64 := base64.StdEncoding.EncodeToString(eb)
// The cookie expiry date must not be the same as the access token
// expiry. Otherwise the browser deletes the cookie as soon as the
// access token expires, but _before_ the refresh token has expired.
// Since we don't know the actual refresh token expiry, set it to
// 30 days as a good compromise.
return &http.Cookie{
Name: config.TokenCookieName,
Value: b64,
Path: "/",
Domain: extractDomainFromHost(host, 1),
Expires: t.Expiry.Add(time.Hour * 24 * 30),
Secure: true,
HttpOnly: true,
}, nil
}