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 Nov 4, 2021
2 parents 928b104 + 6c768ee commit 02cde4f
Show file tree
Hide file tree
Showing 13 changed files with 253 additions and 60 deletions.
83 changes: 83 additions & 0 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Copyright 2021 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

name: Nightly Builds

on:
# Runs every day at 06:30 AM (PT) and 08:30 PM (PT) / 04:30 AM (UTC) and 02:30 PM (UTC)
# or on 'firebase_nightly_build' repository dispatch event.
schedule:
- cron: "30 4,14 * * *"
repository_dispatch:
types: [firebase_nightly_build]

jobs:
nightly:

runs-on: ubuntu-latest

steps:
- name: Set up Go
uses: actions/setup-go@v1
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: |
GOLINT=`go list -f {{.Target}} golang.org/x/lint/golint`
$GOLINT -set_exit_status ./...
- name: Run Tests
run: ./.github/scripts/run_all_tests.sh
env:
FIREBASE_SERVICE_ACCT_KEY: ${{ secrets.FIREBASE_SERVICE_ACCT_KEY }}
FIREBASE_API_KEY: ${{ secrets.FIREBASE_API_KEY }}

- name: Send email on failure
if: failure()
uses: firebase/firebase-admin-node/.github/actions/send-email@master
with:
api-key: ${{ secrets.OSS_BOT_MAILGUN_KEY }}
domain: ${{ secrets.OSS_BOT_MAILGUN_DOMAIN }}
from: 'GitHub <admin-github@${{ secrets.OSS_BOT_MAILGUN_DOMAIN }}>'
to: ${{ secrets.FIREBASE_ADMIN_GITHUB_EMAIL }}
subject: 'Nightly build ${{github.run_id}} of ${{github.repository}} failed!'
html: >
<b>Nightly workflow ${{github.run_id}} failed on: ${{github.repository}}</b>
<br /><br />Navigate to the
<a href="https://github.com/firebase/firebase-admin-go/actions/runs/${{github.run_id}}">failed workflow</a>.
continue-on-error: true

- name: Send email on cancelled
if: cancelled()
uses: firebase/firebase-admin-node/.github/actions/send-email@master
with:
api-key: ${{ secrets.OSS_BOT_MAILGUN_KEY }}
domain: ${{ secrets.OSS_BOT_MAILGUN_DOMAIN }}
from: 'GitHub <admin-github@${{ secrets.OSS_BOT_MAILGUN_DOMAIN }}>'
to: ${{ secrets.FIREBASE_ADMIN_GITHUB_EMAIL }}
subject: 'Nightly build ${{github.run_id}} of ${{github.repository}} cancelled!'
html: >
<b>Nightly workflow ${{github.run_id}} cancelled on: ${{github.repository}}</b>
<br /><br />Navigate to the
<a href="https://github.com/firebase/firebase-admin-go/actions/runs/${{github.run_id}}">cancelled workflow</a>.
continue-on-error: true
91 changes: 56 additions & 35 deletions auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import (
"time"

"firebase.google.com/go/v4/internal"
"golang.org/x/oauth2"
"google.golang.org/api/option"
"google.golang.org/api/transport"
)

Expand All @@ -37,6 +39,7 @@ const (

// SDK-generated error codes
idTokenRevoked = "ID_TOKEN_REVOKED"
userDisabled = "USER_DISABLED"
sessionCookieRevoked = "SESSION_COOKIE_REVOKED"
tenantIDMismatch = "TENANT_ID_MISMATCH"
)
Expand All @@ -46,6 +49,10 @@ var reservedClaims = []string{
"exp", "firebase", "iat", "iss", "jti", "nbf", "nonce", "sub",
}

var emulatorToken = &oauth2.Token{
AccessToken: "owner",
}

// Client is the interface for the Firebase auth service.
//
// Client facilitates generating custom JWT tokens for Firebase clients, and verifying ID tokens issued
Expand Down Expand Up @@ -113,7 +120,15 @@ func NewClient(ctx context.Context, conf *internal.AuthConfig) (*Client, error)
return nil, err
}

transport, _, err := transport.NewHTTPClient(ctx, conf.Opts...)
var opts []option.ClientOption
if isEmulator {
ts := oauth2.StaticTokenSource(emulatorToken)
opts = append(opts, option.WithTokenSource(ts))
} else {
opts = append(opts, conf.Opts...)
}

