-
Notifications
You must be signed in to change notification settings - Fork 0
/
firebase.go
155 lines (132 loc) · 3.94 KB
/
firebase.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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package auth
import (
"fmt"
"net/http"
"strings"
"firebase.google.com/go"
"google.golang.org/api/option"
fbauth "firebase.google.com/go/auth"
"github.com/Liquid-Labs/env/go/env"
. "github.com/Liquid-Labs/terror/go/terror"
)
const credsKey string = "FIREBASE_CREDS_FILE"
var fbConfig = &firebase.Config{}
var fbClientOptions option.ClientOption
type FbAuthOracle interface {
// GetFirebaseAuthClient returns the underlying Firebase authentication/authorization client used to verify tokens and retrieve claims from google.
GetFirebaseAuthClient() *fbauth.Client
}
type FbOracle struct {
firebaseAuthClient *fbauth.Client
request *http.Request
token *fbauth.Token
authID string
claims map[string]interface{}
}
func init() {
// We'll need to do a little more here if we ever support other providers.
if env.IsDev() {
localFirebaseCredsFile := env.MustGet(credsKey)
fbClientOptions = option.WithCredentialsFile(localFirebaseCredsFile)
}
}
func (auth *FbOracle) InitFromRequest(r *http.Request) (Terror) {
auth.request = r
var app *firebase.App
var err error
if env.IsDev() {
app, err = firebase.NewApp(r.Context(), fbConfig, fbClientOptions)
} else {
app, err = firebase.NewApp(r.Context(), fbConfig)
}
if err != nil {
return ServerError("Could not access authentication service.", err)
}
authClient, err := app.Auth(r.Context())
auth.firebaseAuthClient = authClient
if err != nil {
return ServerError("Could not access authenticaiton service.", err)
}
authHeader := r.Header.Get("Authorization")
if authHeader == `` {
auth.token = nil
auth.authID = ``
auth.claims = map[string]interface{}{}
return nil
} else {
tokenString := strings.TrimPrefix(authHeader, "Bearer ")
// TODO: use VerifyIDTokenAndCheckRevoked?
token, err := authClient.VerifyIDToken(r.Context(), tokenString)
if err != nil {
return UnprocessableEntityError(fmt.Sprintf(`Could not decode HTTP authorizaiton token.`))
} else {
auth.token = token
auth.authID = token.UID
auth.claims = token.Claims
return nil
}
}
}
func (authOracle *FbOracle) RequireAuthentication() Terror {
if !authOracle.IsRequestAuthenticated() {
return UnauthenticatedError(`Non-Authenticated user cannot requested 'owned' items.`)
}
authID := authOracle.GetAuthID()
if authID == `` {
return ServerError(`Missing authorization ID for authenticated user.`, nil)
}
return nil
}
func (a *FbOracle) IsRequestAuthenticated() (bool) {
return a != nil && a.authID != ``
}
func (a *FbOracle) GetAuthID() (string) {
if a == nil {
return ``
} else { return a.authID }
}
func (a *FbOracle) HasAllClaims(req ...string) (bool) {
claims := a.token.Claims
for _, reqClaim := range req {
claim, ok := claims[reqClaim]
if !ok || !claim.(bool) {
return false
}
}
return true
}
func (a *FbOracle) RequireAllClaims(req ...string) Terror {
passes := a.HasAllClaims(req...)
if !passes {
return ForbiddenError(fmt.Sprintf("Access to resource requires claims '%s'.", strings.Join(req, `', '`)))
} else { return nil }
}
func (a *FbOracle) HasAnyClaim(req ...string) (bool) {
claims := a.token.Claims
for _, reqClaim := range req {
claim, ok := claims[reqClaim]
if ok && claim.(bool) {
return true
}
}
return false
}
func (a *FbOracle) RequireAnyClaim(req ...string) Terror {
passes := a.HasAnyClaim(req...)
if !passes {
return ForbiddenError(fmt.Sprintf("Access to resource requires any claim '%s'.", strings.Join(req, `', '`)))
} else { return nil }
}
func (a *FbOracle) GetClaims() []string {
list := make([]string, 0, len(a.claims))
for claim, ok := range a.claims {
if ok.(bool) { list = append(list, claim) }
}
return list
}
func (a *FbOracle) GetFirebaseAuthClient() (*fbauth.Client) {
return a.firebaseAuthClient
}
func (a *FbOracle) GetRequest() (*http.Request) {
return a.request
}