This repository has been archived by the owner on Oct 9, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 63
/
Copy pathcookie.go
140 lines (123 loc) · 4.17 KB
/
cookie.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 auth
import (
"context"
"crypto/sha256"
"encoding/hex"
"math/rand"
"net/http"
"net/url"
"time"
"github.com/gorilla/securecookie"
"github.com/lyft/flyteadmin/pkg/auth/interfaces"
"github.com/lyft/flytestdlib/errors"
"github.com/lyft/flytestdlib/logger"
)
const (
// #nosec
accessTokenCookieName = "flyte_jwt"
// #nosec
refreshTokenCookieName = "flyte_refresh"
// #nosec
csrfStateCookieName = "flyte_csrf_state"
// #nosec
redirectURLCookieName = "flyte_redirect_location"
)
const (
ErrSecureCookie errors.ErrorCode = "SECURE_COOKIE_ERROR"
// #nosec
ErrInvalidCsrfToken errors.ErrorCode = "CSRF_TOKEN_VALIDATION_FAILED"
)
var AllowedChars = []rune("abcdefghijklmnopqrstuvwxyz1234567890")
func HashCsrfState(csrf string) string {
shaBytes := sha256.Sum256([]byte(csrf))
hash := hex.EncodeToString(shaBytes[:])
return hash
}
func NewSecureCookie(cookieName, value string, hashKey, blockKey []byte) (http.Cookie, error) {
var s = securecookie.New(hashKey, blockKey)
encoded, err := s.Encode(cookieName, value)
if err == nil {
return http.Cookie{
Name: cookieName,
Value: encoded,
}, nil
}
return http.Cookie{}, errors.Wrapf(ErrSecureCookie, err, "Error creating secure cookie")
}
func ReadSecureCookie(ctx context.Context, cookie http.Cookie, hashKey, blockKey []byte) (string, error) {
var s = securecookie.New(hashKey, blockKey)
var value string
var err error
if err = s.Decode(cookie.Name, cookie.Value, &value); err == nil {
return value, nil
}
logger.Errorf(ctx, "Error reading secure cookie %s %s", cookie.Name, err)
return "", errors.Wrapf(ErrSecureCookie, err, "Error reading secure cookie %s", cookie.Name)
}
func NewCsrfToken(seed int64) string {
rand.Seed(seed)
csrfToken := [10]rune{}
for i := 0; i < len(csrfToken); i++ {
csrfToken[i] = AllowedChars[rand.Intn(len(AllowedChars))]
}
return string(csrfToken[:])
}
func NewCsrfCookie() http.Cookie {
csrfStateToken := NewCsrfToken(time.Now().UnixNano())
return http.Cookie{
Name: csrfStateCookieName,
Value: csrfStateToken,
SameSite: http.SameSiteLaxMode,
HttpOnly: true,
}
}
func VerifyCsrfCookie(ctx context.Context, request *http.Request) error {
csrfState := request.FormValue(CsrfFormKey)
if csrfState == "" {
return errors.Errorf(ErrInvalidCsrfToken, "Empty state in callback, %s", request.Form)
}
csrfCookie, err := request.Cookie(csrfStateCookieName)
if csrfCookie == nil || err != nil {
return errors.Errorf(ErrInvalidCsrfToken, "Could not find csrf cookie %s", err)
}
if HashCsrfState(csrfCookie.Value) != csrfState {
return errors.Errorf(ErrInvalidCsrfToken, "CSRF token does not match state %s, %s vs %s", csrfCookie.Value,
HashCsrfState(csrfCookie.Value), csrfState)
}
return nil
}
// This function takes in a string and returns a cookie that's used to keep track of where to send the user after
// the OAuth2 login flow is complete.
func NewRedirectCookie(ctx context.Context, redirectURL string) *http.Cookie {
urlObj, err := url.Parse(redirectURL)
if err != nil || urlObj == nil {
logger.Errorf(ctx, "Error creating redirect cookie %s %s", urlObj, err)
return nil
}
if urlObj.EscapedPath() == "" {
logger.Errorf(ctx, "Error parsing URL, redirect %s resolved to empty string", redirectURL)
return nil
}
return &http.Cookie{
Name: redirectURLCookieName,
Value: urlObj.String(),
SameSite: http.SameSiteLaxMode,
HttpOnly: true,
}
}
// At the end of the OAuth flow, the server needs to send the user somewhere. This should have been stored as a cookie
// during the initial /login call. If that cookie is missing from the request, it will default to the one configured
// in this package's Config object.
func getAuthFlowEndRedirect(ctx context.Context, authContext interfaces.AuthenticationContext, request *http.Request) string {
queryParams := request.URL.Query()
// Use the redirect URL specified in the request if one is available.
if redirectURL := queryParams.Get(RedirectURLParameter); len(redirectURL) > 0 {
return redirectURL
}
cookie, err := request.Cookie(redirectURLCookieName)
if err != nil {
logger.Debugf(ctx, "Could not detect end-of-flow redirect url cookie")
return authContext.Options().RedirectURL
}
return cookie.Value
}