// Copyright 2023 Google LLC // // 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. package credentials import ( "bytes" "context" "crypto/rand" "crypto/rsa" "crypto/x509" "encoding/base64" "encoding/json" "encoding/pem" "strings" "testing" "time" "cloud.google.com/go/auth/internal/jwt" ) var jwtJSONKey = []byte(`{ "private_key_id": "268f54e43a1af97cfc71731688434f45aca15c8b", "private_key": "super secret key", "client_email": "gopher@developer.gserviceaccount.com", "client_id": "gopher.apps.googleusercontent.com", "token_uri": "https://accountp.google.com/o/gophers/token", "type": "service_account", "audience": "https://testpervice.googleapis.com/" }`) func TestDefaultCredentials_SelfSignedJSON(t *testing.T) { privateKey, jsonKey, err := setupFakeKey() if err != nil { t.Fatal(err) } tp, err := DetectDefault(&DetectOptions{ CredentialsJSON: jsonKey, Audience: "audience", UseSelfSignedJWT: true, }) if err != nil { t.Fatalf("DefaultCredentials(%s): %v", jsonKey, err) } tok, err := tp.Token(context.Background()) if err != nil { t.Fatalf("Token(): %v", err) } if got, want := tok.Type, "Bearer"; got != want { t.Errorf("Type = %q, want %q", got, want) } if got := tok.Expiry; tok.Expiry.Before(time.Now()) { t.Errorf("Expiry = %v, should not be expired", got) } err = jwt.VerifyJWS(tok.Value, &privateKey.PublicKey) if err != nil { t.Errorf("jwt.Verify(%q): %v", tok.Value, err) } claim, err := jwt.DecodeJWS(tok.Value) if err != nil { t.Fatalf("jwt.Decode(%q): %v", tok.Value, 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. tokParts := strings.Split(tok.Value, ".") hdrJSON, err := base64.RawURLEncoding.DecodeString(tokParts[0]) if err != nil { t.Fatalf("DecodeString(%q): %v", tokParts[0], err) } var hdr jwt.Header if err := json.Unmarshal(hdrJSON, &hdr); err != nil { t.Fatalf("json.Unmarshal(%q): %v", hdrJSON, err) } if got, want := hdr.KeyID, "268f54e43a1af97cfc71731688434f45aca15c8b"; got != want { t.Errorf("KeyID = %q, want %q", got, want) } } func TestDefaultCredentials_SelfSignedWithScope(t *testing.T) { privateKey, jsonKey, err := setupFakeKey() if err != nil { t.Fatal(err) } tp, err := DetectDefault(&DetectOptions{ CredentialsJSON: jsonKey, Scopes: []string{"scope1", "scope2"}, UseSelfSignedJWT: true, }) if err != nil { t.Fatalf("DefaultCredentials(%s): %v", jsonKey, err) } tok, err := tp.Token(context.Background()) if err != nil { t.Fatalf("Token(): %v", err) } if got, want := tok.Type, "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 = jwt.VerifyJWS(tok.Value, &privateKey.PublicKey) if err != nil { t.Errorf("jwt.Verify(%q): %v", tok.Value, err) } claim, err := jwt.DecodeJWS(tok.Value) if err != nil { t.Fatalf("jwt.Decode(%q): %v", tok.Value, 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. tokParts := strings.Split(tok.Value, ".") hdrJSON, err := base64.RawURLEncoding.DecodeString(tokParts[0]) if err != nil { t.Fatalf("DecodeString(%q): %v", tokParts[0], err) } var hdr jwt.Header if err := json.Unmarshal(hdrJSON, &hdr); err != nil { t.Fatalf("json.Unmarshal(%q): %v", hdrJSON, err) } if got, want := hdr.KeyID, "268f54e43a1af97cfc71731688434f45aca15c8b"; got != want { t.Errorf("KeyID = %q, want %q", got, want) } } // setupFakeKey generates a key we can use in the test data. func setupFakeKey() (*rsa.PrivateKey, []byte, error) { // Generate a key we can use in the test data. pk, err := rsa.GenerateKey(rand.Reader, 2048) if err != nil { return nil, nil, err } // Encode the key and substitute into our example JSON. enc := pem.EncodeToMemory(&pem.Block{ Type: "PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(pk), }) enc, err = json.Marshal(string(enc)) if err != nil { return nil, nil, err } return pk, bytes.Replace(jwtJSONKey, []byte(`"super secret key"`), enc, 1), nil }