Authentication
Authenticate both directions of the integration surface — bearer tokens for the SCIM requests you send, HMAC signatures for the webhook deliveries you receive.
SCIM bearer tokens
Every SCIM request carries a tenant-scoped bearer token:
GET /functions/v1/scim-users?count=1 HTTP/1.1
Host: chgrkvftjfibufoopmav.supabase.co
Authorization: Bearer tqc_scim_3q2-8h…Token properties:
- Format:
tqc_scim_<base64url-encoded 32 random bytes>. - Tenant-scoped: the tenant is resolved from the token itself, never from the URL or body. One token can only ever read or write its own tenant.
- Hashed at rest: only a PBKDF2-SHA256 hash is stored. The plaintext is displayed once at issuance and cannot be recovered — if it is lost, issue a new token.
- Revocable and expirable: revoked or expired tokens receive
401immediately.
Tokens are issued from the SCIM administration area; issuance and revocation are step-up-protected admin operations. See SCIM provisioning for the operational workflow.
Treat the token as a secret
- Store it only in your identity provider's credential field or a secrets manager.
- Never commit it to source control, paste it into tickets, or log it. The platform redacts
tqc_scim_substrings from its own sync logs; do the same on your side.
Rotation
Multiple unrevoked tokens per tenant are allowed, so rotation has no downtime: issue the new token, update the IdP, confirm syncs succeed, then revoke the old token.
Webhook signature verification
Webhook deliveries are authenticated in the other direction: The Quantum Club signs each request so your receiver can verify origin and integrity.
Each delivery includes:
| Header | Content |
|---|---|
X-Webhook-Signature | Hex-encoded HMAC-SHA256 of the raw request body, keyed with your endpoint's shared secret |
X-Webhook-Event | The event type, for example meeting.ended |
X-Webhook-Delivery-ID | Unique delivery identifier, for deduplication |
Verify by computing the same HMAC over the raw body bytes and comparing in constant time:
import { createHmac, timingSafeEqual } from 'node:crypto';
function verifyWebhook(rawBody, signatureHeader, secret) {
const expected = createHmac('sha256', secret)
.update(rawBody) // raw bytes, before any JSON parsing
.digest('hex');
const a = Buffer.from(expected, 'hex');
const b = Buffer.from(signatureHeader, 'hex');
return a.length === b.length && timingSafeEqual(a, b);
}Compute the HMAC over the raw request body exactly as received. Parsing the JSON and re-serializing it can reorder keys or change whitespace, which produces a different digest and a false verification failure.
Rules for receivers:
- Reject any request whose signature does not verify; do not process the payload first.
- Use a constant-time comparison to avoid timing side channels.
- Keep the shared secret out of client-side code and logs.
For endpoint requirements, retry behavior, and delivery semantics, see webhooks. For the event catalog, see webhook events.
Your SCIM token lives only in the IdP and a secrets store, and your webhook receiver rejects unsigned or mis-signed requests using a constant-time comparison over the raw body.

