Skip to main content

Single sign-on with JWT

With JWT single sign-on your application authenticates the user, then redirects them to Deeper Signals with a short-lived signed token. Deeper Signals finds the user by ds_id or e-mail, creates the account if it does not exist yet, signs them in and, optionally, runs a playbook that assigns assessments, groups and projects.

The SSO URL​

https://app.deepersignals.com/sso/{jwt}?error_url=https://your-app.example.com/sso-error
PartDescription
{jwt}The signed token described below, URL-safe (standard compact JWT encoding).
error_url (optional)Where to send the user if the token is invalid or expired. The domain must be one of the redirect domains registered for your API token.

Building the token​

Sign the JWT with HMAC SHA-256 (HS256) using your account API token as the secret. Use the token string as-is; do not base64-encode it first.

Generate the token on your server, never in client-side JavaScript, so the API token is not exposed.

Claims​

ClaimRequiredDescription
iatyesIssued-at time, seconds since the UNIX epoch.
expyesExpiry, seconds since the UNIX epoch. Tokens are valid for at most 15 minutes after iat; a shorter window is fine.
client_idyesThe client id that was issued together with your API token.
emailyesThe user's e-mail. Used as the unique identifier when ds_id is absent.
first_nameyesThe user's first name.
last_nameyesThe user's last name.
ds_idnoThe user's Deeper Signals id, if you stored it from the User Created webhook or an API response. Takes precedence over email for the lookup.
localenoTwo-letter language code. Defaults to en; unsupported languages fall back to English.
identitynoThe playbook identity to run after sign-in: assigns assessments, groups and projects.

Example payload​

{
"iat": 1760000000,
"exp": 1760000600,
"client_id": "6hUsd268",
"email": "user@example.com",
"first_name": "Jane",
"last_name": "Doe",
"ds_id": 15768,
"locale": "en",
"identity": "60ae0168f2eb7"
}

Code samples​

import jwt from 'jsonwebtoken';

const token = jwt.sign(
{
client_id: process.env.DS_CLIENT_ID,
email: user.email,
first_name: user.firstName,
last_name: user.lastName,
locale: 'en',
identity: 'YOUR_PLAYBOOK_IDENTITY',
},
process.env.DS_API_TOKEN,
{ algorithm: 'HS256', expiresIn: '10m' } // sets iat and exp
);

res.redirect(`https://app.deepersignals.com/sso/${token}?error_url=${encodeURIComponent('https://your-app.example.com/sso-error')}`);

What happens on the platform​

  1. The signature, exp and client_id are verified. Failures redirect to error_url when given.
  2. The user is looked up by ds_id, otherwise by email, within your account.
  3. A missing user is created with the names and locale from the token.
  4. If identity names a playbook, its assessments, groups and projects are applied.
  5. The user lands signed in on the platform.

Troubleshooting​

SymptomLikely cause
Redirected to error_url immediatelySignature mismatch (wrong secret, base64-encoded secret, wrong algorithm) or client_id not matching the token.
Works in tests, fails in productionClock skew: iat in the future or exp more than 15 minutes ahead. Sync your server clock.
A second user is createdemail differs in case or spelling from the existing user. Send ds_id once you have it.
User signed in but no assessment assignedidentity missing or not a playbook of this account. Check GET /playbook.