@@ -24,7 +24,6 @@ import (
24
24
25
25
"firebase.google.com/go/internal"
26
26
"google.golang.org/api/identitytoolkit/v3"
27
- "google.golang.org/api/transport"
28
27
)
29
28
30
29
const (
@@ -42,8 +41,10 @@ var reservedClaims = []string{
42
41
// Client facilitates generating custom JWT tokens for Firebase clients, and verifying ID tokens issued
43
42
// by Firebase backend services.
44
43
type Client struct {
45
- is * identitytoolkit.Service
44
+ is * identitytoolkit.Service
45
+ userManagementClient
46
46
idTokenVerifier * tokenVerifier
47
+ cookieVerifier * tokenVerifier
47
48
signer cryptoSigner
48
49
version string
49
50
clock internal.Clock
@@ -84,12 +85,12 @@ func NewClient(ctx context.Context, conf *internal.AuthConfig) (*Client, error)
84
85
}
85
86
}
86
87
87
- hc , _ , err := transport .NewHTTPClient (ctx , conf .Opts ... )
88
+ hc , _ , err := internal .NewHTTPClient (ctx , conf .Opts ... )
88
89
if err != nil {
89
90
return nil , err
90
91
}
91
92
92
- is , err := identitytoolkit .New (hc )
93
+ is , err := identitytoolkit .New (hc . Client )
93
94
if err != nil {
94
95
return nil , err
95
96
}
@@ -98,11 +99,25 @@ func NewClient(ctx context.Context, conf *internal.AuthConfig) (*Client, error)
98
99
if err != nil {
99
100
return nil , err
100
101
}
102
+
103
+ cookieVerifier , err := newSessionCookieVerifier (ctx , conf .ProjectID )
104
+ if err != nil {
105
+ return nil , err
106
+ }
107
+
108
+ version := "Go/Admin/" + conf .Version
101
109
return & Client {
110
+ userManagementClient : userManagementClient {
111
+ baseURL : idToolkitEndpoint ,
112
+ projectID : conf .ProjectID ,
113
+ version : version ,
114
+ httpClient : hc ,
115
+ },
102
116
is : is ,
103
117
idTokenVerifier : idTokenVerifier ,
118
+ cookieVerifier : cookieVerifier ,
104
119
signer : signer ,
105
- version : "Go/Admin/" + conf . Version ,
120
+ version : version , // This can be removed when userManagementClient implements all user mgt APIs.
106
121
clock : internal .SystemClock ,
107
122
}, nil
108
123
}
@@ -216,13 +231,61 @@ func (c *Client) VerifyIDTokenAndCheckRevoked(ctx context.Context, idToken strin
216
231
return nil , err
217
232
}
218
233
219
- user , err := c .GetUser (ctx , p . UID )
234
+ revoked , err := c .checkRevoked (ctx , p )
220
235
if err != nil {
221
236
return nil , err
222
237
}
223
-
224
- if p .IssuedAt * 1000 < user .TokensValidAfterMillis {
238
+ if revoked {
225
239
return nil , internal .Error (idTokenRevoked , "ID token has been revoked" )
226
240
}
227
241
return p , nil
228
242
}
243
+
244
+ // VerifySessionCookie verifies the signature and payload of the provided Firebase session cookie.
245
+ //
246
+ // VerifySessionCookie accepts a signed JWT token string, and verifies that it is current, issued for the
247
+ // correct Firebase project, and signed by the Google Firebase services in the cloud. It returns a Token containing the
248
+ // decoded claims in the input JWT. See https://firebase.google.com/docs/auth/admin/manage-cookies for more details on
249
+ // how to obtain a session cookie.
250
+ //
251
+ // This function does not make any RPC calls most of the time. The only time it makes an RPC call
252
+ // is when Google public keys need to be refreshed. These keys get cached up to 24 hours, and
253
+ // therefore the RPC overhead gets amortized over many invocations of this function.
254
+ //
255
+ // This does not check whether or not the cookie has been revoked. Use `VerifySessionCookieAndCheckRevoked()`
256
+ // when a revocation check is needed.
257
+ func (c * Client ) VerifySessionCookie (ctx context.Context , sessionCookie string ) (* Token , error ) {
258
+ return c .cookieVerifier .VerifyToken (ctx , sessionCookie )
259
+ }
260
+
261
+ // VerifySessionCookieAndCheckRevoked verifies the provided session cookie, and additionally checks that the
262
+ // cookie has not been revoked.
263
+ //
264
+ // This function uses `VerifySessionCookie()` internally to verify the cookie JWT. However, unlike
265
+ // `VerifySessionCookie()` this function must make an RPC call to perform the revocation check.
266
+ // Developers are advised to take this additional overhead into consideration when including this
267
+ // function in an authorization flow that gets executed often.
268
+ func (c * Client ) VerifySessionCookieAndCheckRevoked (ctx context.Context , sessionCookie string ) (* Token , error ) {
269
+ p , err := c .VerifySessionCookie (ctx , sessionCookie )
270
+ if err != nil {
271
+ return nil , err
272
+ }
273
+
274
+ revoked , err := c .checkRevoked (ctx , p )
275
+ if err != nil {
276
+ return nil , err
277
+ }
278
+ if revoked {
279
+ return nil , internal .Error (sessionCookieRevoked , "session cookie has been revoked" )
280
+ }
281
+ return p , nil
282
+ }
283
+
284
+ func (c * Client ) checkRevoked (ctx context.Context , token * Token ) (bool , error ) {
285
+ user , err := c .GetUser (ctx , token .UID )
286
+ if err != nil {
287
+ return false , err
288
+ }
289
+
290
+ return token .IssuedAt * 1000 < user .TokensValidAfterMillis , nil
291
+ }
0 commit comments