naco-api/internal/server/auth.go
Nacorid d5b16cba3a
rewrite wrapper
migrate server from v1 to v2
2026-07-29 22:09:27 +00:00

84 lines
2.2 KiB
Go

package server
import (
"context"
"crypto/ecdsa"
"crypto/rand"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"fmt"
"time"
"github.com/golang-jwt/jwt/v5"
x402http "github.com/x402-foundation/x402/go/v2/http"
)
type CDPAuth struct {
keyName string
privateKey *ecdsa.PrivateKey
signingMethod jwt.SigningMethod
}
func NewCDPAuth(keyName, privateKeyPEM string) (*CDPAuth, error) {
block, _ := pem.Decode([]byte(privateKeyPEM))
if block == nil {
return nil, fmt.Errorf("failed to decode PEM block")
}
var signingMethod jwt.SigningMethod
privKey, err := x509.ParseECPrivateKey(block.Bytes)
if err != nil {
parsed, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse private key: %w", err)
}
var ok bool
privKey, ok = parsed.(*ecdsa.PrivateKey)
if !ok {
return nil, fmt.Errorf("not an ECDSA private key")
}
signingMethod = jwt.SigningMethodEdDSA
} else {
signingMethod = jwt.SigningMethodES256
}
return &CDPAuth{
keyName: keyName,
privateKey: privKey,
signingMethod: signingMethod,
}, nil
}
func (a *CDPAuth) generateBearerToken(method, path string) (string, error) {
nonceBytes := make([]byte, 16)
if _, err := rand.Read(nonceBytes); err != nil {
return "", err
}
nonce := hex.EncodeToString(nonceBytes)
uri := fmt.Sprintf("%s api.cdp.coinbase.com%s", method, path)
now := time.Now().Unix()
claims := jwt.MapClaims{
"sub": a.keyName,
"iss": "cdp",
"nbf": now,
"exp": now + 120,
"uris": []string{uri},
}
token := jwt.NewWithClaims(a.signingMethod, claims)
token.Header["kid"] = a.keyName
token.Header["nonce"] = nonce
return token.SignedString(a.privateKey)
}
func (a *CDPAuth) GetAuthHeaders(ctx context.Context) (x402http.AuthHeaders, error) {
verify, _ := a.generateBearerToken("POST", "/platform/v2/x402/verify")
settle, _ := a.generateBearerToken("POST", "/platform/v2/x402/settle")
supported, _ := a.generateBearerToken("GET", "/platform/v2/x402/supported")
return x402http.AuthHeaders{
Verify: map[string]string{"Authorization": "Bearer " + verify},
Settle: map[string]string{"Authorization": "Bearer " + settle},
Supported: map[string]string{"Authorization": "Bearer " + supported},
}, nil
}