Skip to content

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 โ€‹

ClaimValueDescription
algRS256RSA-SHA256 signature, the only algorithm we accept.
typJWTMust be the literal string JWT.
x5t#S256thumbprintSHA-256 thumbprint of your certificate. Available in the dashboard.

JWT payload โ€‹

ClaimDescription
subRequest method followed by a space and the full path, including query parameters. E.g. POST /v1/subscriptions.
audDomain of the endpoint you are calling. E.g. api.quarzo-life.com.
iatUnix timestamp at which the token was created. We accept a maximum clock skew of 5 seconds.
jtiUnique identifier for the token. Must be a UUID and must differ for every request.
secSecret obtained during setup in the dashboard. Custom claim, not part of the JWT spec.
dig#S256base64url(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:

http
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:

json
// Header
{
  "alg": "RS256",
  "typ": "JWT",
  "x5t#S256": "3L7wevm3oDznA14ZcxIaasp4RHaYRe7ZqmgqScCMY74"
}
json
// 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):

js
import crypto from 'crypto'

function bodyDigest(body) {
  return crypto
    .createHash('sha256')
    .update(body)
    .digest('base64url')
}