Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
09fc70b
Renamed some tests and test parameters for clarity, and adhere to Go …
hiranya911 Jan 17, 2018
c24cb17
clean unused types (#76)
avishalom Jan 19, 2018
fb6fa29
Create CHANGELOG.md (#75) (#79)
avishalom Jan 29, 2018
aae4f93
change instance ID format (#82)
avishalom Feb 1, 2018
56a7312
Import context from golang.org/x/net/ for 1.6 compatibility (#87)
avishalom Feb 8, 2018
c764f49
Document non existing name in integration tests for iid (#85)
avishalom Feb 8, 2018
06eb0e0
Revoke Tokens (#77)
avishalom Feb 13, 2018
3f7b4ba
Firebase Cloud Messaging API (#81)
hiranya911 Feb 13, 2018
82606fb
Merged with master
hiranya911 Feb 13, 2018
a053b99
Bumped version to 2.5.0 (#90)
hiranya911 Feb 14, 2018
9f91451
Merge branch 'master' into dev
hiranya911 Feb 15, 2018
0b3ac34
Merge branch 'master' into dev
hiranya911 Feb 16, 2018
fd78f9d
Lint (#96)
chemidy Feb 18, 2018
3a386a4
Doc (#97)
chemidy Feb 18, 2018
0dd57f9
Merge branch 'master' into dev
hiranya911 Feb 20, 2018
387fa39
add travis build for go versions 1.7.x -> 1.10.x (#98)
chemidy Feb 26, 2018
9d2e4a8
Import context from standard package (#101)
avishalom Feb 27, 2018
c9be1e9
Firebase Database API (#92)
hiranya911 Feb 28, 2018
3a49e80
Handling FCM canonical error codes (#103)
hiranya911 Feb 28, 2018
f03d5a6
Formatting test file with gofmt (#104)
hiranya911 Feb 28, 2018
f0be2f4
Bumped version to 2.6.0 (#105)
hiranya911 Feb 28, 2018
2331e41
Merge branch 'master' into dev
hiranya911 Feb 28, 2018
04299fa
Formatting (simplification) changes (#107)
hiranya911 Mar 1, 2018
07d3484
Checking for unformatted files in CI (#108)
hiranya911 Mar 1, 2018
0bbfc6c
Document Minimum Go Version (#111)
hiranya911 Mar 9, 2018
cf5cb07
Fix invalid endpoint URL for topic unsubscribe (#114)
michaljemala Mar 12, 2018
31b566d
Fix error message for missing user (#113)
avishalom Mar 14, 2018
a3ce7c8
Update CHANGELOG.md (#117)
hiranya911 Mar 15, 2018
9bd56f9
Removing unused member from auth.Client (#118)
hiranya911 Mar 15, 2018
0c849e9
Support Go 1.6 (#120)
tbpg Mar 15, 2018
eae7451
Bumped version to 2.6.1 (#121)
avishalom Mar 15, 2018
98f50a2
Changlog updates (#123)
avishalom Mar 15, 2018
1ad967f
Merge branch 'master' into dev
hiranya911 Mar 15, 2018
201a4f3
Reading FCM error code from details section (#127)
hiranya911 Mar 30, 2018
6cf2210
Adding additional details to the error message when available (#129)
hiranya911 Apr 4, 2018
1b20f5a
Implementing support for APNS content-mutable field (#126)
hiranya911 Apr 6, 2018
0beafb8
Implementing Functions to Test Error Codes (#131)
hiranya911 Apr 10, 2018
bb41405
Add Go import comments. (#132)
functionary Apr 11, 2018
0dd6a00
Bumped version to 2.7.0 (#133)
hiranya911 Apr 17, 2018
76a5335
Merge branch 'master' into dev
hiranya911 Apr 17, 2018
d9742c8
Breaking context change (#88)
avishalom May 7, 2018
16fd6d3
Removing golint checks from 1.6.x build (#141)
hiranya911 May 7, 2018
2af1a22
Bumped version to 3.0.0 (#142)
hiranya911 May 8, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@ matrix:
go_import_path: firebase.google.com/go

before_install:
- go get github.com/golang/lint/golint # Golint requires Go 1.6 or later.
# Golint requires Go 1.7 or later.
- if ! [[ "$TRAVIS_GO_VERSION" =~ ^1\.6\.[0-9]+$ ]]; then go get github.com/golang/lint/golint; fi

install:
# Prior to golang 1.8, this can trigger an error for packages containing only tests.
- go get -t -v $(go list ./... | grep -v integration)

script:
- golint -set_exit_status $(go list ./...)
- if ! [[ "$TRAVIS_GO_VERSION" =~ ^1\.6\.[0-9]+$ ]]; then golint -set_exit_status $(go list ./...); fi
- ./.travis.gofmt.sh
- go test -v -race -test.short ./... # Run tests with the race detector.
- go vet -v ./... # Run Go static analyzer.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Unreleased

-
# v3.0.0

- All functions that make network calls now take context as an argument.

# v2.7.0

Expand Down
100 changes: 50 additions & 50 deletions auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@ package auth

import (
"crypto/rsa"
"crypto/x509"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"strings"
Expand All @@ -31,10 +29,12 @@ import (
"google.golang.org/api/transport"
)

const firebaseAudience = "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
const googleCertURL = "https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com"
const issuerPrefix = "https://securetoken.google.com/"
const tokenExpSeconds = 3600
const (
firebaseAudience = "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
idTokenCertURL = "https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com"
issuerPrefix = "https://securetoken.google.com/"
tokenExpSeconds = 3600
)

var reservedClaims = []string{
"acr", "amr", "at_hash", "aud", "auth_time", "azp", "cnf", "c_hash",
Expand All @@ -58,6 +58,25 @@ type Token struct {
Claims map[string]interface{} `json:"-"`
}

func (t *Token) decodeFrom(s string) error {
// Decode into a regular map to access custom claims.
claims := make(map[string]interface{})
if err := decode(s, &claims); err != nil {
return err
}
// Now decode into Token to access the standard claims.
if err := decode(s, t); err != nil {
return err
}

// Delete standard claims from the custom claims maps.
for _, r := range []string{"iss", "aud", "exp", "iat", "sub", "uid"} {
delete(claims, r)
}
t.Claims = claims
return nil
}

// Client is the interface for the Firebase auth service.
//
// Client facilitates generating custom JWT tokens for Firebase clients, and verifying ID tokens issued
Expand All @@ -71,8 +90,8 @@ type Client struct {
}

type signer interface {
Email() (string, error)
Sign(b []byte) ([]byte, error)
Email(ctx context.Context) (string, error)
Sign(ctx context.Context, b []byte) ([]byte, error)
}

// NewClient creates a new instance of the Firebase Auth Client.
Expand All @@ -94,7 +113,7 @@ func NewClient(ctx context.Context, c *internal.AuthConfig) (*Client, error) {
return nil, err
}
if svcAcct.PrivateKey != "" {
pk, err = parseKey(svcAcct.PrivateKey)
pk, err = parsePrivateKey(svcAcct.PrivateKey)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -124,7 +143,7 @@ func NewClient(ctx context.Context, c *internal.AuthConfig) (*Client, error) {

return &Client{
is: is,
ks: newHTTPKeySource(googleCertURL, hc),
ks: newHTTPKeySource(idTokenCertURL, hc),
projectID: c.ProjectID,
snr: snr,
version: "Go/Admin/" + c.Version,
Expand All @@ -135,14 +154,14 @@ func NewClient(ctx context.Context, c *internal.AuthConfig) (*Client, error) {
// JWT can be used in a Firebase client SDK to trigger an authentication flow. See
// https://firebase.google.com/docs/auth/admin/create-custom-tokens#sign_in_using_custom_tokens_on_clients
// for more details on how to use custom tokens for client authentication.
func (c *Client) CustomToken(uid string) (string, error) {
return c.CustomTokenWithClaims(uid, nil)
func (c *Client) CustomToken(ctx context.Context, uid string) (string, error) {
return c.CustomTokenWithClaims(ctx, uid, nil)
}

// CustomTokenWithClaims is similar to CustomToken, but in addition to the user ID, it also encodes
// all the key-value pairs in the provided map as claims in the resulting JWT.
func (c *Client) CustomTokenWithClaims(uid string, devClaims map[string]interface{}) (string, error) {
iss, err := c.snr.Email()
func (c *Client) CustomTokenWithClaims(ctx context.Context, uid string, devClaims map[string]interface{}) (string, error) {
iss, err := c.snr.Email(ctx)
if err != nil {
return "", err
}
Expand All @@ -164,6 +183,7 @@ func (c *Client) CustomTokenWithClaims(uid string, devClaims map[string]interfac
}

now := clk.Now().Unix()
header := jwtHeader{Algorithm: "RS256", Type: "JWT"}
payload := &customToken{
Iss: iss,
Sub: iss,
Expand All @@ -173,7 +193,7 @@ func (c *Client) CustomTokenWithClaims(uid string, devClaims map[string]interfac
Exp: now + tokenExpSeconds,
Claims: devClaims,
}
return encodeToken(c.snr, defaultHeader(), payload)
return encodeToken(ctx, c.snr, header, payload)
}

// RevokeRefreshTokens revokes all refresh tokens issued to a user.
Expand All @@ -196,50 +216,50 @@ func (c *Client) RevokeRefreshTokens(ctx context.Context, uid string) error {
// 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 does not check whether or not the token has been revoked. See `VerifyIDTokenAndCheckRevoked` below.
func (c *Client) VerifyIDToken(idToken string) (*Token, error) {
func (c *Client) VerifyIDToken(ctx context.Context, idToken string) (*Token, error) {
if c.projectID == "" {
return nil, errors.New("project id not available")
}
if idToken == "" {
return nil, fmt.Errorf("ID token must be a non-empty string")
return nil, fmt.Errorf("id token must be a non-empty string")
}

h := &jwtHeader{}
p := &Token{}
if err := decodeToken(idToken, c.ks, h, p); err != nil {
if err := decodeToken(ctx, idToken, c.ks, h, p); err != nil {
return nil, err
}

projectIDMsg := "Make sure the ID token comes from the same Firebase project as the credential used to" +
" authenticate this SDK."
verifyTokenMsg := "See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to " +
"retrieve a valid ID token."
projectIDMsg := "make sure the ID token comes from the same Firebase project as the credential used to" +
" authenticate this SDK"
verifyTokenMsg := "see https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to " +
"retrieve a valid ID token"
issuer := issuerPrefix + c.projectID

var err error
if h.KeyID == "" {
if p.Audience == firebaseAudience {
err = fmt.Errorf("VerifyIDToken() expects an ID token, but was given a custom token")
err = fmt.Errorf("expected an ID token but got a custom token")
} else {
err = fmt.Errorf("ID token has no 'kid' header")
}
} else if h.Algorithm != "RS256" {
err = fmt.Errorf("ID token has invalid incorrect algorithm. Expected 'RS256' but got %q. %s",
err = fmt.Errorf("ID token has invalid algorithm; expected 'RS256' but got %q; %s",
h.Algorithm, verifyTokenMsg)
} else if p.Audience != c.projectID {
err = fmt.Errorf("ID token has invalid 'aud' (audience) claim. Expected %q but got %q. %s %s",
err = fmt.Errorf("ID token has invalid 'aud' (audience) claim; expected %q but got %q; %s; %s",
c.projectID, p.Audience, projectIDMsg, verifyTokenMsg)
} else if p.Issuer != issuer {
err = fmt.Errorf("ID token has invalid 'iss' (issuer) claim. Expected %q but got %q. %s %s",
err = fmt.Errorf("ID token has invalid 'iss' (issuer) claim; expected %q but got %q; %s; %s",
issuer, p.Issuer, projectIDMsg, verifyTokenMsg)
} else if p.IssuedAt > clk.Now().Unix() {
err = fmt.Errorf("ID token issued at future timestamp: %d", p.IssuedAt)
} else if p.Expires < clk.Now().Unix() {
err = fmt.Errorf("ID token has expired. Expired at: %d", p.Expires)
err = fmt.Errorf("ID token has expired at: %d", p.Expires)
} else if p.Subject == "" {
err = fmt.Errorf("ID token has empty 'sub' (subject) claim. %s", verifyTokenMsg)
err = fmt.Errorf("ID token has empty 'sub' (subject) claim; %s", verifyTokenMsg)
} else if len(p.Subject) > 128 {
err = fmt.Errorf("ID token has a 'sub' (subject) claim longer than 128 characters. %s", verifyTokenMsg)
err = fmt.Errorf("ID token has a 'sub' (subject) claim longer than 128 characters; %s", verifyTokenMsg)
}

if err != nil {
Expand All @@ -254,7 +274,7 @@ func (c *Client) VerifyIDToken(idToken string) (*Token, error) {
// VerifyIDTokenAndCheckRevoked verifies the signature and payload of the provided ID token and
// checks that it wasn't revoked. Uses VerifyIDToken() internally to verify the ID token JWT.
func (c *Client) VerifyIDTokenAndCheckRevoked(ctx context.Context, idToken string) (*Token, error) {
p, err := c.VerifyIDToken(idToken)
p, err := c.VerifyIDToken(ctx, idToken)
if err != nil {
return nil, err
}
Expand All @@ -269,23 +289,3 @@ func (c *Client) VerifyIDTokenAndCheckRevoked(ctx context.Context, idToken strin
}
return p, nil
}

func parseKey(key string) (*rsa.PrivateKey, error) {
block, _ := pem.Decode([]byte(key))
if block == nil {
return nil, fmt.Errorf("no private key data found in: %v", key)
}
k := block.Bytes
parsedKey, err := x509.ParsePKCS8PrivateKey(k)
if err != nil {
parsedKey, err = x509.ParsePKCS1PrivateKey(k)
if err != nil {
return nil, fmt.Errorf("private key should be a PEM or plain PKSC1 or PKCS8; parse error: %v", err)
}
}
parsed, ok := parsedKey.(*rsa.PrivateKey)
if !ok {
return nil, errors.New("private key is not an RSA key")
}
return parsed, nil
}
11 changes: 5 additions & 6 deletions auth/auth_appengine.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,17 @@ import (
)

type aeSigner struct {
ctx context.Context
}

func newSigner(ctx context.Context) (signer, error) {
return aeSigner{ctx}, nil
return aeSigner{}, nil
}

func (s aeSigner) Email() (string, error) {
return appengine.ServiceAccount(s.ctx)
func (s aeSigner) Email(ctx context.Context) (string, error) {
return appengine.ServiceAccount(ctx)
}

func (s aeSigner) Sign(ss []byte) ([]byte, error) {
_, sig, err := appengine.SignBytes(s.ctx, ss)
func (s aeSigner) Sign(ctx context.Context, ss []byte) ([]byte, error) {
_, sig, err := appengine.SignBytes(ctx, ss)
return sig, err
}
Loading