The secret
Each managed account has its ownpartner_sso_secret, an HS256 key.
- It is minted at provisioning and returned once as
partner_sso_secret(and on exact idempotent replays). - Rotate it, or mint the first one for an account provisioned before partner SSO existed, with
POST /api/partner/v1/accounts/{id}/sso_secret/rotate. Tokens signed with the old secret are refused at once. A suspended account’s secret can still be rotated, as its API tokens can, so you can answer a leak before reactivating it. An archived account is read-only: rotation answers409(account_archived) and changes nothing.
200
The token
An HS256 JWT. Any other algorithm, includingnone, is refused.
SpitShake allows 5 seconds of clock skew on
iat and exp. Mint the token when the user clicks
the button, not when the page loads.
return_to must be a relative path such as /templates or /templates/123/edit. A value
starting with //, containing a backslash, a scheme (https:, javascript:), spaces, CR/LF or
non-ASCII characters is refused, and so is the whole sign-in.
Sending the user
GET or POST https://spitshake.io/sso/partner with the token in a token parameter, in the
query string or a form body (application/x-www-form-urlencoded). Send it in one place only: a
query token and a different form token are refused with 400. Prefer a form POST: it keeps the
token out of browser history and access logs. Open it as a page (a link, a form, window.open),
not from an image, iframe or fetch; those are refused. A HEAD request (link previews,
scanners) gets 405 and neither signs anyone in nor uses up the token.
- Not signed in, or signed in as the same user: SpitShake starts the session and redirects
(
303) toreturn_to. - Signed in as someone else: SpitShake does not switch silently. It shows a page naming the current user and workspace and the one the token is for. Only after the user clicks Continue (a same-origin form with a CSRF token) is the token used and the session switched. The token must still be inside its minute when they click.
POST from your console is a cross-site request, so the browser does not send its SpitShake
session cookie with it. SpitShake answers it with a page that immediately resubmits the same
token from SpitShake’s own origin, which does carry the cookie, and then proceeds as above. With
JavaScript off, the page shows a Continue button instead.
On any failure SpitShake returns the same 401 page and never says which check failed. The
exceptions are for a correctly signed token only: a role SpitShake cannot grant (the page says
read-only access is not available), the account’s sign-in limit (429, see
Rate limits) and the plan’s user limit (403, see Users and roles).
The reason is in SpitShake’s server log and, once the token’s signature has been verified, in the
account’s audit trail as partner.sso_login_failed. A correctly signed token is audited only
until a minute after its exp (plus the 5 s skew), whatever it is refused for; after that it is
only logged, so a token found later in a browser history cannot fill the audit trail. A token
whose exp is not an integer, or is further ahead than any valid token’s could be (60 s after an
iat at most 5 s ahead), is never audited, only logged.
Minting a token
Users and roles
- The first sign-in for a
subcreates a SpitShake user in that account; later sign-ins with the samesubreturn to it. The samesubin two accounts is two separate users. - SSO signs in only users it created itself. It never links to an existing SpitShake user, even
one with the same email. Every SSO user’s own SpitShake email is a non-routable placeholder
(
sso-…@partner-sso.invalid), whatever you send. - An SSO user signs in only through you. SpitShake refuses their password sign-in and their “Forgot password” requests, answering exactly as it does for an address it does not know, and they cannot set a SpitShake email or password of their own, whatever address they have. Disabling them is how you cut their access.
- The
emailyou send is the user’s contact: SpitShake sends it the mail it would send to the user’s email (security alerts to the account’s admins, and a document they sent being completed or declined). Each sign-in replaces it with the token’semail, and a sign-in without one, or with one that is not a valid address, clears it; with none, that mail is not sent. It is never verified, and it claims nothing: the same address can be another SSO user’s contact, or anyone’s SpitShake email, in any account. SpitShake never finds, signs in or sends a reset link to a user by it. - A new user is created confirmed, with a random password nobody knows. No welcome, confirmation or password-reset email is sent. Being confirmed gives them nothing beyond SSO: it only matters to password sign-in.
- A first sign-in counts against the plan’s user limit, as an invitation does. When the account
is full, the sign-in is refused with
403and a page that says so; users who already exist still sign in. Size the plan for the users you will send. - The role follows the token on every sign-in, demotion included, with one exception: the
account’s last active admin is never demoted, so the account always has an admin. They stay
admin, and the sign-in’spartner.sso_loginevent recordsrole_not_applied: "last_active_admin".adminandeditormap to the same SpitShake roles. - There is no read-only access. Every SpitShake role can create and send documents, so a
token with
role: "viewer", with norole, or with any other value is refused rather than signed in with more access than you meant to grant. The refusal page says that read-only access is not available, so you can tell it apart from an expired link. Map your read-only users to no button, or toeditorif they should be able to send. - A user who is archived in SpitShake, or disabled by you, cannot sign in by SSO.
- SpitShake MFA is not asked for: your own sign-in is the gate. Send
amrso the audit trail shows how the user authenticated with you. - Because an SSO user has no password they know, they cannot do what SpitShake confirms with the
user’s password: turning off MFA, rotating the identity-handoff secret, or deleting the account.
The API answers those requests with
403and a message that says so (never the401of a wrong password, which would send the app to the password sign-in page), and the account settings page shows that message in place of its delete button. An admin who signs in with a password does those.
Disabling a user
When you remove a user on your side, disable them in SpitShake. Disabling archives the user, which blocks SSO and password sign-in. It also ends their open sessions: a tab that is already open is signed out on its next request. Their API and OAuth tokens are revoked. Thesubject
is the token’s sub, URL-encoded. Disabling is a revocation, so it also works on a suspended
or archived account.
200 (one account)
200 (every account)
sessions_purged counts the records deleted. If the
session store is unavailable, session_purge_failed is true, but the user is still disabled
and every one of their sessions is still refused on its next request.
Both calls are idempotent. A subject that is unknown or already disabled answers 200 with
"already": true and changes nothing, so retrying is always safe. The every-account form reaches
only your own accounts, and accounts_affected lists those where something changed. Each change
writes a partner.sso_user_disabled audit event with the subject and the counts of sessions and
tokens ended. Only users created by SSO are reached; other users of the account are never
touched. There is no re-enable through the Partner API: an account admin can restore an archived
user in SpitShake, and their tokens stay revoked.
Refusals
Besides an invalid token, SSO is refused for an account that is not partner-managed, has no partner SSO secret, belongs to a suspended partner, or is itself suspended or archived. Each of these gets the same generic401 page as an invalid token.
Each successful sign-in writes a partner.sso_login audit event with the user, sub, role,
amr, a SHA-256 hash of the jti, and the client IP. Tokens are never logged or stored.
Rate limits
30 requests per minute per client IP (aPOST launch takes two), and 60 sign-ins per minute per
account. Only fresh tokens correctly signed for that account count against the account limit:
forged tokens and replays of an already-used token are refused before it is touched, so neither
can lock your users out. Over either limit SpitShake answers 429 with Retry-After. A token
refused by the account limit is not used up, so the same token can be retried until it expires.
