Overview
The CDP Go SDK uses JWT (JSON Web Token) based authentication to secure API requests. The SDK provides utilities for generating both API key JWTs and wallet authentication JWTs.
Package Import
import "github.com/coinbase/cdp-sdk/go/auth"
JWT Types
The SDK supports two types of JWTs:
- API Key JWT - For authenticating all API requests
- Wallet JWT - For signing mutations to accounts and spend permissions
JwtOptions
Configuration for generating API key JWTs:
type JwtOptions struct {
// API key ID
KeyID string
// API key secret (EC PEM or Ed25519 base64)
KeySecret string
// HTTP method (e.g., "GET", "POST")
RequestMethod string
// Request host (e.g., "api.cdp.coinbase.com")
RequestHost string
// Request path (e.g., "/platform/v2/evm/accounts")
RequestPath string
// JWT expiration in seconds (default: 120)
ExpiresIn int64
// Optional audience claim
Audience []string
}
Field Details
Your CDP API key ID. Examples:
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
organizations/xxx/apiKeys/xxx
Your API key secret in one of these formats:
- Ed25519: Base64-encoded 64-byte key
- EC: PEM-formatted EC private key
HTTP method. Empty string for WebSocket JWTs
Request host. Empty string for WebSocket JWTs
Request path. Empty string for WebSocket JWTs
JWT expiration time in seconds
Optional audience claim for the JWT
Generating API Key JWTs
Generate a JWT for API authentication:
package main
import (
"fmt"
"log"
"github.com/coinbase/cdp-sdk/go/auth"
)
func main() {
options := auth.JwtOptions{
KeyID: "your-api-key-id",
KeySecret: "your-api-key-secret",
RequestMethod: "GET",
RequestHost: "api.cdp.coinbase.com",
RequestPath: "/platform/v2/evm/accounts",
ExpiresIn: 120,
}
jwt, err := auth.GenerateJWT(options)
if err != nil {
log.Fatalf("Failed to generate JWT: %v", err)
}
fmt.Printf("JWT: %s\n", jwt)
}
REST API JWTs
For REST API requests, provide all request parameters:
options := auth.JwtOptions{
KeyID: "your-api-key-id",
KeySecret: "your-api-key-secret",
RequestMethod: "POST",
RequestHost: "api.cdp.coinbase.com",
RequestPath: "/platform/v2/evm/accounts",
ExpiresIn: 120,
}
jwt, err := auth.GenerateJWT(options)
WebSocket JWTs
For WebSocket connections, omit request parameters:
options := auth.JwtOptions{
KeyID: "your-api-key-id",
KeySecret: "your-api-key-secret",
RequestMethod: "", // Empty for WebSocket
RequestHost: "", // Empty for WebSocket
RequestPath: "", // Empty for WebSocket
ExpiresIn: 120,
}
jwt, err := auth.GenerateJWT(options)
WalletJwtOptions
Configuration for generating wallet authentication JWTs:
type WalletJwtOptions struct {
// Wallet secret (base64-encoded EC DER private key)
WalletSecret string
// HTTP method (e.g., "POST", "DELETE")
RequestMethod string
// Request host
RequestHost string
// Request path
RequestPath string
// Request body data
RequestData map[string]interface{}
}
Field Details
Base64-encoded EC DER format private key for wallet signing
HTTP method (typically “POST” or “DELETE”)
RequestData
map[string]interface{}
required
The request body data to be hashed and included in the JWT
Generating Wallet JWTs
Generate a wallet JWT for signing operations:
package main
import (
"fmt"
"log"
"github.com/coinbase/cdp-sdk/go/auth"
)
func main() {
options := auth.WalletJwtOptions{
WalletSecret: "your-wallet-secret",
RequestMethod: "POST",
RequestHost: "api.cdp.coinbase.com",
RequestPath: "/platform/v2/evm/accounts",
RequestData: map[string]interface{}{
"name": "my-account",
},
}
walletJwt, err := auth.GenerateWalletJWT(options)
if err != nil {
log.Fatalf("Failed to generate wallet JWT: %v", err)
}
fmt.Printf("Wallet JWT: %s\n", walletJwt)
}
WalletAuthClaims
JWT claims structure for wallet authentication:
type WalletAuthClaims struct {
// List of URIs being accessed
URIs []string `json:"uris"`
// SHA-256 hash of sorted request data (hex-encoded)
ReqHash string `json:"reqHash,omitempty"`
// Standard JWT claims (iat, nbf, jti)
jwt.RegisteredClaims
}
Array containing the request URI (e.g., ["POST api.cdp.coinbase.com/platform/v2/evm/accounts"])
Hex-encoded SHA-256 hash of the sorted JSON request body
Standard JWT claims including:
iat (issued at)
nbf (not before)
jti (JWT ID / nonce)
For ES256 signing:
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIBKuSvr...
-----END EC PRIVATE KEY-----
For EdDSA signing (64 bytes when decoded):
YmFzZTY0ZW5jb2RlZGtleWJ5dGVz...==
For wallet signing:
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg...
Error Handling
Handle authentication errors properly:
jwt, err := auth.GenerateJWT(options)
if err != nil {
switch {
case strings.Contains(err.Error(), "key name is required"):
log.Fatal("API key ID is missing")
case strings.Contains(err.Error(), "private key is required"):
log.Fatal("API key secret is missing")
case strings.Contains(err.Error(), "invalid key format"):
log.Fatal("Key format is invalid - use PEM EC or base64 Ed25519")
default:
log.Fatalf("JWT generation failed: %v", err)
}
}
Request Body Hashing
The wallet JWT includes a hash of the request body for integrity:
options := auth.WalletJwtOptions{
WalletSecret: "your-wallet-secret",
RequestMethod: "POST",
RequestHost: "api.cdp.coinbase.com",
RequestPath: "/platform/v2/evm/accounts",
RequestData: map[string]interface{}{
"name": "my-account",
"metadata": map[string]interface{}{
"key": "value",
},
},
}
// The RequestData is:
// 1. Recursively sorted by keys
// 2. Serialized to JSON
// 3. Hashed with SHA-256
// 4. Hex-encoded and included in the ReqHash claim
walletJwt, err := auth.GenerateWalletJWT(options)
Security Best Practices
Store Credentials Securely
Use environment variables for sensitive data:
import "os"
options := auth.JwtOptions{
KeyID: os.Getenv("CDP_API_KEY_ID"),
KeySecret: os.Getenv("CDP_API_KEY_SECRET"),
// ...
}
Set Appropriate Expiration
Use shorter expiration times for sensitive operations:
options := auth.JwtOptions{
KeyID: "your-api-key-id",
KeySecret: "your-api-key-secret",
ExpiresIn: 60, // 1 minute for high-security operations
// ...
}
Ensure keys are in the correct format before use:
import "encoding/base64"
func isValidEd25519Key(key string) bool {
decoded, err := base64.StdEncoding.DecodeString(key)
if err != nil {
return false
}
return len(decoded) == 64
}
Complete Example
Full example using both JWT types:
package main
import (
"context"
"fmt"
"log"
"os"
"github.com/coinbase/cdp-sdk/go"
"github.com/coinbase/cdp-sdk/go/auth"
"github.com/coinbase/cdp-sdk/go/openapi"
)
func main() {
// Load credentials from environment
apiKeyID := os.Getenv("CDP_API_KEY_ID")
apiKeySecret := os.Getenv("CDP_API_KEY_SECRET")
walletSecret := os.Getenv("CDP_WALLET_SECRET")
// Create client (handles auth automatically)
client, err := cdp.NewClient(cdp.ClientOptions{
APIKeyID: apiKeyID,
APIKeySecret: apiKeySecret,
WalletSecret: walletSecret,
})
if err != nil {
log.Fatalf("Failed to create client: %v", err)
}
// The client automatically:
// 1. Generates API key JWT for Authorization header
// 2. Generates wallet JWT for X-Wallet-Auth header (when needed)
ctx := context.Background()
// Make authenticated request
resp, err := client.CreateEvmAccountWithResponse(
ctx,
&openapi.CreateEvmAccountParams{
XWalletAuth: "", // Auto-filled by client
XIdempotencyKey: "unique-request-id",
},
openapi.CreateEvmAccountJSONRequestBody{
Name: openapi.NewOptional("my-account"),
},
)
if err != nil {
log.Fatalf("Request failed: %v", err)
}
if resp.JSON201 != nil {
fmt.Printf("Created account: %s\n", resp.JSON201.Address)
}
}
See Also