Skip to content

Commit 8bf0710

Browse files
authored
Release 3.0.0 (#143)
* Breaking context change (#88) * context signatures * VerifyIDToken context * custom token context * fix context arguments * custom token with claims context * remove TODO * integration_iid_name * propagate ctx * propagate to private functions * snippets and tests ctx * integration test user2 fix * error message * Revert "integration test user2 fix" This reverts commit 57148ce. * add context to AE, fix integration test after merge. * Revert "error message" This reverts commit db7c34a. * Merged with latest dev * Using the 1.6 import for context * Dropping call to WithContext * Cleaning up the auth package (#134) * Cleaning up the auth package * Variable grouping * Removing some punctuation; Changed id to ID in error messages * Adding a minor comment * Cleaning up the auth (user management) code (#136) * Cleaning up the auth (user management) code * Added a few more tests * Removed the fields inner type from UserToCreate and UserToUpdate * Renamed _req variables; More consistent error messages * Removing golint checks from 1.6.x build (#141) * Removing golint checks from 1.6.x build * Minor reformat * Bumped version to 3.0.0 (#142) * Bumped version to 3.0.0 * Updated changelog
1 parent 7f0a0da commit 8bf0710

File tree

15 files changed

+414
-358
lines changed

15 files changed

+414
-358
lines changed

.travis.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,15 @@ matrix:
1919
go_import_path: firebase.google.com/go
2020

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

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

2829
script:
29-
- golint -set_exit_status $(go list ./...)
30+
- if ! [[ "$TRAVIS_GO_VERSION" =~ ^1\.6\.[0-9]+$ ]]; then golint -set_exit_status $(go list ./...); fi
3031
- ./.travis.gofmt.sh
3132
- go test -v -race -test.short ./... # Run tests with the race detector.
3233
- go vet -v ./... # Run Go static analyzer.

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Unreleased
22

3-
-
3+
# v3.0.0
4+
5+
- All functions that make network calls now take context as an argument.
46

57
# v2.7.0
68

auth/auth.go

Lines changed: 50 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@ package auth
1717

1818
import (
1919
"crypto/rsa"
20-
"crypto/x509"
2120
"encoding/json"
22-
"encoding/pem"
2321
"errors"
2422
"fmt"
2523
"strings"
@@ -31,10 +29,12 @@ import (
3129
"google.golang.org/api/transport"
3230
)
3331

34-
const firebaseAudience = "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
35-
const googleCertURL = "https://www.googleapis.com/robot/v1/metadata/x509/[email protected]"
36-
const issuerPrefix = "https://securetoken.google.com/"
37-
const tokenExpSeconds = 3600
32+
const (
33+
firebaseAudience = "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
34+
idTokenCertURL = "https://www.googleapis.com/robot/v1/metadata/x509/[email protected]"
35+
issuerPrefix = "https://securetoken.google.com/"
36+
tokenExpSeconds = 3600
37+
)
3838

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

61+
func (t *Token) decodeFrom(s string) error {
62+
// Decode into a regular map to access custom claims.
63+
claims := make(map[string]interface{})
64+
if err := decode(s, &claims); err != nil {
65+
return err
66+
}
67+
// Now decode into Token to access the standard claims.
68+
if err := decode(s, t); err != nil {
69+
return err
70+
}
71+
72+
// Delete standard claims from the custom claims maps.
73+
for _, r := range []string{"iss", "aud", "exp", "iat", "sub", "uid"} {
74+
delete(claims, r)
75+
}
76+
t.Claims = claims
77+
return nil
78+
}
79+
6180
// Client is the interface for the Firebase auth service.
6281
//
6382
// Client facilitates generating custom JWT tokens for Firebase clients, and verifying ID tokens issued
@@ -71,8 +90,8 @@ type Client struct {
7190
}
7291

7392
type signer interface {
74-
Email() (string, error)
75-
Sign(b []byte) ([]byte, error)
93+
Email(ctx context.Context) (string, error)
94+
Sign(ctx context.Context, b []byte) ([]byte, error)
7695
}
7796

7897
// NewClient creates a new instance of the Firebase Auth Client.
@@ -94,7 +113,7 @@ func NewClient(ctx context.Context, c *internal.AuthConfig) (*Client, error) {
94113
return nil, err
95114
}
96115
if svcAcct.PrivateKey != "" {
97-
pk, err = parseKey(svcAcct.PrivateKey)
116+
pk, err = parsePrivateKey(svcAcct.PrivateKey)
98117
if err != nil {
99118
return nil, err
100119
}
@@ -124,7 +143,7 @@ func NewClient(ctx context.Context, c *internal.AuthConfig) (*Client, error) {
124143

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

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

166185
now := clk.Now().Unix()
186+
header := jwtHeader{Algorithm: "RS256", Type: "JWT"}
167187
payload := &customToken{
168188
Iss: iss,
169189
Sub: iss,
@@ -173,7 +193,7 @@ func (c *Client) CustomTokenWithClaims(uid string, devClaims map[string]interfac
173193
Exp: now + tokenExpSeconds,
174194
Claims: devClaims,
175195
}
176-
return encodeToken(c.snr, defaultHeader(), payload)
196+
return encodeToken(ctx, c.snr, header, payload)
177197
}
178198

179199
// RevokeRefreshTokens revokes all refresh tokens issued to a user.
@@ -196,50 +216,50 @@ func (c *Client) RevokeRefreshTokens(ctx context.Context, uid string) error {
196216
// https://firebase.google.com/docs/auth/admin/verify-id-tokens#retrieve_id_tokens_on_clients for
197217
// more details on how to obtain an ID token in a client app.
198218
// This does not check whether or not the token has been revoked. See `VerifyIDTokenAndCheckRevoked` below.
199-
func (c *Client) VerifyIDToken(idToken string) (*Token, error) {
219+
func (c *Client) VerifyIDToken(ctx context.Context, idToken string) (*Token, error) {
200220
if c.projectID == "" {
201221
return nil, errors.New("project id not available")
202222
}
203223
if idToken == "" {
204-
return nil, fmt.Errorf("ID token must be a non-empty string")
224+
return nil, fmt.Errorf("id token must be a non-empty string")
205225
}
206226

207227
h := &jwtHeader{}
208228
p := &Token{}
209-
if err := decodeToken(idToken, c.ks, h, p); err != nil {
229+
if err := decodeToken(ctx, idToken, c.ks, h, p); err != nil {
210230
return nil, err
211231
}
212232

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

219239
var err error
220240
if h.KeyID == "" {
221241
if p.Audience == firebaseAudience {
222-
err = fmt.Errorf("VerifyIDToken() expects an ID token, but was given a custom token")
242+
err = fmt.Errorf("expected an ID token but got a custom token")
223243
} else {
224244
err = fmt.Errorf("ID token has no 'kid' header")
225245
}
226246
} else if h.Algorithm != "RS256" {
227-
err = fmt.Errorf("ID token has invalid incorrect algorithm. Expected 'RS256' but got %q. %s",
247+
err = fmt.Errorf("ID token has invalid algorithm; expected 'RS256' but got %q; %s",
228248
h.Algorithm, verifyTokenMsg)
229249
} else if p.Audience != c.projectID {
230-
err = fmt.Errorf("ID token has invalid 'aud' (audience) claim. Expected %q but got %q. %s %s",
250+
err = fmt.Errorf("ID token has invalid 'aud' (audience) claim; expected %q but got %q; %s; %s",
231251
c.projectID, p.Audience, projectIDMsg, verifyTokenMsg)
232252
} else if p.Issuer != issuer {
233-
err = fmt.Errorf("ID token has invalid 'iss' (issuer) claim. Expected %q but got %q. %s %s",
253+
err = fmt.Errorf("ID token has invalid 'iss' (issuer) claim; expected %q but got %q; %s; %s",
234254
issuer, p.Issuer, projectIDMsg, verifyTokenMsg)
235255
} else if p.IssuedAt > clk.Now().Unix() {
236256
err = fmt.Errorf("ID token issued at future timestamp: %d", p.IssuedAt)
237257
} else if p.Expires < clk.Now().Unix() {
238-
err = fmt.Errorf("ID token has expired. Expired at: %d", p.Expires)
258+
err = fmt.Errorf("ID token has expired at: %d", p.Expires)
239259
} else if p.Subject == "" {
240-
err = fmt.Errorf("ID token has empty 'sub' (subject) claim. %s", verifyTokenMsg)
260+
err = fmt.Errorf("ID token has empty 'sub' (subject) claim; %s", verifyTokenMsg)
241261
} else if len(p.Subject) > 128 {
242-
err = fmt.Errorf("ID token has a 'sub' (subject) claim longer than 128 characters. %s", verifyTokenMsg)
262+
err = fmt.Errorf("ID token has a 'sub' (subject) claim longer than 128 characters; %s", verifyTokenMsg)
243263
}
244264

245265
if err != nil {
@@ -254,7 +274,7 @@ func (c *Client) VerifyIDToken(idToken string) (*Token, error) {
254274
// VerifyIDTokenAndCheckRevoked verifies the signature and payload of the provided ID token and
255275
// checks that it wasn't revoked. Uses VerifyIDToken() internally to verify the ID token JWT.
256276
func (c *Client) VerifyIDTokenAndCheckRevoked(ctx context.Context, idToken string) (*Token, error) {
257-
p, err := c.VerifyIDToken(idToken)
277+
p, err := c.VerifyIDToken(ctx, idToken)
258278
if err != nil {
259279
return nil, err
260280
}
@@ -269,23 +289,3 @@ func (c *Client) VerifyIDTokenAndCheckRevoked(ctx context.Context, idToken strin
269289
}
270290
return p, nil
271291
}
272-
273-
func parseKey(key string) (*rsa.PrivateKey, error) {
274-
block, _ := pem.Decode([]byte(key))
275-
if block == nil {
276-
return nil, fmt.Errorf("no private key data found in: %v", key)
277-
}
278-
k := block.Bytes
279-
parsedKey, err := x509.ParsePKCS8PrivateKey(k)
280-
if err != nil {
281-
parsedKey, err = x509.ParsePKCS1PrivateKey(k)
282-
if err != nil {
283-
return nil, fmt.Errorf("private key should be a PEM or plain PKSC1 or PKCS8; parse error: %v", err)
284-
}
285-
}
286-
parsed, ok := parsedKey.(*rsa.PrivateKey)
287-
if !ok {
288-
return nil, errors.New("private key is not an RSA key")
289-
}
290-
return parsed, nil
291-
}

auth/auth_appengine.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,17 @@ import (
2323
)
2424

2525
type aeSigner struct {
26-
ctx context.Context
2726
}
2827

2928
func newSigner(ctx context.Context) (signer, error) {
30-
return aeSigner{ctx}, nil
29+
return aeSigner{}, nil
3130
}
3231

33-
func (s aeSigner) Email() (string, error) {
34-
return appengine.ServiceAccount(s.ctx)
32+
func (s aeSigner) Email(ctx context.Context) (string, error) {
33+
return appengine.ServiceAccount(ctx)
3534
}
3635

37-
func (s aeSigner) Sign(ss []byte) ([]byte, error) {
38-
_, sig, err := appengine.SignBytes(s.ctx, ss)
36+
func (s aeSigner) Sign(ctx context.Context, ss []byte) ([]byte, error) {
37+
_, sig, err := appengine.SignBytes(ctx, ss)
3938
return sig, err
4039
}

0 commit comments

Comments
 (0)