@@ -21,6 +21,7 @@ import (
2121 "errors"
2222 "fmt"
2323 "strings"
24+ "time"
2425
2526 "firebase.google.com/go/internal"
2627 "google.golang.org/api/transport"
@@ -41,12 +42,10 @@ var reservedClaims = []string{
4142// Client facilitates generating custom JWT tokens for Firebase clients, and verifying ID tokens issued
4243// by Firebase backend services.
4344type 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
5049}
5150
5251// NewClient creates a new instance of the Firebase Auth Client.
@@ -99,13 +98,17 @@ func NewClient(ctx context.Context, conf *internal.AuthConfig) (*Client, error)
9998 return nil , err
10099 }
101100
102- return & Client {
101+ base := & baseClient {
103102 userManagementClient : newUserManagementClient (hc , conf ),
104103 providerConfigClient : newProviderConfigClient (hc , conf ),
105104 idTokenVerifier : idTokenVerifier ,
106105 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 ),
109112 }, nil
110113}
111114
@@ -172,21 +175,62 @@ func (c *Client) CustomTokenWithClaims(ctx context.Context, uid string, devClaim
172175 return info .Token (ctx , c .signer )
173176}
174177
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+
175189// Token represents a decoded Firebase ID token.
176190//
177191// Token provides typed accessors to the common JWT fields such as Audience (aud) and Expiry (exp).
178192// Additionally it provides a UID field, which indicates the user ID of the account to which this token
179193// belongs. Any additional JWT claims can be accessed via the Claims map of Token.
180194type Token struct {
195+ AuthTime int64 `json:"auth_time"`
181196 Issuer string `json:"iss"`
182197 Audience string `json:"aud"`
183198 Expires int64 `json:"exp"`
184199 IssuedAt int64 `json:"iat"`
185200 Subject string `json:"sub,omitempty"`
186201 UID string `json:"uid,omitempty"`
202+ Firebase FirebaseInfo `json:"firebase"`
187203 Claims map [string ]interface {} `json:"-"`
188204}
189205
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+
190234// VerifyIDToken verifies the signature and payload of the provided ID token.
191235//
192236// VerifyIDToken accepts a signed JWT token string, and verifies that it is current, issued for the
@@ -201,8 +245,13 @@ type Token struct {
201245//
202246// This does not check whether or not the token has been revoked. Use `VerifyIDTokenAndCheckRevoked()`
203247// 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
206255}
207256
208257// 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
212261// `VerifyIDToken()` this function must make an RPC call to perform the revocation check.
213262// Developers are advised to take this additional overhead into consideration when including this
214263// 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 )
217266 if err != nil {
218267 return nil , err
219268 }
220269
221- revoked , err := c .checkRevoked (ctx , p )
270+ revoked , err := c .checkRevoked (ctx , decoded )
222271 if err != nil {
223272 return nil , err
224273 }
225274 if revoked {
226275 return nil , internal .Error (idTokenRevoked , "ID token has been revoked" )
227276 }
228- return p , nil
277+ return decoded , nil
229278}
230279
231280// 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)
253302// Developers are advised to take this additional overhead into consideration when including this
254303// function in an authorization flow that gets executed often.
255304func (c * Client ) VerifySessionCookieAndCheckRevoked (ctx context.Context , sessionCookie string ) (* Token , error ) {
256- p , err := c .VerifySessionCookie (ctx , sessionCookie )
305+ decoded , err := c .VerifySessionCookie (ctx , sessionCookie )
257306 if err != nil {
258307 return nil , err
259308 }
260309
261- revoked , err := c .checkRevoked (ctx , p )
310+ revoked , err := c .checkRevoked (ctx , decoded )
262311 if err != nil {
263312 return nil , err
264313 }
265314 if revoked {
266315 return nil , internal .Error (sessionCookieRevoked , "session cookie has been revoked" )
267316 }
268- return p , nil
317+ return decoded , nil
269318}
270319
271- func (c * Client ) checkRevoked (ctx context.Context , token * Token ) (bool , error ) {
320+ func (c * baseClient ) checkRevoked (ctx context.Context , token * Token ) (bool , error ) {
272321 user , err := c .GetUser (ctx , token .UID )
273322 if err != nil {
274323 return false , err
0 commit comments