If your signature application serves several customers through one OAuth client, send an account token with each authorization so Cleverbase can attribute the signature to the right customer for accounting. One client, many customers: without the token every signature looks like yours.
You do not need this when your client serves one organization.
How it travels
An account_token parameter on GET /oauth2/authorize, next to the other parameters. It is a JSON Web Token (JWT) you build and sign yourself; Cleverbase verifies it with your client secret.
GET /oauth2/authorize?response_type=code&client_id=<client_id>
&redirect_uri=<redirect_uri>&scope=service&state=...
&account_token=<header>.<payload>.<signature>
The token
base64UrlEncode(header) + "." + base64UrlEncode(payload) + "." + base64UrlEncode(signature)
Header
| Field | Required | Value |
|---|---|---|
typ | yes | JWT |
alg | yes | HS256 |
Payload
| Claim | Required | Meaning |
|---|---|---|
sub | yes | The account id, uniquely assigned by your application to that customer. |
iat | yes | Issued at, Unix epoch seconds. |
jti | yes | A unique identifier for this token. |
azp | yes | Authorized presenter: your OAuth 2.0 client id. |
iss | no | The name of your signature application. |
Signature
HMAC-SHA256 over base64UrlEncode(header) + "." + base64UrlEncode(payload), with the key being SHA256(client_secret), the raw 32-byte digest of your OAuth client secret, not the secret itself and not its hex or base64 text.
That last detail is where implementations go wrong. In Python:
import base64, hashlib, hmac, json, time, uuid
def b64(raw: bytes) -> str:
return base64.urlsafe_b64encode(raw).rstrip(b"=").decode()
def account_token(client_id: str, client_secret: str, account_id: str) -> str:
header = b64(json.dumps({"typ": "JWT", "alg": "HS256"}, separators=(",", ":")).encode())
payload = b64(json.dumps({
"sub": account_id,
"iat": int(time.time()),
"jti": str(uuid.uuid4()),
"azp": client_id,
}, separators=(",", ":")).encode())
signing_input = f"{header}.{payload}".encode()
key = hashlib.sha256(client_secret.encode()).digest() # the digest, not the secret
sig = hmac.new(key, signing_input, hashlib.sha256).digest()
return f"{header}.{payload}.{b64(sig)}"
A fresh jti and iat per authorization; reusing a token is not the point of it.
The same construction in the CSC specification is chapter 8.3.1, if you want the source.
Which authorizations carry it
Both legs accept it. Send it on the service leg and on the credential leg of the same flow, with the same sub, so the whole signature is attributed to one customer.
The normative text is CSC API v1, chapter 8.3.1.