| // Copyright 2016 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package google | |
| import ( | |
| "bytes" | |
| "crypto/rand" | |
| "crypto/rsa" | |
| "crypto/x509" | |
| "encoding/base64" | |
| "encoding/json" | |
| "encoding/pem" | |
| "strings" | |
| "sync" | |
| "testing" | |
| "time" | |
| "golang.org/x/oauth2/jws" | |
| ) | |
| var ( | |
| privateKey *rsa.PrivateKey | |
| jsonKey []byte | |
| once sync.Once | |
| ) | |
| func TestJWTAccessTokenSourceFromJSON(t *testing.T) { | |
| setupDummyKey(t) | |
| ts, err := JWTAccessTokenSourceFromJSON(jsonKey, "audience") | |
| if err != nil { | |
| t.Fatalf("JWTAccessTokenSourceFromJSON: %v\nJSON: %s", err, string(jsonKey)) | |
| } | |
| tok, err := ts.Token() | |
| if err != nil { | |
| t.Fatalf("Token: %v", err) | |
| } | |
| if got, want := tok.TokenType, "Bearer"; got != want { | |
| t.Errorf("TokenType = %q, want %q", got, want) | |
| } | |
| if got := tok.Expiry; tok.Expiry.Before(time.Now()) { | |
| t.Errorf("Expiry = %v, should not be expired", got) | |
| } | |
| err = jws.Verify(tok.AccessToken, &privateKey.PublicKey) | |
| if err != nil { | |
| t.Errorf("jws.Verify on AccessToken: %v", err) | |
| } | |
| claim, err := jws.Decode(tok.AccessToken) | |
| if err != nil { | |
| t.Fatalf("jws.Decode on AccessToken: %v", err) | |
| } | |
| if got, want := claim.Iss, "gopher@developer.gserviceaccount.com"; got != want { | |
| t.Errorf("Iss = %q, want %q", got, want) | |
| } | |
| if got, want := claim.Sub, "gopher@developer.gserviceaccount.com"; got != want { | |
| t.Errorf("Sub = %q, want %q", got, want) | |
| } | |
| if got, want := claim.Aud, "audience"; got != want { | |
| t.Errorf("Aud = %q, want %q", got, want) | |
| } | |
| // Finally, check the header private key. | |
| parts := strings.Split(tok.AccessToken, ".") | |
| hdrJSON, err := base64.RawURLEncoding.DecodeString(parts[0]) | |
| if err != nil { | |
| t.Fatalf("base64 DecodeString: %v\nString: %q", err, parts[0]) | |
| } | |
| var hdr jws.Header | |
| if err := json.Unmarshal(hdrJSON, &hdr); err != nil { | |
| t.Fatalf("json.Unmarshal: %v (%q)", err, hdrJSON) | |
| } | |
| if got, want := hdr.KeyID, "268f54e43a1af97cfc71731688434f45aca15c8b"; got != want { | |
| t.Errorf("Header KeyID = %q, want %q", got, want) | |
| } | |
| } | |
| func TestJWTAccessTokenSourceWithScope(t *testing.T) { | |
| setupDummyKey(t) | |
| ts, err := JWTAccessTokenSourceWithScope(jsonKey, "scope1", "scope2") | |
| if err != nil { | |
| t.Fatalf("JWTAccessTokenSourceWithScope: %v\nJSON: %s", err, string(jsonKey)) | |
| } | |
| tok, err := ts.Token() | |
| if err != nil { | |
| t.Fatalf("Token: %v", err) | |
| } | |
| if got, want := tok.TokenType, "Bearer"; got != want { | |
| t.Errorf("TokenType = %q, want %q", got, want) | |
| } | |
| if got := tok.Expiry; tok.Expiry.Before(time.Now()) { | |
| t.Errorf("Expiry = %v, should not be expired", got) | |
| } | |
| err = jws.Verify(tok.AccessToken, &privateKey.PublicKey) | |
| if err != nil { | |
| t.Errorf("jws.Verify on AccessToken: %v", err) | |
| } | |
| claim, err := jws.Decode(tok.AccessToken) | |
| if err != nil { | |
| t.Fatalf("jws.Decode on AccessToken: %v", err) | |
| } | |
| if got, want := claim.Iss, "gopher@developer.gserviceaccount.com"; got != want { | |
| t.Errorf("Iss = %q, want %q", got, want) | |
| } | |
| if got, want := claim.Sub, "gopher@developer.gserviceaccount.com"; got != want { | |
| t.Errorf("Sub = %q, want %q", got, want) | |
| } | |
| if got, want := claim.Scope, "scope1 scope2"; got != want { | |
| t.Errorf("Aud = %q, want %q", got, want) | |
| } | |
| // Finally, check the header private key. | |
| parts := strings.Split(tok.AccessToken, ".") | |
| hdrJSON, err := base64.RawURLEncoding.DecodeString(parts[0]) | |
| if err != nil { | |
| t.Fatalf("base64 DecodeString: %v\nString: %q", err, parts[0]) | |
| } | |
| var hdr jws.Header | |
| if err := json.Unmarshal(hdrJSON, &hdr); err != nil { | |
| t.Fatalf("json.Unmarshal: %v (%q)", err, hdrJSON) | |
| } | |
| if got, want := hdr.KeyID, "268f54e43a1af97cfc71731688434f45aca15c8b"; got != want { | |
| t.Errorf("Header KeyID = %q, want %q", got, want) | |
| } | |
| } | |
| func setupDummyKey(t *testing.T) { | |
| once.Do(func() { | |
| // Generate a key we can use in the test data. | |
| pk, err := rsa.GenerateKey(rand.Reader, 2048) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| privateKey = pk | |
| // Encode the key and substitute into our example JSON. | |
| enc := pem.EncodeToMemory(&pem.Block{ | |
| Type: "PRIVATE KEY", | |
| Bytes: x509.MarshalPKCS1PrivateKey(privateKey), | |
| }) | |
| enc, err = json.Marshal(string(enc)) | |
| if err != nil { | |
| t.Fatalf("json.Marshal: %v", err) | |
| } | |
| jsonKey = bytes.Replace(jwtJSONKey, []byte(`"super secret key"`), enc, 1) | |
| }) | |
| } |