naco-api/internal/server/auth_test.go
Nacorid 57a9c43890
Some checks failed
Test Every Endpoint on Base Sepolia / test_endpoints (push) Failing after 3m52s
CI/CD Go Verification / build_and_test (push) Successful in 1m21s
fix: interface{} -> any, +goose up -> +goose up
2026-09-05 01:01:07 +00:00

213 lines
5.1 KiB
Go

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)
}
}
}