Skip to content

Commit

Permalink
Merge dev into master
Browse files Browse the repository at this point in the history
  • Loading branch information
google-oss-bot authored Jun 2, 2021
2 parents 4121c50 + 862fabe commit 928b104
Show file tree
Hide file tree
Showing 15 changed files with 460 additions and 100 deletions.
12 changes: 8 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,24 @@ jobs:
name: Module build
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
go: [1.12, 1.13, 1.14]
go: [1.12, 1.16]

steps:
- name: Set up Go ${{ matrix.go }}
uses: actions/setup-go@v1
with:
go-version: ${{ matrix.go }}

- name: Install golint
run: go get golang.org/x/lint/golint

- name: Check out code
uses: actions/checkout@v2

- name: Run Linter
run: |
go get -u golang.org/x/lint/golint
GOLINT=`go list -f {{.Target}} golang.org/x/lint/golint`
$GOLINT -set_exit_status ./...
Expand All @@ -44,12 +47,13 @@ jobs:
runs-on: ubuntu-latest
env:
GOPATH: ${{ github.workspace }}/go
GO111MODULE: off

steps:
- name: Set up Go 1.12
- name: Set up Go 1.16
uses: actions/setup-go@v1
with:
go-version: 1.12
go-version: 1.16

- name: Check out code into GOPATH
uses: actions/checkout@v2
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,16 @@ jobs:
with:
go-version: 1.12

- name: Install golint
run: go get golang.org/x/lint/golint

- name: Check out code
uses: actions/checkout@v2
with:
ref: ${{ github.event.client_payload.ref || github.ref }}

- name: Run Linter
run: |
go get -u golang.org/x/lint/golint
GOLINT=`go list -f {{.Target}} golang.org/x/lint/golint`
$GOLINT -set_exit_status ./...
Expand Down
113 changes: 63 additions & 50 deletions auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,12 @@ func NewClient(ctx context.Context, conf *internal.AuthConfig) (*Client, error)
}
}

idTokenVerifier, err := newIDTokenVerifier(ctx, conf.ProjectID, isEmulator)
idTokenVerifier, err := newIDTokenVerifier(ctx, conf.ProjectID)
if err != nil {
return nil, err
}

