Overview

Single sign-on (SSO) lets your users open Grid Orchestrator from your own portal or identity provider, signed in, without a Grid password.

Grid supports two methods.

  • HMAC-signed launch: your server signs a short form, and the browser posts it to Grid.
  • SAML 2.0: your identity provider (Okta, Microsoft Entra ID, Google Workspace, and others) posts a signed assertion to Grid.

Both methods end the same way. Grid redirects the browser to Orchestrator with a one-time code, Orchestrator redeems it, and the user lands signed in. Grid never puts a bearer token in a URL.

Choosing a method

HMAC-signed launchSAML 2.0
Who starts sign-inYour application, from a link or button you controlYour identity provider, from its app dashboard
What Grid trustsA signing key you generate in GridYour identity provider's signing certificate
Best whenYou already authenticate users in your own portalYour users already sign in through a corporate IdP
Role for new usersYou choose, per requestYou choose, per request
Return to your portalreturn_to, signed with the requestRelayState, which must match a saved portal origin
Setup lives inYour server codeYour IdP's application settings

You can enable both. Configure each one independently under the same parent customer.

Sessions, users, and roles

Every SSO sign-in names a target customer: your parent customer or one of its direct sub-organizations. For HMAC it is resource_id; for SAML it is the customerId in the ACS URL. Trust (the signing key or IdP certificate) always comes from the parent, even when the target is a sub-organization.

Grid matches the signed-in email against Orchestrator users:

SituationWhat happens
The email belongs to a user on the target customerThat user's own session opens.
The email belongs to a parent user, and the target is a sub-orgThe parent user opens the sub-organization in view-as mode, if they are allowed to view it.
The email is new to GridGrid creates the user on the target customer, then opens their session.
The email belongs to a user on a different customerSign-in is rejected (SSO_USER_CUSTOMER_MISMATCH). A sub-org user cannot open the parent.

Roles for new users. With HMAC, role sets the new user's role. If you omit it, the default is Case Reviewer. Use a role name exactly as it appears in Orchestrator, for example Observer, Processor, Reviewer, or Case Reviewer. Admin and Billing are rejected. Existing users keep their current role: role and name fields apply only when Grid creates a user.

HMAC-signed launch

1. Create your signing key

In Developer → SSO, select Create Key. Grid shows the full key once. Store it in your secrets manager. Later views show only a truncated value.

Keep the key on your server. Anyone with the key can sign in as any email on your customer. Never ship it to a browser or mobile app. If it leaks, select Rotate Key: the old key stops working immediately.

2. Build the request

Post these fields to POST {API host}/auth/sso/launch, as a form (application/x-www-form-urlencoded) or as JSON.

FieldRequiredSignedNotes
resource_idYesYesTarget customer: your parent customer ID, or a direct sub-organization id.
timestampYesYesUnix time in seconds, as a string. Must be within 120 seconds of Grid's clock.
emailYesYesThe user to sign in. Signed exactly as posted.
tokenYesNoThe signature (see below).
roleNoIf sentRole for a newly created user.
return_toNoIf sentPortal URL to return to after sign-in.
name[firstName], name[lastName]NoNoName for a newly created user. In JSON, send name: { firstName, lastName }.

3. Sign it

Join these values with a colon (:), in this order, then compute HMAC-SHA256 with your signing key. Send the digest in token as 64 lowercase hex characters.

  1. resource_id
  2. timestamp
  3. email
  4. role, only if you send role
  5. encodeURIComponent(return_to), only if you send return_to
# No role, no return_to
3f8a2c1e-5b7d-4e9a-9c2f-1a2b3c4d5e6f:1792536400:[email protected]

# With role
3f8a2c1e-5b7d-4e9a-9c2f-1a2b3c4d5e6f:1792536400:[email protected]:Observer

# With role and return_to (https://portal.example.com/grid)
3f8a2c1e-5b7d-4e9a-9c2f-1a2b3c4d5e6f:1792536400:[email protected]:Observer:https%3A%2F%2Fportal.example.com%2Fgrid

Use the key exactly as Grid shows it. The signing key is a 64-character string. Pass that string to HMAC as-is (UTF-8). Do not hex-decode it.

import { createHmac } from 'node:crypto'

export function signGridLaunch({ signingKey, resourceId, email, role, returnTo }) {
  const timestamp = String(Math.floor(Date.now() / 1000))
  const parts = [resourceId, timestamp, email]
  if (role) parts.push(role)
  if (returnTo) parts.push(encodeURIComponent(returnTo))

  const token = createHmac('sha256', signingKey).update(parts.join(':')).digest('hex')
  return {
    resource_id: resourceId,
    timestamp,
    email,
    token,
    ...(role ? { role } : {}),
    ...(returnTo ? { return_to: returnTo } : {}),
  }
}
import hashlib
import hmac
import time
from urllib.parse import quote


def sign_grid_launch(signing_key, resource_id, email, role=None, return_to=None):
    timestamp = str(int(time.time()))
    parts = [resource_id, timestamp, email]
    if role:
        parts.append(role)
    if return_to:
        # Matches JavaScript's encodeURIComponent.
        parts.append(quote(return_to, safe="-_.!~*'()"))

    token = hmac.new(signing_key.encode(), ":".join(parts).encode(), hashlib.sha256).hexdigest()
    fields = {"resource_id": resource_id, "timestamp": timestamp, "email": email, "token": token}
    if role:
        fields["role"] = role
    if return_to:
        fields["return_to"] = return_to
    return fields

4. Send the browser

