Skip to content

Commit afd91cd

Browse files
authored
feat(auth): Implemented tenant management APIs (#311)
* Added Tenant, TenantClient and TenantManager types (#287) * Added user management APIs to the TenantClient (#288) * Added IdP config management APIs to TenantClient (#290) * Added baseClient type (#291) * Exposing ID token verification APIs from TenantClient (#296) * Added JWT verification APIs to TenantClient * Removed SessionCookie() and related APIs from TenantClient * Minor simplification * Added Tenant() and DeleteTenant() APIs (#300) * Added CreateTenant() and UpdateTenant() APIs (#303) * Added Tenants() function to iterate over tenants (#304) * Adding integration tests (#305) * Adding integration tests * Added license header * Removing go1.9 from the CI config; This is already done in the dev branch. Needed to do it here for the CI to pass with the latest deps. * More integration tests for tenant management support (#306) * Added more integration tests * Added SAML and OIDC integration tests * Added ImportUsers integration test * Updated ID token verification test
1 parent 17d8615 commit afd91cd

15 files changed

+2929
-245
lines changed

auth/auth.go

Lines changed: 68 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
4344
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
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.
180194
type 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.
255304
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)
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

Comments
 (0)