@@ -21,6 +21,7 @@ import (
21
21
"errors"
22
22
"fmt"
23
23
"strings"
24
+ "time"
24
25
25
26
"firebase.google.com/go/internal"
26
27
"google.golang.org/api/transport"
@@ -41,12 +42,10 @@ var reservedClaims = []string{
41
42
// Client facilitates generating custom JWT tokens for Firebase clients, and verifying ID tokens issued
42
43
// by Firebase backend services.
43
44
type Client struct {
44
- * userManagementClient
45
- * providerConfigClient
46
- idTokenVerifier * tokenVerifier
47
- cookieVerifier * tokenVerifier
48
- signer cryptoSigner
49
- clock internal.Clock
45
+ * baseClient
46
+ TenantManager * TenantManager
47
+ signer cryptoSigner
48
+ clock internal.Clock
50
49
}
51
50
52
51
// NewClient creates a new instance of the Firebase Auth Client.
@@ -99,13 +98,17 @@ func NewClient(ctx context.Context, conf *internal.AuthConfig) (*Client, error)
99
98
return nil , err
100
99
}
101
100
102
- return & Client {
101
+ base := & baseClient {
103
102
userManagementClient : newUserManagementClient (hc , conf ),
104
103
providerConfigClient : newProviderConfigClient (hc , conf ),
105
104
idTokenVerifier : idTokenVerifier ,
106
105
cookieVerifier : cookieVerifier ,
107
- signer : signer ,
108
- clock : internal .SystemClock ,
106
+ }
107
+ return & Client {
108
+ baseClient : base ,
109
+ signer : signer ,
110
+ clock : internal .SystemClock ,
111
+ TenantManager : newTenantManager (hc , conf , base ),
109
112
}, nil
110
113
}
111
114
@@ -172,21 +175,62 @@ func (c *Client) CustomTokenWithClaims(ctx context.Context, uid string, devClaim
172
175
return info .Token (ctx , c .signer )
173
176
}
174
177
178
+ // SessionCookie creates a new Firebase session cookie from the given ID token and expiry
179
+ // duration. The returned JWT can be set as a server-side session cookie with a custom cookie
180
+ // policy. Expiry duration must be at least 5 minutes but may not exceed 14 days.
181
+ func (c * Client ) SessionCookie (
182
+ ctx context.Context ,
183
+ idToken string ,
184
+ expiresIn time.Duration ,
185
+ ) (string , error ) {
186
+ return c .baseClient .userManagementClient .createSessionCookie (ctx , idToken , expiresIn )
187
+ }
188
+
175
189
// Token represents a decoded Firebase ID token.
176
190
//
177
191
// Token provides typed accessors to the common JWT fields such as Audience (aud) and Expiry (exp).
178
192
// Additionally it provides a UID field, which indicates the user ID of the account to which this token
179
193
// belongs. Any additional JWT claims can be accessed via the Claims map of Token.
180
194
type Token struct {
195
+ AuthTime int64 `json:"auth_time"`
181
196
Issuer string `json:"iss"`
182
197
Audience string `json:"aud"`
183
198
Expires int64 `json:"exp"`
184
199
IssuedAt int64 `json:"iat"`
185
200
Subject string `json:"sub,omitempty"`
186
201
UID string `json:"uid,omitempty"`
202
+ Firebase FirebaseInfo `json:"firebase"`
187
203
Claims map [string ]interface {} `json:"-"`
188
204
}
189
205
206
+ // FirebaseInfo represents the information about the sign-in event, including which auth provider
207
+ // was used and provider-specific identity details.
208
+ //
209
+ // This data is provided by the Firebase Auth service and is a reserved claim in the ID token.
210
+ type FirebaseInfo struct {
211
+ SignInProvider string `json:"sign_in_provider"`
212
+ Tenant string `json:"tenant"`
213
+ Identities map [string ]interface {} `json:"identities"`
214
+ }
215
+
216
+ type baseClient struct {
217
+ * userManagementClient
218
+ * providerConfigClient
219
+ idTokenVerifier * tokenVerifier
220
+ cookieVerifier * tokenVerifier
221
+ tenantID string
222
+ }
223
+
224
+ func (c * baseClient ) withTenantID (tenantID string ) * baseClient {
225
+ return & baseClient {
226
+ userManagementClient : c .userManagementClient .withTenantID (tenantID ),
227
+ providerConfigClient : c .providerConfigClient .withTenantID (tenantID ),
228
+ idTokenVerifier : c .idTokenVerifier ,
229
+ cookieVerifier : c .cookieVerifier ,
230
+ tenantID : tenantID ,
231
+ }
232
+ }
233
+
190
234
// VerifyIDToken verifies the signature and payload of the provided ID token.
191
235
//
192
236
// VerifyIDToken accepts a signed JWT token string, and verifies that it is current, issued for the
@@ -201,8 +245,13 @@ type Token struct {
201
245
//
202
246
// This does not check whether or not the token has been revoked. Use `VerifyIDTokenAndCheckRevoked()`
203
247
// when a revocation check is needed.
204
- func (c * Client ) VerifyIDToken (ctx context.Context , idToken string ) (* Token , error ) {
205
- return c .idTokenVerifier .VerifyToken (ctx , idToken )
248
+ func (c * baseClient ) VerifyIDToken (ctx context.Context , idToken string ) (* Token , error ) {
249
+ decoded , err := c .idTokenVerifier .VerifyToken (ctx , idToken )
250
+ if err == nil && c .tenantID != "" && c .tenantID != decoded .Firebase .Tenant {
251
+ return nil , internal .Errorf (tenantIDMismatch , "invalid tenant id: %q" , decoded .Firebase .Tenant )
252
+ }
253
+
254
+ return decoded , err
206
255
}
207
256
208
257
// VerifyIDTokenAndCheckRevoked verifies the provided ID token, and additionally checks that the
@@ -212,20 +261,20 @@ func (c *Client) VerifyIDToken(ctx context.Context, idToken string) (*Token, err
212
261
// `VerifyIDToken()` this function must make an RPC call to perform the revocation check.
213
262
// Developers are advised to take this additional overhead into consideration when including this
214
263
// function in an authorization flow that gets executed often.
215
- func (c * Client ) VerifyIDTokenAndCheckRevoked (ctx context.Context , idToken string ) (* Token , error ) {
216
- p , err := c .VerifyIDToken (ctx , idToken )
264
+ func (c * baseClient ) VerifyIDTokenAndCheckRevoked (ctx context.Context , idToken string ) (* Token , error ) {
265
+ decoded , err := c .VerifyIDToken (ctx , idToken )
217
266
if err != nil {
218
267
return nil , err
219
268
}
220
269
221
- revoked , err := c .checkRevoked (ctx , p )
270
+ revoked , err := c .checkRevoked (ctx , decoded )
222
271
if err != nil {
223
272
return nil , err
224
273
}
225
274
if revoked {
226
275
return nil , internal .Error (idTokenRevoked , "ID token has been revoked" )
227
276
}
228
- return p , nil
277
+ return decoded , nil
229
278
}
230
279
231
280
// VerifySessionCookie verifies the signature and payload of the provided Firebase session cookie.
@@ -253,22 +302,22 @@ func (c *Client) VerifySessionCookie(ctx context.Context, sessionCookie string)
253
302
// Developers are advised to take this additional overhead into consideration when including this
254
303
// function in an authorization flow that gets executed often.
255
304
func (c * Client ) VerifySessionCookieAndCheckRevoked (ctx context.Context , sessionCookie string ) (* Token , error ) {
256
- p , err := c .VerifySessionCookie (ctx , sessionCookie )
305
+ decoded , err := c .VerifySessionCookie (ctx , sessionCookie )
257
306
if err != nil {
258
307
return nil , err
259
308
}
260
309
261
- revoked , err := c .checkRevoked (ctx , p )
310
+ revoked , err := c .checkRevoked (ctx , decoded )
262
311
if err != nil {
263
312
return nil , err
264
313
}
265
314
if revoked {
266
315
return nil , internal .Error (sessionCookieRevoked , "session cookie has been revoked" )
267
316
}
268
- return p , nil
317
+ return decoded , nil
269
318
}
270
319
271
- func (c * Client ) checkRevoked (ctx context.Context , token * Token ) (bool , error ) {
320
+ func (c * baseClient ) checkRevoked (ctx context.Context , token * Token ) (bool , error ) {
272
321
user , err := c .GetUser (ctx , token .UID )
273
322
if err != nil {
274
323
return false , err
0 commit comments