Authentication โ
Our authentication is based on JSON Web Token (JWT) and JSON Web Signature (JWS).
Regardless of which programming language you are using, there should be a library to handle the cryptographic part for you. All you need is to provide the correct header and payload claims.
JWT header โ
| Claim | Value | Description |
|---|---|---|
alg | RS256 | RSA-SHA256 signature, the only algorithm we accept. |
typ | JWT | Must be the literal string JWT. |
x5t#S256 | thumbprint | SHA-256 thumbprint of your certificate. Available in the dashboard. |
JWT payload โ
| Claim | Description |
|---|---|
sub | Request method followed by a space and the full path, including query parameters. E.g. POST /v1/subscriptions. |
aud | Domain of the endpoint you are calling. E.g. api.quarzo-life.com. |
iat | Unix timestamp at which the token was created. We accept a maximum clock skew of 5 seconds. |
jti | Unique identifier for the token. Must be a UUID and must differ for every request. |
sec | Secret obtained during setup in the dashboard. Custom claim, not part of the JWT spec. |
dig#S256 | base64url(sha256(body)) โ required only when the request has a body. Omit for GET requests. Custom claim, not part of the JWT spec. |
Once signed, include the token in every HTTP request as a standard bearer token:
Authorization: Bearer <token>Private key
The JWT must be signed with the private key you generated during onboarding. See Getting started for setup instructions.
Clock skew
We reject tokens with an iat more than 5 seconds away from our server time. Make sure your system clock is synchronized (NTP).
Example โ
JWT header and payload for a POST /v1/subscriptions request:
// Header
{
"alg": "RS256",
"typ": "JWT",
"x5t#S256": "3L7wevm3oDznA14ZcxIaasp4RHaYRe7ZqmgqScCMY74"
}// Payload
{
"sub": "POST /v1/subscriptions",
"aud": "api.quarzo-life.com",
"iat": 1657055009,
"jti": "60984f46cb4-9dcd-4562-8c6c-85525620b",
"sec": "406d2945b7a2b31e4fb3fa2029d646c94f09a6d0ae29144380775b5471c4e846",
"dig#S256": "2gPMsMklkOzXyn028W6NgWwrnaN0kJaiy7FMJcR0Ek"
}How dig#S256 is computed
Hash the raw request body with SHA-256, then encode the result using base64url (no padding):
import crypto from 'crypto'
function bodyDigest(body) {
return crypto
.createHash('sha256')
.update(body)
.digest('base64url')
}