package server import ( "context" "crypto/ecdsa" "crypto/ed25519" "crypto/elliptic" "crypto/rand" "crypto/x509" "encoding/base64" "encoding/pem" "testing" "github.com/golang-jwt/jwt/v5" ) func generateTestECKey() string { privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { panic(err) } der, err := x509.MarshalECPrivateKey(privateKey) if err != nil { panic(err) } block := &pem.Block{ Type: "EC PRIVATE KEY", Bytes: der, } return string(pem.EncodeToMemory(block)) } func TestNewCDPAuth_ECKey(t *testing.T) { ecKey := generateTestECKey() auth, err := NewCDPAuth("test-key-id", ecKey) if err != nil { t.Fatalf("NewCDPAuth failed: %v", err) } if auth.signingMethod != jwt.SigningMethodES256 { t.Errorf("expected ES256, got %v", auth.signingMethod) } if auth.keyName != "test-key-id" { t.Errorf("expected keyName 'test-key-id', got %v", auth.keyName) } if auth.privateKey == nil { t.Error("privateKey should not be nil") } _, ok := auth.privateKey.(*ecdsa.PrivateKey) if !ok { t.Error("privateKey should be *ecdsa.PrivateKey") } } func TestNewCDPAuth_Ed25519Key(t *testing.T) { // Generate a valid Ed25519 key _, privateKey, err := ed25519.GenerateKey(rand.Reader) if err != nil { t.Fatalf("failed to generate Ed25519 key: %v", err) } edKey := base64.StdEncoding.EncodeToString(privateKey) auth, err := NewCDPAuth("test-key-id", edKey) if err != nil { t.Fatalf("NewCDPAuth failed: %v", err) } if auth.signingMethod != jwt.SigningMethodEdDSA { t.Errorf("expected EdDSA, got %v", auth.signingMethod) } if auth.privateKey == nil { t.Error("privateKey should not be nil") } _, ok := auth.privateKey.(ed25519.PrivateKey) if !ok { t.Error("privateKey should be ed25519.PrivateKey") } } func TestNewCDPAuth_InvalidKey(t *testing.T) { _, err := NewCDPAuth("test-key-id", "invalid-key") if err == nil { t.Error("expected error for invalid key") } } func TestNewCDPAuth_EmptyKeyName(t *testing.T) { _, err := NewCDPAuth("", "some-key") if err == nil { t.Error("expected error for empty key name") } } func TestNewCDPAuth_EmptyKey(t *testing.T) { _, err := NewCDPAuth("test-key-id", "") if err == nil { t.Error("expected error for empty key") } } func TestGenerateBearerToken_ECKey(t *testing.T) { ecKey := generateTestECKey() auth, err := NewCDPAuth("test-key-id", ecKey) if err != nil { t.Fatalf("NewCDPAuth failed: %v", err) } token, err := auth.generateBearerToken("POST", "/platform/v2/x402/verify") if err != nil { t.Fatalf("generateBearerToken failed: %v", err) } if token == "" { t.Error("token should not be empty") } // Parse and verify claims parsed, err := jwt.Parse(token, func(token *jwt.Token) (any, error) { if _, ok := token.Method.(*jwt.SigningMethodECDSA); !ok { return nil, jwt.ErrSignatureInvalid } return auth.privateKey.(*ecdsa.PrivateKey).Public(), nil }) if err != nil { t.Fatalf("failed to parse token: %v", err) } if !parsed.Valid { t.Error("token should be valid") } claims, ok := parsed.Claims.(jwt.MapClaims) if !ok { t.Fatal("claims should be MapClaims") } if claims["sub"] != "test-key-id" { t.Errorf("expected sub 'test-key-id', got %v", claims["sub"]) } if claims["iss"] != "cdp" { t.Errorf("expected iss 'cdp', got %v", claims["iss"]) } if claims["iat"] == nil { t.Error("iat claim should be present") } if claims["nbf"] == nil { t.Error("nbf claim should be present") } if claims["exp"] == nil { t.Error("exp claim should be present") } if claims["uris"] == nil { t.Error("uris claim should be present") } uris, ok := claims["uris"].([]any) if !ok || len(uris) != 1 { t.Errorf("expected uris array with 1 element, got %v", claims["uris"]) } if uris[0] != "POST api.cdp.coinbase.com/platform/v2/x402/verify" { t.Errorf("expected uri 'POST api.cdp.coinbase.com/platform/v2/x402/verify', got %v", uris[0]) } // Check header if parsed.Header["kid"] != "test-key-id" { t.Errorf("expected kid 'test-key-id', got %v", parsed.Header["kid"]) } if parsed.Header["nonce"] == nil { t.Error("nonce header should be present") } } func TestGetAuthHeaders(t *testing.T) { ecKey := generateTestECKey() auth, err := NewCDPAuth("test-key-id", ecKey) if err != nil { t.Fatalf("NewCDPAuth failed: %v", err) } headers, err := auth.GetAuthHeaders(context.TODO()) if err != nil { t.Fatalf("GetAuthHeaders failed: %v", err) } if headers.Verify == nil || headers.Verify["Authorization"] == "" { t.Error("Verify header should be present") } if headers.Settle == nil || headers.Settle["Authorization"] == "" { t.Error("Settle header should be present") } if headers.Supported == nil || headers.Supported["Authorization"] == "" { t.Error("Supported header should be present") } // All should start with "Bearer " for name, header := range map[string]string{ "Verify": headers.Verify["Authorization"], "Settle": headers.Settle["Authorization"], "Supported": headers.Supported["Authorization"], } { if len(header) < 7 || header[:7] != "Bearer " { t.Errorf("%s header should start with 'Bearer ', got %s", name, header) } } }