transport, _, err := transport.NewHTTPClient(ctx, opts...)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -288,14 +303,14 @@ func (c *baseClient) withTenantID(tenantID string) *baseClient {
// 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()`
// This does not check whether or not the token has been revoked or disabled. Use `VerifyIDTokenAndCheckRevoked()`
// when a revocation check is needed.
func (c *baseClient) VerifyIDToken(ctx context.Context, idToken string) (*Token, error) {
return c.verifyIDToken(ctx, idToken, false)
}

// VerifyIDTokenAndCheckRevoked verifies the provided ID token, and additionally checks that the
// token has not been revoked.
// token has not been revoked or disabled.
//
// 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
Expand All @@ -304,7 +319,7 @@ func (c *baseClient) VerifyIDTokenAndCheckRevoked(ctx context.Context, idToken s
return c.verifyIDToken(ctx, idToken, true)
}

func (c *baseClient) verifyIDToken(ctx context.Context, idToken string, checkRevoked bool) (*Token, error) {
func (c *baseClient) verifyIDToken(ctx context.Context, idToken string, checkRevokedOrDisabled bool) (*Token, error) {
decoded, err := c.idTokenVerifier.VerifyToken(ctx, idToken, c.isEmulator)
if err != nil {
return nil, err
Expand All @@ -320,21 +335,11 @@ func (c *baseClient) verifyIDToken(ctx context.Context, idToken string, checkRev
}
}

if c.isEmulator || checkRevoked {
revoked, err := c.checkRevoked(ctx, decoded)
if c.isEmulator || checkRevokedOrDisabled {
err = c.checkRevokedOrDisabled(ctx, decoded, idTokenRevoked, "ID token has been revoked")
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
Expand All @@ -347,11 +352,18 @@ func IsTenantIDMismatch(err error) bool {

// IsIDTokenRevoked checks if the given error was due to a revoked ID token.
//
// When IsIDTokenRevoked returns true, IsIDTokenInvalid is guranteed to return true.
// When IsIDTokenRevoked returns true, IsIDTokenInvalid is guaranteed to return true.
func IsIDTokenRevoked(err error) bool {
return hasAuthErrorCode(err, idTokenRevoked)
}

// IsUserDisabled checks if the given error was due to a disabled ID token
//
// When IsUserDisabled returns true, IsIDTokenInvalid is guaranteed to return true.
func IsUserDisabled(err error) bool {
return hasAuthErrorCode(err, userDisabled)
}

// VerifySessionCookie verifies the signature and payload of the provided Firebase session cookie.
//
// VerifySessionCookie accepts a signed JWT token string, and verifies that it is current, issued for the
Expand All @@ -371,7 +383,7 @@ func (c *Client) VerifySessionCookie(ctx context.Context, sessionCookie string)
}

// VerifySessionCookieAndCheckRevoked verifies the provided session cookie, and additionally checks that the
// cookie has not been revoked.
// cookie has not been revoked and the user has not been disabled.
//
// 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
Expand All @@ -380,46 +392,55 @@ func (c *Client) VerifySessionCookieAndCheckRevoked(ctx context.Context, session
return c.verifySessionCookie(ctx, sessionCookie, true)
}

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

if c.isEmulator || checkRevoked {
revoked, err := c.checkRevoked(ctx, decoded)
if c.isEmulator || checkRevokedOrDisabled {
err := c.checkRevokedOrDisabled(ctx, decoded, sessionCookieRevoked, "session cookie has been revoked")
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,
},
}
}
}

return decoded, nil
}

// IsSessionCookieRevoked checks if the given error was due to a revoked session cookie.
//
// When IsSessionCookieRevoked returns true, IsSessionCookieInvalid is guranteed to return true.
// When IsSessionCookieRevoked returns true, IsSessionCookieInvalid is guaranteed to return true.
func IsSessionCookieRevoked(err error) bool {
return hasAuthErrorCode(err, sessionCookieRevoked)
}

func (c *baseClient) checkRevoked(ctx context.Context, token *Token) (bool, error) {
// checkRevokedOrDisabled checks whether the input token has been revoked or disabled.
func (c *baseClient) checkRevokedOrDisabled(ctx context.Context, token *Token, errCode string, errMessage string) error {
user, err := c.GetUser(ctx, token.UID)
if err != nil {
return false, err
return err
}
if user.Disabled {
return &internal.FirebaseError{
ErrorCode: internal.InvalidArgument,
String: "user has been disabled",
Ext: map[string]interface{}{
authErrorCode: userDisabled,
},
}

return token.IssuedAt*1000 < user.TokensValidAfterMillis, nil
}
if token.IssuedAt*1000 < user.TokensValidAfterMillis {
return &internal.FirebaseError{
ErrorCode: internal.InvalidArgument,
String: errMessage,
Ext: map[string]interface{}{
authErrorCode: errCode,
},
}
}
return nil
}

func hasAuthErrorCode(err error, code string) bool {
Expand Down
Loading

0 comments on commit 02cde4f

Please sign in to comment.