Introduction
Coinbase Developer Platform (CDP) uses three distinct types of authentication keys, each serving a specific purpose: Server requests: These keys should be stored securely, and used only by trusted back-end services:- Secret API Key: For all server-to-server communication (i.e., REST APIs).
- Wallet Secret: Additional requirement for any server-to-server communication that involves sensitive wallet operations (i.e., signing transactions via REST APIs).
- Client API Key: For all client-side communication (i.e., JSON-RPC APIs).
Client API Key
The Client API Key is designed specifically for client-side applications. This key:- Is present within your RPC endpoint URL (i.e.,
https://api.developer.coinbase.com/rpc/v1/base/<MY-CLIENT-API-KEY>) - Authenticates JSON-RPC requests from browser-based applications and mobile apps
- Is safe to include in client-side code
- Has limited functionality by design
- Can be easily rotated if needed
1. Create Client API Key
To create a Client API Key:- Navigate to your API Keys dashboard.
- Select your desired project from the top drop-down.
- Select the Client API Key tab.
- Copy the generated key.
- Export as an environment variable:
Copy
Ask AI
export CLIENT_API_KEY="your_client_api_key"
Click the Rotate button to expire this key and generate a new one.
2. Authenticate
To authenticate your client-side code, include it with your JSON-RPC request:Copy
Ask AI
curl -L -X https://api.developer.coinbase.com/rpc/v1/base/${CLIENT_API_KEY} \
-H "Content-Type: application/json" \
-d '${REQUEST_BODY_JSON}'
Copy
Ask AI
curl -L -X https://api.developer.coinbase.com/rpc/v1/base/${CLIENT_API_KEY} \
-H "Content-Type: application/json" \
-d '{"jsonrpc": "2.0", "id": 1, "method": "cdp_listBalances", "params": [{"address":"0xF7DCa789B08Ed2F7995D9bC22c500A8CA715D0A8","pageToken":"","pageSize":1}]}'
Secret API Key
The Secret API Key is required for all server-to-server communication with CDP APIs. This key:- Is used to generate a Bearer Token (JWT), which authenticates your CDP project ownership
- Is used in the
Authorizationheader of your request - Is required as the base layer of authentication for all server endpoints
- Must be kept secure and never exposed in client-side code
- Can be configured with IP allowlists and more granular permissions
1. Create Secret API Key
To create a Secret API Key:- Navigate to your API Keys dashboard.
- Select your desired project from the top drop-down.
- Select the Secret API Keys tab.
- Click Create API key and name your key.
- Optional: Configure additional settings
- IP allowlist
- Permission restrictions
- Signature algorithm (Ed25519 recommended)
- Click Create & download.
To regenerate a Secret API key, click Configure to delete and recreate the key.
2. Generate Bearer Token
Bearer Tokens (JWTs) are required for server-to-server communication only, are included in yourAuthorization header, and are generated using your Secret API Key.
Use our SDK for easier authenticationThe CDP SDK automatically handles generation of Bearer Tokens for you, streamlining the process of making requests to all of our REST endpoints.
- Set up your environment for Bearer Token generation by configuring environment variables and installing dependencies
- Export your generated Bearer Token as an environment variable
Show More on JWTs
Show More on JWTs
A JWT is a compact, self-contained, stateless token format used to securely transmit API keys as a JSON object for authentication with the CDP API. They are typically included in the
Authorization header of your request.Read more in our JWT documentation.Never include Secret API key information in your code.Instead, securely store it and retrieve it from an environment variable, a secure database, or other storage mechanism intended for highly-sensitive parameters.
Environment setup
To begin, export the following environment variables:Copy
Ask AI
export KEY_NAME="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
export KEY_SECRET="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX=="
export REQUEST_METHOD="GET"
export REQUEST_PATH="/api/v3/brokerage/accounts"
export REQUEST_HOST="api.coinbase.com"
Newlines must be preserved to properly parse the key secret. Do this on one line by using \n to escape new lines, or via a multi-line string.
Generate Bearer Token (JWT) and export
- Python
- JavaScript
- TypeScript
- Go
- Ruby
- PHP
- Java
- C++
- C#
First, install required dependencies:Create a new file for JWT generation code:Finally, run the script to generate the JWT output and export it as an environment variable:
Copy
Ask AI
pip install PyJWT==2.8.0
pip install cryptography==42.0.5
main.py
Copy
Ask AI
import jwt
import time
import secrets
import os
from cryptography.hazmat.primitives import serialization
def build_jwt(uri):
private_key_bytes = os.getenv('KEY_SECRET').encode('utf-8')
private_key = serialization.load_pem_private_key(private_key_bytes, password=None)
jwt_payload = {
'sub': os.getenv('KEY_NAME'),
'iss': "cdp",
'nbf': int(time.time()),
'exp': int(time.time()) + 120,
'uri': uri,
}
jwt_token = jwt.encode(
jwt_payload,
private_key,
algorithm='ES256',
headers={'kid': os.getenv('KEY_NAME'), 'nonce': secrets.token_hex()},
)
return jwt_token
# Generate and export the JWT
uri = f"{os.getenv('REQUEST_METHOD')} {os.getenv('REQUEST_HOST')}{os.getenv('REQUEST_PATH')}"
jwt_token = build_jwt(uri)
print(jwt_token)
Copy
Ask AI
export JWT=$(python main.py)
echo $JWT
First, install required dependencies:Create a new file for JWT generation code:Finally, run the script to generate the JWT output and export it as an environment variable:
Copy
Ask AI
npm install jsonwebtoken uuid
main.js
Copy
Ask AI
const { sign } = require("jsonwebtoken");
const crypto = require("crypto");
// Fetch environment variables
const key_name = process.env.KEY_NAME;
const key_secret = process.env.KEY_SECRET;
const request_method = process.env.REQUEST_METHOD;
const request_host = process.env.REQUEST_HOST;
const request_path = process.env.REQUEST_PATH;
const algorithm = "ES256";
const uri = `${request_method} ${request_host}${request_path}`;
const token = sign(
{
iss: "cdp",
nbf: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + 120, // JWT expires in 120 seconds
sub: key_name,
uri,
},
key_secret,
{
algorithm,
header: {
kid: key_name,
nonce: crypto.randomBytes(16).toString("hex"),
},
}
);
console.log("export JWT=" + token);
Copy
Ask AI
export JWT=$(node main.js)
echo $JWT
First, install required dependencies:Create a new file for JWT generation code:Finally, compile and run the script to generate the JWT output and export it as an environment variable:
Copy
Ask AI
npm install jsonwebtoken @types/jsonwebtoken typescript
npm install -g typescript
main.ts
Copy
Ask AI
import * as jwt from 'jsonwebtoken';
import * as crypto from 'crypto';
// Fetch environment variables
const keyName = process.env.KEY_NAME!;
const keySecret = process.env.KEY_SECRET!;
const requestMethod = process.env.REQUEST_METHOD!;
const requestHost = process.env.REQUEST_HOST!;
const requestPath = process.env.REQUEST_PATH!;
const algorithm = 'ES256'; // Not an environment variable
// Construct the URI
const uri = `${requestMethod} ${requestHost}${requestPath}`;
const generateJWT = (): string => {
const payload = {
iss: 'cdp',
nbf: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + 120, // JWT expires in 120 seconds
sub: keyName,
uri,
};
const header = {
alg: algorithm,
kid: keyName,
nonce: crypto.randomBytes(16).toString('hex'),
};
return jwt.sign(payload, keySecret, { algorithm, header });
};
const main = () => {
const token = generateJWT();
console.log("export JWT=" + token);
};
main();
Copy
Ask AI
tsc main.ts
export JWT=$(node main.js)
echo $JWT
First, install required dependencies:Create a new file for JWT generation code:Run the following to generate your modules and hashes:Finally, run the script to generate the JWT output and export it as an environment variable:
Copy
Ask AI
go mod init jwt-example
go get gopkg.in/go-jose/go-jose.v2
go get github.com/sirupsen/logrus
main.go
Copy
Ask AI
package main
import (
"crypto/rand"
"crypto/x509"
"encoding/pem"
"fmt"
"math"
"math/big"
"os"
"time"
log "github.com/sirupsen/logrus"
"gopkg.in/go-jose/go-jose.v2"
"gopkg.in/go-jose/go-jose.v2/jwt"
)
type APIKeyClaims struct {
*jwt.Claims
URI string `json:"uri"`
}
func buildJWT(uri string) (string, error) {
// Get private key from environment variable
keySecret := os.Getenv("KEY_SECRET")
if keySecret == "" {
return "", fmt.Errorf("KEY_SECRET environment variable is required")
}
// Decode the private key
block, _ := pem.Decode([]byte(keySecret))
if block == nil {
return "", fmt.Errorf("jwt: Could not decode private key")
}
key, err := x509.ParseECPrivateKey(block.Bytes)
if err != nil {
return "", fmt.Errorf("jwt: %w", err)
}
// Create a signer using the private key
sig, err := jose.NewSigner(
jose.SigningKey{Algorithm: jose.ES256, Key: key},
(&jose.SignerOptions{NonceSource: nonceSource{}}).WithType("JWT").WithHeader("kid", os.Getenv("KEY_NAME")),
)
if err != nil {
return "", fmt.Errorf("jwt: %w", err)
}
// Prepare JWT claims
cl := &APIKeyClaims{
Claims: &jwt.Claims{
Subject: os.Getenv("KEY_NAME"),
Issuer: "cdp",
NotBefore: jwt.NewNumericDate(time.Now()),
Expiry: jwt.NewNumericDate(time.Now().Add(2 * time.Minute)),
},
URI: uri,
}
// Sign and serialize the JWT
jwtString, err := jwt.Signed(sig).Claims(cl).CompactSerialize()
if err != nil {
return "", fmt.Errorf("jwt: %w", err)
}
return jwtString, nil
}
var max = big.NewInt(math.MaxInt64)
type nonceSource struct{}
// Generate a nonce using a random number generator
func (n nonceSource) Nonce() (string, error) {
r, err := rand.Int(rand.Reader, max)
if err != nil {
return "", err
}
return r.String(), nil
}
func main() {
// Get request method, host, and path from environment variables
requestMethod := os.Getenv("REQUEST_METHOD")
if requestMethod == "" {
requestMethod = "GET" // Default to "GET" if not set
}
requestHost := os.Getenv("REQUEST_HOST")
if requestHost == "" {
requestHost = "api.coinbase.com" // Default host if not set
}
requestPath := os.Getenv("REQUEST_PATH")
if requestPath == "" {
requestPath = "/api/v3/brokerage/accounts" // Default path if not set
}
// Construct the URI
uri := fmt.Sprintf("%s %s%s", requestMethod, requestHost, requestPath)
// Generate JWT
jwt, err := buildJWT(uri)
if err != nil {
log.Errorf("error building jwt: %v", err)
}
fmt.Println(jwt)
}
Copy
Ask AI
go mod init jwt-generator
go mod tidy
Copy
Ask AI
export JWT=$(go run main.go)
echo $JWT
First, install required dependencies:Create a new file for JWT generation code:Finally, run the script to generate the JWT output and export it as an environment variable:
Copy
Ask AI
gem install jwt
gem install openssl
main.rb
Copy
Ask AI
require 'jwt'
require 'openssl'
require 'time'
require 'securerandom'
# Fetching environment variables
key_name = ENV['KEY_NAME']
key_secret = ENV['KEY_SECRET']
request_method = ENV['REQUEST_METHOD'] || 'GET' # Default to 'GET' if not set
request_host = ENV['REQUEST_HOST'] || 'api.coinbase.com' # Default host if not set
request_path = ENV['REQUEST_PATH'] || '/api/v3/brokerage/accounts' # Default path if not set
def build_jwt(uri)
# Header for the JWT
header = {
typ: 'JWT',
kid: key_name,
nonce: SecureRandom.hex(16)
}
# Claims for the JWT
claims = {
sub: key_name,
iss: 'cdp',
aud: ['cdp_service'],
nbf: Time.now.to_i,
exp: Time.now.to_i + 120, # Expiration time: 2 minute from now.
uri: uri
}
# Read the private key from the environment variable
private_key = OpenSSL::PKey::read(key_secret)
# Encode the JWT
JWT.encode(claims, private_key, 'ES256', header)
end
# Build the JWT with the URI
token = build_jwt("#{request_method.upcase} #{request_host}#{request_path}")
# Print the JWT token
puts token
Copy
Ask AI
ruby main.rb
export JWT=$(ruby main.rb)
echo $JWT
First, install required dependencies:Create a new file for JWT generation code:Finally, run the script to generate the JWT output and export it as an environment variable:
Copy
Ask AI
composer require firebase/php-jwt
main.php
Copy
Ask AI
<?php
require 'vendor/autoload.php';
use Firebase\JWT\JWT;
function buildJwt() {
// Fetching values directly from environment variables (no defaults)
$keyName = getenv('KEY_NAME');
$keySecret = str_replace('\\n', "\n", getenv('KEY_SECRET')); // Handling the private key format
$requestMethod = getenv('REQUEST_METHOD');
$requestHost = getenv('REQUEST_HOST');
$requestPath = getenv('REQUEST_PATH');
// Ensure that the environment variables are set
if (!$keyName || !$keySecret || !$requestMethod || !$requestHost || !$requestPath) {
throw new Exception('Required environment variables are missing');
}
// Constructing the URI from method, host, and path
$uri = $requestMethod . ' ' . $requestHost . $requestPath;
// Loading the private key
$privateKeyResource = openssl_pkey_get_private($keySecret);
if (!$privateKeyResource) {
throw new Exception('Private key is not valid');
}
// Setting the current time and creating a unique nonce
$time = time();
$nonce = bin2hex(random_bytes(16)); // Generate a 32-character hexadecimal nonce
// JWT Payload
$jwtPayload = [
'sub' => $keyName,
'iss' => 'cdp',
'nbf' => $time,
'exp' => $time + 120, // Token valid for 120 seconds from now
'uri' => $uri,
];
// JWT Header
$headers = [
'typ' => 'JWT',
'alg' => 'ES256',
'kid' => $keyName, // Key ID header for JWT
'nonce' => $nonce // Nonce included in headers for added security
];
// Encoding JWT with private key
$jwtToken = JWT::encode($jwtPayload, $privateKeyResource, 'ES256', $keyName, $headers);
return $jwtToken;
}
// Example of calling the function to generate the JWT
try {
$jwt = buildJwt();
echo "JWT Token: " . $jwt . "\n";
} catch (Exception $e) {
echo "Error generating JWT: " . $e->getMessage() . "\n";
}
Copy
Ask AI
php main.php
export JWT=$(php main.php)
echo $JWT
First, install required dependencies:Create a new file for JWT generation code:Finally, compile the script and export the JWT output as an environment variable:
Copy
Ask AI
<!-- Add these to your pom.xml -->
<dependencies>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>9.31</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version>
</dependency>
</dependencies>
Main.java
Copy
Ask AI
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.*;
import com.nimbusds.jwt.*;
import java.security.interfaces.ECPrivateKey;
import java.util.Map;
import java.util.HashMap;
import java.time.Instant;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import java.security.PrivateKey;
import java.security.Security;
import java.security.KeyFactory;
import java.security.spec.PKCS8EncodedKeySpec;
import java.io.StringReader;
public class Main {
public static void main(String[] args) throws Exception {
// Register BouncyCastle as a security provider
Security.addProvider(new BouncyCastleProvider());
// Load environment variables directly
String privateKeyPEM = System.getenv("PRIVATE_KEY").replace("\\n", "\n");
String name = System.getenv("KEY_NAME");
String requestMethod = System.getenv("REQUEST_METHOD");
String requestHost = System.getenv("REQUEST_HOST");
String requestPath = System.getenv("REQUEST_PATH");
// Ensure all environment variables are provided
if (privateKeyPEM == null || name == null || requestMethod == null || requestHost == null || requestPath == null) {
throw new IllegalArgumentException("Required environment variables are missing");
}
// Create header object
Map<String, Object> header = new HashMap<>();
header.put("alg", "ES256");
header.put("typ", "JWT");
header.put("kid", name);
header.put("nonce", String.valueOf(Instant.now().getEpochSecond()));
// Create URI string for current request
String uri = requestMethod + " " + requestHost + requestPath;
// Create data object
Map<String, Object> data = new HashMap<>();
data.put("iss", "cdp");
data.put("nbf", Instant.now().getEpochSecond());
data.put("exp", Instant.now().getEpochSecond() + 120); // Token valid for 120 seconds from now
data.put("sub", name);
data.put("uri", uri);
// Load private key
PEMParser pemParser = new PEMParser(new StringReader(privateKeyPEM));
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
Object object = pemParser.readObject();
PrivateKey privateKey;
if (object instanceof PrivateKey) {
privateKey = (PrivateKey) object;
} else if (object instanceof org.bouncycastle.openssl.PEMKeyPair) {
privateKey = converter.getPrivateKey(((org.bouncycastle.openssl.PEMKeyPair) object).getPrivateKeyInfo());
} else {
throw new Exception("Unexpected private key format");
}
pemParser.close();
// Convert to ECPrivateKey
KeyFactory keyFactory = KeyFactory.getInstance("EC");
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey.getEncoded());
ECPrivateKey ecPrivateKey = (ECPrivateKey) keyFactory.generatePrivate(keySpec);
// Create JWT
JWTClaimsSet.Builder claimsSetBuilder = new JWTClaimsSet.Builder();
for (Map.Entry<String, Object> entry : data.entrySet()) {
claimsSetBuilder.claim(entry.getKey(), entry.getValue());
}
JWTClaimsSet claimsSet = claimsSetBuilder.build();
JWSHeader jwsHeader = new JWSHeader.Builder(JWSAlgorithm.ES256).customParams(header).build();
SignedJWT signedJWT = new SignedJWT(jwsHeader, claimsSet);
JWSSigner signer = new ECDSASigner(ecPrivateKey);
signedJWT.sign(signer);
String sJWT = signedJWT.serialize();
System.out.println(sJWT);
}
}
Copy
Ask AI
mvn compile
export JWT=$(mvn exec:java -Dexec.mainClass=Main)
echo $JWT
First, install required dependencies:Create a new file for JWT generation code:Finally, compile the script and export the JWT output as an environment variable:
Copy
Ask AI
# For Ubuntu/Debian
sudo apt-get install libssl-dev libjwt-dev libcurl4-openssl-dev
# For MacOS
brew install openssl jwt-cpp curl
main.cpp
Copy
Ask AI
#include <iostream>
#include <sstream>
#include <string>
#include <cstdlib> // for std::getenv
#include <openssl/evp.h>
#include <openssl/ec.h>
#include <openssl/pem.h>
#include <openssl/rand.h>
#include <jwt-cpp/jwt.h>
std::string create_jwt() {
// Fetching environment variables
const char* key_name_env = std::getenv("KEY_NAME");
const char* key_secret_env = std::getenv("KEY_SECRET");
const char* request_method_env = std::getenv("REQUEST_METHOD");
const char* request_host_env = std::getenv("REQUEST_HOST");
const char* request_path_env = std::getenv("REQUEST_PATH");
// Ensure all environment variables are present
if (!key_name_env || !key_secret_env || !request_method_env || !request_host_env || !request_path_env) {
throw std::runtime_error("Missing required environment variables");
}
std::string key_name = key_name_env;
std::string key_secret = key_secret_env;
std::string request_method = request_method_env;
std::string request_host = request_host_env;
std::string request_path = request_path_env;
std::string uri = request_method + " " + request_host + request_path;
// Generate a random nonce
unsigned char nonce_raw[16];
RAND_bytes(nonce_raw, sizeof(nonce_raw));
std::string nonce(reinterpret_cast<char*>(nonce_raw), sizeof(nonce_raw));
// Create JWT token
auto token = jwt::create()
.set_subject(key_name)
.set_issuer("cdp")
.set_not_before(std::chrono::system_clock::now())
.set_expires_at(std::chrono::system_clock::now() + std::chrono::seconds{120})
.set_payload_claim("uri", jwt::claim(uri))
.set_header_claim("kid", jwt::claim(key_name))
.set_header_claim("nonce", jwt::claim(nonce))
.sign(jwt::algorithm::es256(key_name, key_secret));
return token;
}
int main() {
try {
std::string token = create_jwt();
std::cout << "Generated JWT Token: " << token << std::endl;
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}
Copy
Ask AI
g++ main.cpp -o myapp -lcurlpp -lcurl -lssl -lcrypto -I/usr/local/include -L/usr/local/lib -ljwt -std=c++17
export JWT=$(./main)
echo $JWT
First, install required dependencies:Create a new file to generate your Wallet Token:Finally, build and run the project to generate the JWT output and export it as an environment variable:
Copy
Ask AI
dotnet add package System.IdentityModel.Tokens.Jwt
dotnet add package BouncyCastle.NetCore
dotnet add package Microsoft.IdentityModel.Tokens
GenerateWalletJWT.cs
Copy
Ask AI
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Cryptography;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using System.Text.Json;
namespace WalletJWT
{
internal class Program
{
static void Main(string[] args)
{
// Get environment variables
string walletSecret = Environment.GetEnvironmentVariable("WALLET_SECRET");
string requestMethod = Environment.GetEnvironmentVariable("REQUEST_METHOD");
string requestHost = Environment.GetEnvironmentVariable("REQUEST_HOST");
string requestPath = Environment.GetEnvironmentVariable("REQUEST_PATH");
string requestBody = Environment.GetEnvironmentVariable("REQUEST_BODY");
// Validate environment variables
if (string.IsNullOrEmpty(walletSecret) || string.IsNullOrEmpty(requestMethod) ||
string.IsNullOrEmpty(requestHost) || string.IsNullOrEmpty(requestPath))
{
throw new InvalidOperationException("Missing required environment variables");
}
string token = GenerateWalletJWT(walletSecret, requestMethod, requestHost, requestPath, requestBody);
Console.WriteLine(token);
}
static string GenerateWalletJWT(string walletSecret, string requestMethod, string requestHost,
string requestPath, string requestBody)
{
// Create the URI
string uri = $"{requestMethod} {requestHost}{requestPath}";
// Create security key
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(walletSecret));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
// Create header
var header = new JwtHeader(credentials);
header["typ"] = "JWT";
// Create payload
var now = DateTimeOffset.UtcNow;
var payload = new JwtPayload
{
{ "iat", now.ToUnixTimeSeconds() },
{ "nbf", now.ToUnixTimeSeconds() },
{ "jti", GenerateJTI() },
{ "uris", new[] { uri } }
};
// Add request body if present
if (!string.IsNullOrEmpty(requestBody))
{
payload["req"] = JsonDocument.Parse(requestBody).RootElement;
}
// Create and sign the token
var token = new JwtSecurityToken(header, payload);
var tokenHandler = new JwtSecurityTokenHandler();
return tokenHandler.WriteToken(token);
}
// Method to generate a dynamic nonce
static string GenerateJTI()
{
byte[] randomBytes = new byte[16];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(randomBytes);
}
return Convert.ToBase64String(randomBytes)
.Replace("+", "-")
.Replace("/", "_")
.Replace("=", "");
}
}
}
Copy
Ask AI
dotnet build
export WALLET_AUTH_JWT=$(dotnet run)
echo $WALLET_AUTH_JWT
Bearer Tokens are valid for 2 minutes by default. After 2 minutes, you will need to generate a new Bearer Token (JWT) to ensure uninterrupted access to the CDP APIs.
If you are experiencing issues, please make sure your machine’s clock is accurate.
3. Authenticate
Use our SDK for easier authenticationThe CDP SDK automatically handles authentication for you, streamlining the process of making requests to all of our REST endpoints.
Copy
Ask AI
export API_ENDPOINT="https://$REQUEST_HOST$REQUEST_PATH"
# Now, use that endpoint in your curl command
curl -L -X "$HTTP_METHOD" "$API_ENDPOINT" \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-H "Accept: application/json"
Copy
Ask AI
curl -L -X POST "https://api.cdp.coinbase.com/platform/v1/networks/base-mainnet/assets/BTC" \
-H "Authorization: Bearer ${JWT}" \
-H "Content-Type: application/json" \
-H "Accept: application/json"
Wallet Secret
The Wallet Secret is an additional layer of security that’s required for any server-to-server requests that involve sensitive wallet write operations to the EVM and Solana APIs. This key:- Is used to generate a Wallet Token (JWT), which authenticates your wallet ownership
- Is used in the
X-Wallet-Authheader of your request - Is required for sensitive wallet operations (i.e.,
POSTandDELETErequests), such as signing a transaction - Should be treated like the password to your onchain wallet
- Is generated by CDP’s Trusted Execution Environment (TEE)
- Is never visible to Coinbase
1. Create Wallet Secret
To create a Wallet Secret:- Navigate to your Wallet API dashboard.
- Ensure your desired project is selected from the top drop-down.
- In the Wallet Secret section, click the Generate button.
- Save the secret in a secure location - you won’t be able to view it again.
Your Wallet Secret is a secret that, when combined with your Secret API Key, can be used to sign transactions and messages. It is generated by CDP’s Trusted Execution Environment (TEE), and is never visible to Coinbase. Secure it as you would a password, and never share it or expose it in client-side code.
2. Generate Wallet Token
Wallet Tokens (Wallet Authentication JWTs) are required for any server-to-server communication that requires aX-Wallet-Auth header, and are generated using your Wallet Secret.
Use our SDK for easier authentication
The CDP SDK automatically handles generation of Wallet Authentication JWTs for you, streamlining the process of making requests to all of our REST endpoints.
- Set up your environment for Wallet Authentication JWT generation by configuring environment variables and installing dependencies
- Export your generated Wallet Authentication JWT as an environment variable
More on Wallet Authentication JWTs
More on Wallet Authentication JWTs
The Wallet Authentication JWT provides an additional layer of security for sensitive wallet operations. It is verified by CDP’s Trusted Execution Environment (TEE) to ensure that:
- The request body matches exactly what was signed
- The endpoint URI matches exactly what was signed
- The JWT was signed with the correct Wallet Secret
Environment setup
To begin, export the following environment variables:Copy
Ask AI
# Your Wallet Secret from the CDP Portal
export WALLET_SECRET="your-wallet-secret"
# The endpoint you're calling
export REQUEST_METHOD="POST"
export REQUEST_PATH="/platform/v2/evm/accounts/0x742d35Cc6634C0532925a3b844Bc454e4438f44e/sign/transaction"
export REQUEST_HOST="api.cdp.coinbase.com"
# The exact request body you'll send
export REQUEST_BODY='{"transaction": "0x1234567890123456789012345678901234567890"}'
Generate Wallet Token (JWT) and export
- Python
- JavaScript
- TypeScript
- Go
- Ruby
- PHP
- Java
- C++
- C#
First, install required dependencies:Create a new file to generate your Wallet Token:Finally, run the script to generate the JWT output and export it as an environment variable:
Copy
Ask AI
pip install PyJWT==2.8.0
pip install cryptography==42.0.5
generate_wallet_jwt.py
Copy
Ask AI
import jwt
import time
import uuid
import os
import json
# Get environment variables
wallet_secret = os.getenv('WALLET_SECRET')
request_method = os.getenv('REQUEST_METHOD')
request_host = os.getenv('REQUEST_HOST')
request_path = os.getenv('REQUEST_PATH')
request_body = os.getenv('REQUEST_BODY')
# Create the JWT payload
now = int(time.time())
uri = f"{request_method} {request_host}{request_path}"
payload = {
'iat': now,
'nbf': now,
'jti': str(uuid.uuid4()),
'uris': [uri]
}
# Add request body if present
if request_body:
payload['req'] = json.loads(request_body)
# Generate the JWT
jwt_token = jwt.encode(
payload,
wallet_secret,
algorithm='ES256',
headers={'typ': 'JWT'}
)
print(jwt_token)
Copy
Ask AI
# Generate and export the JWT
export WALLET_AUTH_JWT=$(python generate_wallet_jwt.py)
echo $WALLET_AUTH_JWT
First, install required dependencies:Create a new file to generate your Wallet Token:Finally, run the script to generate the JWT output and export it as an environment variable:
Copy
Ask AI
npm install jsonwebtoken uuid
generate_wallet_jwt.js
Copy
Ask AI
const jwt = require('jsonwebtoken');
const crypto = require('crypto');
// Get environment variables
const walletSecret = process.env.WALLET_SECRET;
const requestMethod = process.env.REQUEST_METHOD;
const requestHost = process.env.REQUEST_HOST;
const requestPath = process.env.REQUEST_PATH;
const requestBody = process.env.REQUEST_BODY;
// Create the JWT payload
const now = Math.floor(Date.now() / 1000);
const uri = `${requestMethod} ${requestHost}${requestPath}`;
const payload = {
iat: now,
nbf: now,
jti: crypto.randomBytes(16).toString('hex'),
uris: [uri]
};
// Add request body if present
if (requestBody) {
payload.req = JSON.parse(requestBody);
}
// Generate the JWT
const token = jwt.sign(payload, walletSecret, {
algorithm: 'ES256',
header: { typ: 'JWT' }
});
console.log("export JWT=" + token);
Copy
Ask AI
# Generate and export the JWT
export WALLET_AUTH_JWT=$(node generate_wallet_jwt.js)
echo $WALLET_AUTH_JWT
First, install required dependencies:Create a new file to generate your Wallet Token:Finally, compile and run the script to generate the JWT output and export it as an environment variable:
Copy
Ask AI
npm install jsonwebtoken @types/jsonwebtoken typescript
npm install -g typescript
generate_wallet_jwt.ts
Copy
Ask AI
import * as jwt from 'jsonwebtoken';
import * as crypto from 'crypto';
// Get environment variables
const walletSecret = process.env.WALLET_SECRET!;
const requestMethod = process.env.REQUEST_METHOD!;
const requestHost = process.env.REQUEST_HOST!;
const requestPath = process.env.REQUEST_PATH!;
const requestBody = process.env.REQUEST_BODY;
// Create the JWT payload
const now = Math.floor(Date.now() / 1000);
const uri = `${requestMethod} ${requestHost}${requestPath}`;
const payload = {
iat: now,
nbf: now,
jti: crypto.randomBytes(16).toString('hex'),
uris: [uri]
};
// Add request body if present
if (requestBody) {
payload.req = JSON.parse(requestBody);
}
// Generate the JWT
const token = jwt.sign(payload, walletSecret, {
algorithm: 'ES256',
header: { typ: 'JWT' }
});
console.log(token);
Copy
Ask AI
tsc generate_wallet_jwt.ts
export WALLET_AUTH_JWT=$(node generate_wallet_jwt.js)
echo $WALLET_AUTH_JWT
First, install required dependencies:Create a new file to generate your Wallet Token:Run the following to generate your modules and hashes:Finally, run the script to generate the JWT output and export it as an environment variable:
Copy
Ask AI
go mod init wallet-jwt-example
go get gopkg.in/go-jose/go-jose.v2
go get github.com/sirupsen/logrus
generate_wallet_jwt.go
Copy
Ask AI
package main
import (
"crypto/rand"
"encoding/json"
"fmt"
"os"
"time"
log "github.com/sirupsen/logrus"
"gopkg.in/go-jose/go-jose.v2"
"gopkg.in/go-jose/go-jose.v2/jwt"
)
type WalletClaims struct {
IAT int64 `json:"iat"`
NBF int64 `json:"nbf"`
JTI string `json:"jti"`
URIs []string `json:"uris"`
Req interface{} `json:"req,omitempty"`
}
func generateWalletJWT() (string, error) {
// Get wallet secret from environment variable
walletSecret := os.Getenv("WALLET_SECRET")
if walletSecret == "" {
return "", fmt.Errorf("WALLET_SECRET environment variable is required")
}
// Get request details from environment variables
requestMethod := os.Getenv("REQUEST_METHOD")
requestHost := os.Getenv("REQUEST_HOST")
requestPath := os.Getenv("REQUEST_PATH")
requestBody := os.Getenv("REQUEST_BODY")
// Create the URI
uri := fmt.Sprintf("%s %s%s", requestMethod, requestHost, requestPath)
// Generate a random JTI
jti := make([]byte, 16)
if _, err := rand.Read(jti); err != nil {
return "", fmt.Errorf("failed to generate JTI: %w", err)
}
// Create claims
now := time.Now().Unix()
claims := &WalletClaims{
IAT: now,
NBF: now,
JTI: fmt.Sprintf("%x", jti),
URIs: []string{uri},
}
// Add request body if present
if requestBody != "" {
var body interface{}
if err := json.Unmarshal([]byte(requestBody), &body); err != nil {
return "", fmt.Errorf("failed to parse request body: %w", err)
}
claims.Req = body
}
// Create signer
signer, err := jose.NewSigner(
jose.SigningKey{Algorithm: jose.ES256, Key: []byte(walletSecret)},
(&jose.SignerOptions{}).WithType("JWT"),
)
if err != nil {
return "", fmt.Errorf("failed to create signer: %w", err)
}
// Sign and serialize the JWT
jwtString, err := jwt.Signed(signer).Claims(claims).CompactSerialize()
if err != nil {
return "", fmt.Errorf("failed to sign JWT: %w", err)
}
return jwtString, nil
}
func main() {
token, err := generateWalletJWT()
if err != nil {
log.Errorf("error generating wallet JWT: %v", err)
os.Exit(1)
}
fmt.Println(token)
}
Copy
Ask AI
go mod init wallet-jwt-generator
go mod tidy
Copy
Ask AI
export WALLET_AUTH_JWT=$(go run generate_wallet_jwt.go)
echo $WALLET_AUTH_JWT
First, install required dependencies:Create a new file to generate your Wallet Token:Finally, run the script to generate the JWT output and export it as an environment variable:
Copy
Ask AI
gem install jwt
gem install openssl
generate_wallet_jwt.rb
Copy
Ask AI
require 'jwt'
require 'json'
require 'securerandom'
# Get environment variables
wallet_secret = ENV['WALLET_SECRET']
request_method = ENV['REQUEST_METHOD']
request_host = ENV['REQUEST_HOST']
request_path = ENV['REQUEST_PATH']
request_body = ENV['REQUEST_BODY']
# Create the JWT payload
now = Time.now.to_i
uri = "#{request_method} #{request_host}#{request_path}"
payload = {
iat: now,
nbf: now,
jti: SecureRandom.uuid,
uris: [uri]
}
# Add request body if present
if request_body
payload[:req] = JSON.parse(request_body)
end
# Generate the JWT
token = JWT.encode(payload, wallet_secret, 'ES256', { typ: 'JWT' })
puts token
Copy
Ask AI
ruby generate_wallet_jwt.rb
export WALLET_AUTH_JWT=$(ruby generate_wallet_jwt.rb)
echo $WALLET_AUTH_JWT
First, install required dependencies:Create a new file to generate your Wallet Token:Finally, run the script to generate the JWT output and export it as an environment variable:
Copy
Ask AI
composer require firebase/php-jwt
generate_wallet_jwt.php
Copy
Ask AI
<?php
require 'vendor/autoload.php';
use Firebase\JWT\JWT;
function generateWalletJWT() {
// Get environment variables
$walletSecret = getenv('WALLET_SECRET');
$requestMethod = getenv('REQUEST_METHOD');
$requestHost = getenv('REQUEST_HOST');
$requestPath = getenv('REQUEST_PATH');
$requestBody = getenv('REQUEST_BODY');
// Ensure required environment variables are set
if (!$walletSecret || !$requestMethod || !$requestHost || !$requestPath) {
throw new Exception('Required environment variables are missing');
}
// Create the URI
$uri = $requestMethod . ' ' . $requestHost . $requestPath;
// Create the JWT payload
$now = time();
$payload = [
'iat' => $now,
'nbf' => $now,
'jti' => bin2hex(random_bytes(16)),
'uris' => [$uri]
];
// Add request body if present
if ($requestBody) {
$payload['req'] = json_decode($requestBody, true);
}
// Generate the JWT
return JWT::encode($payload, $walletSecret, 'ES256', null, ['typ' => 'JWT']);
}
try {
$token = generateWalletJWT();
echo $token;
} catch (Exception $e) {
echo "Error generating JWT: " . $e->getMessage();
}
Copy
Ask AI
php generate_wallet_jwt.php
export WALLET_AUTH_JWT=$(php generate_wallet_jwt.php)
echo $WALLET_AUTH_JWT
First, install required dependencies:Create a new file to generate your Wallet Token:Finally, compile and run the script to generate the JWT output and export it as an environment variable:
Copy
Ask AI
<!-- Add these to your pom.xml -->
<dependencies>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>nimbus-jose-jwt</artifactId>
<version>9.31</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version>
</dependency>
</dependencies>
GenerateWalletJWT.java
Copy
Ask AI
import com.nimbusds.jose.*;
import com.nimbusds.jose.crypto.*;
import com.nimbusds.jwt.*;
import java.security.SecureRandom;
import java.util.*;
import java.time.Instant;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.Security;
public class GenerateWalletJWT {
public static void main(String[] args) throws Exception {
// Register BouncyCastle as a security provider
Security.addProvider(new BouncyCastleProvider());
// Get environment variables
String walletSecret = System.getenv("WALLET_SECRET");
String requestMethod = System.getenv("REQUEST_METHOD");
String requestHost = System.getenv("REQUEST_HOST");
String requestPath = System.getenv("REQUEST_PATH");
String requestBody = System.getenv("REQUEST_BODY");
// Ensure all environment variables are provided
if (walletSecret == null || requestMethod == null || requestHost == null || requestPath == null) {
throw new IllegalArgumentException("Required environment variables are missing");
}
// Create header
JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.ES256)
.type(JOSEObjectType.JWT)
.build();
// Create URI
String uri = requestMethod + " " + requestHost + requestPath;
// Create claims
JWTClaimsSet.Builder claimsBuilder = new JWTClaimsSet.Builder()
.issueTime(Date.from(Instant.now()))
.notBeforeTime(Date.from(Instant.now()))
.jwtID(generateJTI());
// Add URIs
claimsBuilder.claim("uris", Collections.singletonList(uri));
// Add request body if present
if (requestBody != null && !requestBody.isEmpty()) {
claimsBuilder.claim("req", requestBody);
}
JWTClaimsSet claims = claimsBuilder.build();
// Create JWT
SignedJWT signedJWT = new SignedJWT(header, claims);
// Create signer
JWSSigner signer = new MACSigner(walletSecret.getBytes());
// Sign the JWT
signedJWT.sign(signer);
// Serialize the JWT
String jwtString = signedJWT.serialize();
System.out.println(jwtString);
}
private static String generateJTI() {
SecureRandom random = new SecureRandom();
byte[] bytes = new byte[16];
random.nextBytes(bytes);
return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
}
}
Copy
Ask AI
mvn compile
export WALLET_AUTH_JWT=$(mvn exec:java -Dexec.mainClass=GenerateWalletJWT)
echo $WALLET_AUTH_JWT
First, install required dependencies:Create a new file to generate your Wallet Token:Finally, compile and run the script to generate the JWT output and export it as an environment variable:
Copy
Ask AI
# For Ubuntu/Debian
sudo apt-get install libssl-dev libjwt-dev libcurl4-openssl-dev
# For MacOS
brew install openssl jwt-cpp curl
generate_wallet_jwt.cpp
Copy
Ask AI
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
#include <random>
#include <jwt-cpp/jwt.h>
#include <nlohmann/json.hpp>
std::string generateWalletJWT() {
// Get environment variables
const char* walletSecret = std::getenv("WALLET_SECRET");
const char* requestMethod = std::getenv("REQUEST_METHOD");
const char* requestHost = std::getenv("REQUEST_HOST");
const char* requestPath = std::getenv("REQUEST_PATH");
const char* requestBody = std::getenv("REQUEST_BODY");
// Ensure all environment variables are present
if (!walletSecret || !requestMethod || !requestHost || !requestPath) {
throw std::runtime_error("Missing required environment variables");
}
std::string uri = std::string(requestMethod) + " " + std::string(requestHost) + std::string(requestPath);
// Generate a random JTI
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, 15);
std::string jti;
for (int i = 0; i < 32; i++) {
jti += "0123456789abcdef"[dis(gen)];
}
// Get current time
auto now = std::chrono::system_clock::now();
auto now_seconds = std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();
// Create JWT token
auto token = jwt::create()
.set_issued_at(now)
.set_not_before(now)
.set_payload_claim("jti", jwt::claim(jti))
.set_payload_claim("uris", jwt::claim(std::vector<std::string>{uri}));
// Add request body if present
if (requestBody) {
nlohmann::json body = nlohmann::json::parse(requestBody);
token.set_payload_claim("req", jwt::claim(body));
}
// Sign and get the token
return token.sign(jwt::algorithm::es256(walletSecret));
}
int main() {
try {
std::string token = generateWalletJWT();
std::cout << token << std::endl;
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}
return 0;
}
Copy
Ask AI
g++ generate_wallet_jwt.cpp -o wallet_jwt -lcurlpp -lcurl -lssl -lcrypto -I/usr/local/include -L/usr/local/lib -ljwt -std=c++17
export WALLET_AUTH_JWT=$(./wallet_jwt)
echo $WALLET_AUTH_JWT
First, install required dependencies:Create a new file to generate your Wallet Token:Finally, build and run the project to generate the JWT output and export it as an environment variable:
Copy
Ask AI
dotnet add package System.IdentityModel.Tokens.Jwt
dotnet add package BouncyCastle.NetCore
dotnet add package Microsoft.IdentityModel.Tokens
GenerateWalletJWT.cs
Copy
Ask AI
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Cryptography;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using System.Text.Json;
namespace WalletJWT
{
internal class Program
{
static void Main(string[] args)
{
// Get environment variables
string walletSecret = Environment.GetEnvironmentVariable("WALLET_SECRET");
string requestMethod = Environment.GetEnvironmentVariable("REQUEST_METHOD");
string requestHost = Environment.GetEnvironmentVariable("REQUEST_HOST");
string requestPath = Environment.GetEnvironmentVariable("REQUEST_PATH");
string requestBody = Environment.GetEnvironmentVariable("REQUEST_BODY");
// Validate environment variables
if (string.IsNullOrEmpty(walletSecret) || string.IsNullOrEmpty(requestMethod) ||
string.IsNullOrEmpty(requestHost) || string.IsNullOrEmpty(requestPath))
{
throw new InvalidOperationException("Missing required environment variables");
}
string token = GenerateWalletJWT(walletSecret, requestMethod, requestHost, requestPath, requestBody);
Console.WriteLine(token);
}
static string GenerateWalletJWT(string walletSecret, string requestMethod, string requestHost,
string requestPath, string requestBody)
{
// Create the URI
string uri = $"{requestMethod} {requestHost}{requestPath}";
// Create security key
var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(walletSecret));
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
// Create header
var header = new JwtHeader(credentials);
header["typ"] = "JWT";
// Create payload
var now = DateTimeOffset.UtcNow;
var payload = new JwtPayload
{
{ "iat", now.ToUnixTimeSeconds() },
{ "nbf", now.ToUnixTimeSeconds() },
{ "jti", GenerateJTI() },
{ "uris", new[] { uri } }
};
// Add request body if present
if (!string.IsNullOrEmpty(requestBody))
{
payload["req"] = JsonDocument.Parse(requestBody).RootElement;
}
// Create and sign the token
var token = new JwtSecurityToken(header, payload);
var tokenHandler = new JwtSecurityTokenHandler();
return tokenHandler.WriteToken(token);
}
// Method to generate a dynamic nonce
static string GenerateJTI()
{
byte[] randomBytes = new byte[16];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(randomBytes);
}
return Convert.ToBase64String(randomBytes)
.Replace("+", "-")
.Replace("/", "_")
.Replace("=", "");
}
}
}
Copy
Ask AI
dotnet build
export WALLET_AUTH_JWT=$(dotnet run)
echo $WALLET_AUTH_JWT
Wallet Tokens are valid for 1 minute. After 1 minute, you will need to generate a new one.
If you are experiencing issues, please make sure your machine’s clock is accurate.
3. Authenticate
Use our SDK for easier authentication
The CDP SDK automatically handles authentication for you, streamlining the process of making requests to all of our REST endpoints.
X-Wallet-Auth header requirement), you must include both:
- The standard Bearer token in the
Authorizationheader - The Wallet Authentication JWT in the
X-Wallet-Auth
Copy
Ask AI
# First construct the full API endpoint using our env vars
export API_ENDPOINT="https://${REQUEST_HOST}${REQUEST_PATH}"
# Make the authenticated request using both JWT tokens
curl -L -X ${REQUEST_METHOD} "${API_ENDPOINT}" \
-H "Authorization: Bearer ${JWT}" \
-H "X-Wallet-Auth: ${WALLET_AUTH_JWT}" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d "${REQUEST_BODY}"
What to read next
- Security Best Practices: Learn how to secure your API keys and other sensitive information.
- CDP API Keys: Learn how to create and manage your API keys.
- JWT Authentication: More information on JWT authentication.
- CDP cURL: Learn how to use our CLI tool to interact with the CDP API.
- Postman Files: Download our Postman collection and environment files to get started.