The launch must be a top-level browser navigation, because Grid answers with a redirect that the browser follows. Render the signed fields into a form that submits itself, in the current tab or a new window. Do not call the endpoint from your server or with fetch.

<form id="grid-sso" method="post" action="https://api.gridverify.com/auth/sso/launch">
  <input type="hidden" name="resource_id" value="{{resource_id}}" />
  <input type="hidden" name="timestamp" value="{{timestamp}}" />
  <input type="hidden" name="email" value="{{email}}" />
  <input type="hidden" name="token" value="{{token}}" />
  <input type="hidden" name="name[firstName]" value="{{first_name}}" />
  <input type="hidden" name="name[lastName]" value="{{last_name}}" />
</form>
<script>document.getElementById('grid-sso').submit()</script>

Sign immediately before rendering the form. Each request is valid for 120 seconds and can be used once. A refreshed or re-submitted page needs a new signature.

Returning to your portal

When you send return_toGrid sends the browser back to that URL after the session is stored. Grid keeps only the origin and path (query and fragment are dropped). Because return_to is part of the signature, nobody can change it in transit.

SAML 2.0

Grid supports IdP-initiated SAML 2.0 with the HTTP-POST binding: users start from your identity provider's dashboard. Service-provider-initiated sign-in (starting from a Grid page) is not supported.

1. Configure Grid

In Developer → SSO, open the SAML configuration and enter:

  • IdP entity ID: the issuer your identity provider puts in its assertions.
  • IdP signing certificates: one to three PEM certificates. Adding a second lets you rotate without downtime (see below).
  • Portal origins (optional): https origins Grid may send users back toRelayState, up to 10.

2. Configure your identity provider

Create a SAML application in your IdP with these values. You can import them from Grid's metadata instead: GET {API host}/auth/sso/saml/{customerId}/metadata.

SettingValue
ACS URL (Single sign-on URL){API host}/auth/sso/saml/{customerId}/acs
Audience (SP entity ID)urn:grid:saml:{parentCustomerId}
BindingHTTP-POST
SigningSign the assertion (signing the outer response is optional)
NameIDEmail address format
Attributesemail, firstName, lastName, role

The audience is always the parent customer ID. The ACS URL uses the target customer ID. To sign users into a sub-organization, create a separate IdP application whose ACS URL uses that sub-organization's ID: download its metadata from /auth/sso/saml/{subOrganizationId}/metadata.

What Grid Checks

  • The issuer matches your saved IdP entity ID.
  • The assertion is signed by one of your saved certificates.
  • The audience is urn:grid:saml:{parentCustomerId}.
  • The response Destination and the assertion Recipient equal the exact ACS URL that received it.
  • The assertion is inside its validity window (2 minutes of clock skew allowed) and no more than 5 minutes old.
  • The assertion ID has not been used before.

Certificate rotation

Before your IdP switches signing certificates, add the new certificate next to the current one. Both are accepted. After the switch, remove the old certificate. Grid shows saved certificates as SHA-256 fingerprints, never the certificate itself.

Returning to your portal

To send users back to your portal after sign-in, set RelayState to the portal URL in your IdP (often called "Default RelayState"), and add that URL's origin under Portal origins. A RelayState whose origin is not saved is ignored, and the user lands in Orchestrator.

After sign-in

Both methods redirect the browser to Orchestrator:

{Orchestrator host}/auth/callback?code={one-time code}&method=sso

Orchestrator redeems the code (single use, valid for 2 minutes), stores the session, and opens the user's landing page, or your return_to / RelayState URL. You do not call any other endpoint.

Errors

When sign-in fails, Grid still redirects, with an error code instead of a one-time code. Orchestrator shows the user a sign-in error.

{Orchestrator host}/auth/callback?method=sso&error={CODE}
CodeMethodCause and fix
SSO_LAUNCH_FIELD_REQUIREDBothA required field is missing, or the SAML assertion has no email.
SSO_LAUNCH_TIMESTAMP_INVALIDHMACtimestamp is not a number or is more than 120 seconds from Grid's clock. Sync your server clock (NTP).
SSO_SIGNATURE_INVALIDBothHMAC: the canonical string or key is wrong. SAML: issuer, certificate, audience, Destination, Recipient, or timing failed.
SSO_LAUNCH_REPLAYEDBothThe token or assertion was already used. Sign a fresh request.
SSO_SIGNING_KEY_NOT_FOUNDHMACThe parent customer has no signing key. Create one in Developer → SSO.
SSO_SAML_CONFIGURATION_NOT_FOUNDSAMLThe parent customer has no SAML configuration.
SSO_MODULE_NOT_ENABLEDBothSSO is not enabled for the parent customer.
CUSTOMER_NOT_FOUNDBothresource_id or the ACS customerId does not exist.
SSO_USER_CUSTOMER_MISMATCHBothThe email belongs to a user on a different customer.
SSO_USER_NOT_ORCHESTRATORBothThe email belongs to a non-Orchestrator account (for example, an applicant).
SSO_ROLE_NOT_ASSIGNABLEHMACrole is Admin or Billing.
SSO_ROLE_NOT_FOUNDHMACrole is not a Grid role name.
FORBIDDENBothA parent user is not allowed to view the target sub-organization.

Security checklist

  • Keep the HMAC signing key in a secrets manager, sign on your server only, and rotate it if it may have been exposed.
  • Keep server clocks synced. Reject launches outside the 120-second window.
  • Sign each launch just before the browser sends it; never cache or reuse signed forms.
  • Keep Portal origins to the exact https origins you own.
  • Store your IdP's next certificate before it rotates, and remove retired certificates.
  • Remove users in Grid when they leave. SSO creates users, but it does not deactivate them.