cookieVerifier, err := newSessionCookieVerifier(ctx, conf.ProjectID, isEmulator)
cookieVerifier, err := newSessionCookieVerifier(ctx, conf.ProjectID)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -144,6 +144,7 @@ func NewClient(ctx context.Context, conf *internal.AuthConfig) (*Client, error)
cookieVerifier: cookieVerifier,
signer: signer,
clock: internal.SystemClock,
isEmulator: isEmulator,
}
return &Client{
baseClient: base,
Expand Down Expand Up @@ -265,6 +266,7 @@ type baseClient struct {
cookieVerifier *tokenVerifier
signer cryptoSigner
clock internal.Clock
isEmulator bool
}

func (c *baseClient) withTenantID(tenantID string) *baseClient {
Expand All @@ -281,63 +283,68 @@ func (c *baseClient) withTenantID(tenantID string) *baseClient {
// https://firebase.google.com/docs/auth/admin/verify-id-tokens#retrieve_id_tokens_on_clients for
// more details on how to obtain an ID token in a client app.
//
// This function does not make any RPC calls most of the time. The only time it makes an RPC call
// is when Google public keys need to be refreshed. These keys get cached up to 24 hours, and
// therefore the RPC overhead gets amortized over many invocations of this function.
// In non-emulator mode, this function does not make any RPC calls most of the time.
// The only time it makes an RPC call is when Google public keys need to be refreshed.
// These keys get cached up to 24 hours, and therefore the RPC overhead gets amortized
// over many invocations of this function.
//
// This does not check whether or not the token has been revoked. Use `VerifyIDTokenAndCheckRevoked()`
// when a revocation check is needed.
func (c *baseClient) VerifyIDToken(ctx context.Context, idToken string) (*Token, error) {
decoded, err := c.idTokenVerifier.VerifyToken(ctx, idToken)
if err == nil && c.tenantID != "" && c.tenantID != decoded.Firebase.Tenant {
return nil, &internal.FirebaseError{
ErrorCode: internal.InvalidArgument,
String: fmt.Sprintf("invalid tenant id: %q", decoded.Firebase.Tenant),
Ext: map[string]interface{}{
authErrorCode: tenantIDMismatch,
},
}
}

return decoded, err
}

// IsTenantIDMismatch checks if the given error was due to a mismatched tenant ID in a JWT.
func IsTenantIDMismatch(err error) bool {
return hasAuthErrorCode(err, tenantIDMismatch)
return c.verifyIDToken(ctx, idToken, false)
}

// VerifyIDTokenAndCheckRevoked verifies the provided ID token, and additionally checks that the
// token has not been revoked.
//
// This function uses `VerifyIDToken()` internally to verify the ID token JWT. However, unlike
// `VerifyIDToken()` this function must make an RPC call to perform the revocation check.
// Unlike `VerifyIDToken()`, this function must make an RPC call to perform the revocation check.
// Developers are advised to take this additional overhead into consideration when including this
// function in an authorization flow that gets executed often.
func (c *baseClient) VerifyIDTokenAndCheckRevoked(ctx context.Context, idToken string) (*Token, error) {
decoded, err := c.VerifyIDToken(ctx, idToken)
if err != nil {
return nil, err
}
return c.verifyIDToken(ctx, idToken, true)
}

revoked, err := c.checkRevoked(ctx, decoded)
func (c *baseClient) verifyIDToken(ctx context.Context, idToken string, checkRevoked bool) (*Token, error) {
decoded, err := c.idTokenVerifier.VerifyToken(ctx, idToken, c.isEmulator)
if err != nil {
return nil, err
}

if revoked {
if c.tenantID != "" && c.tenantID != decoded.Firebase.Tenant {
return nil, &internal.FirebaseError{
ErrorCode: internal.InvalidArgument,
String: "ID token has been revoked",
String: fmt.Sprintf("invalid tenant id: %q", decoded.Firebase.Tenant),
Ext: map[string]interface{}{
authErrorCode: idTokenRevoked,
authErrorCode: tenantIDMismatch,
},
}
}

if c.isEmulator || checkRevoked {
revoked, err := c.checkRevoked(ctx, decoded)
if err != nil {
return nil, err
}

if revoked {
return nil, &internal.FirebaseError{
ErrorCode: internal.InvalidArgument,
String: "ID token has been revoked",
Ext: map[string]interface{}{
authErrorCode: idTokenRevoked,
},
}
}
}

return decoded, nil
}

// IsTenantIDMismatch checks if the given error was due to a mismatched tenant ID in a JWT.
func IsTenantIDMismatch(err error) bool {
return hasAuthErrorCode(err, tenantIDMismatch)
}

// IsIDTokenRevoked checks if the given error was due to a revoked ID token.
//
// When IsIDTokenRevoked returns true, IsIDTokenInvalid is guranteed to return true.
Expand All @@ -352,41 +359,47 @@ func IsIDTokenRevoked(err error) bool {
// decoded claims in the input JWT. See https://firebase.google.com/docs/auth/admin/manage-cookies for more details on
// how to obtain a session cookie.
//
// This function does not make any RPC calls most of the time. The only time it makes an RPC call
// is when Google public keys need to be refreshed. These keys get cached up to 24 hours, and
// therefore the RPC overhead gets amortized over many invocations of this function.
// In non-emulator mode, this function does not make any RPC calls most of the time.
// The only time it makes an RPC call is when Google public keys need to be refreshed.
// These keys get cached up to 24 hours, and therefore the RPC overhead gets amortized
// over many invocations of this function.
//
// This does not check whether or not the cookie has been revoked. Use `VerifySessionCookieAndCheckRevoked()`
// when a revocation check is needed.
func (c *Client) VerifySessionCookie(ctx context.Context, sessionCookie string) (*Token, error) {
return c.cookieVerifier.VerifyToken(ctx, sessionCookie)
return c.verifySessionCookie(ctx, sessionCookie, false)
}

// VerifySessionCookieAndCheckRevoked verifies the provided session cookie, and additionally checks that the
// cookie has not been revoked.
//
// This function uses `VerifySessionCookie()` internally to verify the cookie JWT. However, unlike
// `VerifySessionCookie()` this function must make an RPC call to perform the revocation check.
// Unlike `VerifySessionCookie()`, this function must make an RPC call to perform the revocation check.
// Developers are advised to take this additional overhead into consideration when including this
// function in an authorization flow that gets executed often.
func (c *Client) VerifySessionCookieAndCheckRevoked(ctx context.Context, sessionCookie string) (*Token, error) {
decoded, err := c.VerifySessionCookie(ctx, sessionCookie)
if err != nil {
return nil, err
}
return c.verifySessionCookie(ctx, sessionCookie, true)
}

revoked, err := c.checkRevoked(ctx, decoded)
func (c *Client) verifySessionCookie(ctx context.Context, sessionCookie string, checkRevoked bool) (*Token, error) {
decoded, err := c.cookieVerifier.VerifyToken(ctx, sessionCookie, c.isEmulator)
if err != nil {
return nil, err
}

if revoked {
return nil, &internal.FirebaseError{
ErrorCode: internal.InvalidArgument,
String: "session cookie has been revoked",
Ext: map[string]interface{}{
authErrorCode: sessionCookieRevoked,
},
if c.isEmulator || checkRevoked {
revoked, err := c.checkRevoked(ctx, decoded)
if err != nil {
return nil, err
}

if revoked {
return nil, &internal.FirebaseError{
ErrorCode: internal.InvalidArgument,
String: "session cookie has been revoked",
Ext: map[string]interface{}{
authErrorCode: sessionCookieRevoked,
},
}
}
}

Expand Down
Loading

0 comments on commit 928b104

Please sign in to comment.