blob: daa6b90c6629b69a5ac4bde713c810411181771c [file] [log] [blame]
// 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
}