B2B API Authentication

Learn how to authenticate with the B2B API using JWT (JSON Web Token) and RSA digital signatures.

Key Pair Generation

Generate RSA Key Pair

Before authentication, you need to generate an RSA key pair:

# Generate a 2048-bit RSA private key
openssl genrsa -out private.pem 2048

# Extract the public key
openssl rsa -in private.pem -pubout -out public.pem

Register Your Public Key

You need to log in with a B2B user to get a JWT token first:

curl --request POST \
  --url http://auth-ss-staging.betprophet.io/api/v1/internal/b2b/keys \
  --header 'authorization: Bearer ${JWT_TOKEN}' \
  --header 'content-type: application/json' \
  --header 'internal-auth: xYA7HZh6GSwEDanHdgayCkbz7s2sGD' \
  --data '{
  "name": "Test Public Key",
  "public_key": "-----BEGIN PUBLIC KEY-----\nMIIBI... truncated ...AQAB\n-----END PUBLIC KEY-----\n"
}'

Get List of Public Keys

curl --request GET \
  --url http://auth-ss-staging.betprophet.io/api/v1/internal/b2b/keys \
  --header 'authorization: Bearer ${JWT_TOKEN}'

Delete Public Key

curl --request DELETE \
  --url http://auth-ss-staging.betprophet.io/api/v1/internal/b2b/keys/d3ni76svmcpns4tm2bg0 \
  --header 'authorization: Bearer ${JWT_TOKEN}' \
  --header 'content-type: application/json'

Receive your unique Issuer ID (e.g., d3clqdmkthpikm9dc9kg).

Authentication Headers

Each authenticated request must include:

  • Content-Type: Must be application/json (Required)
  • Authorization: Bearer token with JWT (Required)

Request Signing

Step 1: Prepare Request Body

# Example request data
body = {"message": "sample request"}

# Convert to compact JSON string
body_bytes = json.dumps(body).encode()

Step 2: Calculate Body Hash

# Calculate SHA256 hash of request body
import hashlib

hasher = hashlib.sha256()
hasher.update(body_bytes)
body_hash = base64.b64encode(hasher.digest()).decode()

Step 3: Create JWT Payload

now = int(time.time())  # in seconds
payload = {
    "iss": issuer,
    "aud": "prophetx",  # should always be `prophetx`
    "exp": now + 3600,  # in seconds
    "iat": now,  # in seconds
    "jti": f"test-jti-{now}",
    "body_hash": body_hash,
}

Step 4: Sign JWT Token

import jwt
from cryptography.hazmat.primitives import serialization

path_to_private_key = './keys/private.pem'
with open(path_to_private_key, 'rb') as f:
    private_key = serialization.load_pem_private_key(f.read(), password=None)

token = jwt.encode(payload, private_key, algorithm="RS256")

Making Authenticated Requests

POST Request Example

import requests

url = "https://api-ss-staging.betprophet.co/b2b/api/v1/pong"
headers = {
    "Content-Type": "application/json",
    "Authorization": f"Bearer {token}"
}
response = requests.post(url, data=body_bytes, headers=headers)

cURL Example

curl -X POST "https://api-ss-staging.betprophet.co/b2b/api/v1/pong" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..." \
-d '{"message":"sample request"}'

Authentication Flow

Authentication Flow

JWT Claims Reference

ClaimTypeDescriptionExample
issstringYour partner identifier"d3clqdmkthpikm9dc9kg"
audstringTarget audience (always "prophetx")"prophetx"
expnumberToken expiration (Unix timestamp)1640995200
iatnumberToken issued at (Unix timestamp)1640991600
jtistringUnique token identifier"req-1640991600"
body_hashstringBase64 SHA256 hash of request body"abc123..."

Validation Constraints

Token Expiration

  • Tokens cannot be issued in the future.
  • Tokens cannot be reused.
  • The server allows a 5-second clock skew tolerance.

Body Hash Validation

  • The request body must exactly match the body used for hash calculation.
  • JSON must be in compact format (no extra whitespace).
  • UTF-8 encoding is required.

Required Claims

All six JWT claims are mandatory. Missing any claim will result in authentication failure.

Error Responses

Common Error Codes

StatusDescriptionSolutionError Code
401Authorization header missingAdd Authorization: Bearer headerTBD
401JWT token expiredGenerate a new token with future expirationTBD
401JWT signature verification failedCheck private key and RS256 algorithmTBD
401Request body doesn't match hashRecalculate body hash with exact request bodyTBD
401Required claims missing or invalidInclude all 6 required JWT claimsTBD
401Issuer not registeredVerify issuer ID and public key registrationTBD

Error Response Format

{
  "error_code": "AUTHENTICATION_FAILED",
  "error_type": "token_expired",
  "message": "JWT token has expired",
  "timestamp": "2023-12-31T12:00:00.000Z",
  "request_id": "req_1234567890"
}

Code Examples

Python

# Example Python code for authentication