SSO Connect
Let users who are already authenticated on your existing website or app sign into the marketplace without creating a separate account. Tradly SSO uses HMAC-SHA256 signed payloads — no SAML metadata or OAuth app registration required.
How it works
- 1Marketplace calls
POST /v1/users/ssowith the current domain. Tradly creates a nonce, signs a payload with your shared secret, and returns aredirect_urlpointing to your identity endpoint. - 2App opens the redirect URL. Your endpoint receives
?identity=tradly&payload=<b64>&sig=<hmac>, verifies the signature, looks up the logged-in user, and builds a return payload. - 3Your endpoint signs the return payload (JSON → base64, HMAC-SHA256 with the same secret) and redirects to the
return_urlfrom the original payload. - 4App calls
POST /v1/users/sso_returnwith the payload and signature. Tradly verifies, validates the nonce (10-minute window), and logs in or registers the user. Returns auth + refresh tokens.
Configuration
Three keys must be set via POST /v1/configs before SSO works.
sso_enabledBoolean. Must be true — without this both endpoints return ACTION_NOT_ALLOWED.
sso.sso_secretShared HMAC secret. Use a cryptographically random string of at least 32 characters. Must match on both sides.
sso.sso_redirect_urlHTTPS URL of your identity endpoint. Tradly appends ?identity=tradly&payload=…&sig=… to this URL.
POST /v1/configs
Content-Type: application/json
{
"configs": [
{ "key_group": "sso", "key": "sso_enabled", "value": true, "secured": false },
{ "key_group": "sso", "key": "sso_secret", "value": "your-32-char-random-secret", "secured": true },
{ "key_group": "sso", "key": "sso_redirect_url", "value": "https://your-site.com/sso", "secured": false }
]
}Endpoints
POST /v1/users/sso — initiate
No auth required. Call before the user has a session.
POST /v1/users/sso
tenant_id: <tenant_id> (header)
{ "domain": "marketplace.your-client.com" }
// Response
{
"status": true,
"data": {
"redirect_url": "https://your-site.com/sso?identity=tradly&payload=<b64>&sig=<hmac>"
}
}POST /v1/users/sso_return — complete
No auth required. Call after your identity endpoint redirects back.
POST /v1/users/sso_return
tenant_id: <tenant_id> (header)
{
"payload": "<base64-encoded-return-payload>",
"sig": "<hmac-sha256-hex>"
}
// Response
{
"status": true,
"data": {
"user": {
"id": "usr_abc123",
"first_name": "Jane",
"last_name": "Doe",
"email": "jane@example.com",
"email_verified": true,
"key": {
"auth_key": "<jwt>",
"refresh_key": "<jwt>",
"firebase_token": "<token>"
}
}
}
}Return payload fields
Your identity endpoint must send this JSON, signed and base64-encoded.
| Field | Type | Required | Description |
|---|---|---|---|
| nonce | string | required | Echo back the nonce from the inbound payload. Tradly uses this to match and expire the SSO request. |
| external_id | string | required | Your stable unique ID for the user. Becomes the Tradly username. Must never change. UUID or numeric ID works. |
| string | optional | Stored on first registration only. | |
| email_verified | boolean | optional | Defaults to false. |
| first_name | string | optional | Stored on registration. |
| last_name | string | optional | Stored on registration. |
Signing the return payload
// Node.js example
const payloadStr = JSON.stringify({
nonce: inbound.nonce,
external_id: String(user.id),
email: user.email,
email_verified: user.emailVerified,
first_name: user.firstName,
last_name: user.lastName,
})
const sig = crypto.createHmac('sha256', SSO_SECRET).update(payloadStr).digest('hex')
const encoded = Buffer.from(payloadStr).toString('base64')
res.redirect(`${inbound.return_url}?payload=${encoded}&sig=${sig}`)Errors
| HTTP | Code | Cause |
|---|---|---|
| 412 | ACTION_NOT_ALLOWED | sso_enabled is false or missing. |
| 412 | 108 | HMAC signature mismatch — wrong secret or payload was modified. |
| 412 | 109 | Nonce not found, already used, 10-minute window expired, or external_id missing. |