-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathauth.go
134 lines (123 loc) · 3.78 KB
/
auth.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
package firetils
import (
"context"
"errors"
"fmt"
"net/http"
"strings"
fauth "firebase.google.com/go/v4/auth"
"github.com/treeder/gotils/v2"
)
type contextKey string
const (
TokenContextKey = contextKey("token")
UserIDContextKey = contextKey("user_id")
)
var (
InvalidToken = errors.New("Invalid auth token")
NoToken = errors.New("No auth token provided")
AuthClient *fauth.Client
)
// Authenticate checks the Authorization header for a firebase token
func Authenticate(ctx context.Context, firebaseAuth *fauth.Client, w http.ResponseWriter, r *http.Request, hardVerify bool) (*fauth.Token, error) {
var err error
idToken := r.Header.Get("Authorization")
cookie, _ := r.Cookie("session")
if cookie == nil {
cookie, _ = r.Cookie("__session") // only cookie allowed with firebase hosting: https://stackoverflow.com/a/44935288/105562
}
// gotils.L(ctx).Info().Println("Authorization!!!", idToken, "cookie:", cookie, "--")
if cookie == nil && idToken == "" {
return nil, NoToken
}
sessionCookie := ""
var token *fauth.Token
if idToken != "" {
splitToken := strings.Split(idToken, " ")
if len(splitToken) < 2 {
return nil, InvalidToken
}
authType := splitToken[0]
idToken = splitToken[1]
if authType == "Bearer" {
if hardVerify {
token, err = firebaseAuth.VerifyIDTokenAndCheckRevoked(ctx, idToken)
if err != nil {
// gotils.L(ctx).Error().Println(err)
if err.Error() == "ID token has been revoked" {
// Token is revoked. Inform the user to reauthenticate or signOut() the user.
return nil, errors.New("token has been revoked")
}
return nil, fmt.Errorf("cannot verify token: %w", err)
}
} else {
token, err = firebaseAuth.VerifyIDToken(ctx, idToken)
if err != nil {
return nil, fmt.Errorf("cannot verify token: %w", err)
}
}
return token, nil
} else if authType == "Cookie" {
sessionCookie = idToken
}
}
if cookie != nil {
sessionCookie = cookie.Value
}
if sessionCookie != "" {
if hardVerify {
token, err = firebaseAuth.VerifySessionCookieAndCheckRevoked(ctx, sessionCookie)
} else {
token, err = firebaseAuth.VerifySessionCookie(ctx, sessionCookie)
}
if err != nil {
// gotils.L(ctx).Error().Println(err)
return nil, fmt.Errorf("cannot verify token: %w", err)
}
return token, nil
}
return nil, InvalidToken
}
// FireAuth middleware to guard endpoints
func FireAuth(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token, err := Authenticate(r.Context(), AuthClient, w, r, false)
if err != nil {
gotils.WriteError(w, http.StatusForbidden, err)
return
}
// fmt.Printf("authed %v\n", token.UID)
ctx := r.Context()
ctx = context.WithValue(ctx, TokenContextKey, token)
ctx = context.WithValue(ctx, UserIDContextKey, token.UID)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
// OptionalAuth this won't guard it, but will set the token in the context if it's there. Will not error out if it's not there.
func OptionalAuth(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
token, err := Authenticate(ctx, AuthClient, w, r, false)
if err != nil {
if !errors.Is(err, NoToken) {
gotils.L(ctx).Error().Printf("auth error, but optional so skipping: %v", err)
}
next.ServeHTTP(w, r)
return
}
ctx = context.WithValue(ctx, TokenContextKey, token)
ctx = context.WithValue(ctx, UserIDContextKey, token.UID)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func Token(ctx context.Context) *fauth.Token {
u, _ := ctx.Value(TokenContextKey).(*fauth.Token)
return u
}
func UserID(ctx context.Context) string {
u, _ := ctx.Value(UserIDContextKey).(string)
return u
}
func SetOwned(ctx context.Context, o OwnedI) {
o.SetUserID(UserID(ctx))
}