84 lines
2.2 KiB
Go
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
|
|
}
|