SenseCrypt Docs
Guides

Groups and access

Control who may sign in to each application with SenseCrypt's default-closed group-to-app access gate — attaching groups, managing membership, and understanding eager revocation.

Before SenseCrypt issues a single token, it answers one question: is this user allowed to sign in to this application? That decision is the access gate, and it's driven entirely by groups. This guide shows how to open access to an app, and what happens when you close it.

The access gate is default-closed

Each application (an OIDC client or a SAML SP) has a set of attached groups. A user may sign in only if they are a member of at least one attached group.

An application with zero attached groups admits nobody.

This is deliberate: a newly created app is closed until you explicitly open it. So the very first step to enabling sign-in is attaching a group and putting users in it.

The gate is enforced consistently across the entire sign-in ceremony — at enrollment, when the phone fetches the sign-in payload, and again at the token exchange — and it re-runs on every refresh. Rejections are deliberately opaque: there's no "you're not in the group" oracle, so an outsider can't tell an unknown app from a closed one.

Open access to an application

  1. Create a group (for example acme-employees) in the admin console, if you don't already have one.
  2. Add users to the group.
  3. Attach the group to the application in the application's configuration.

That's it — members of any attached group can now sign in. You can attach multiple groups to one app (union semantics: membership in any attached group suffices), and one group can be attached to many apps.

This works the same for SAML: a SAML SP has its own attached groups (the SAML twin of an OIDC app's groups), also default-closed — an SP with zero attached groups admits nobody.

Groups do two jobs

It helps to keep the two jobs of a group distinct:

  1. Access — attaching a group to an app gates sign-in to that app (this guide).
  2. Role conferral — conferring a role on a group gives every member that role's permissions in your downstream API (see RBAC).

A group can do either or both. A common pattern: one group per team, attached to the apps that team uses and conferring the roles that team needs.

Closing access is immediate and eager

SenseCrypt does not wait for tokens to expire when you sever access. Any of the following makes the user fail the gate on their next token or refresh — and, for the stronger lifecycle events, eagerly revokes their live credentials right away:

  • Removing a user from a group — if that was their only path into an app, they can no longer sign in. Group-member removal acts eagerly on the session planes rather than merely failing the gate on the next mint: it revokes the user's refresh-token families for the apps they no longer reach, and each of those applications that opted into the access-revoked initiator receives a back-channel logout token for every live browser session of theirs that had signed in to it (with no live session there is nothing to announce). A member left in no app-attached group loses more: every remaining refresh-token family in the tenant and their OP browser session, with every application in that session's sign-in list that opted in told the same way. It deliberately leaves the device key and passkey alone: removal from one group is an authorization change, not offboarding, and the member may still reach other apps through their remaining groups. To offboard someone, say so explicitly — delete or deactivate the user.
  • Suspending a user (from the console, or active: false via SCIM) — a reversible suspend that goes further than any group change, because it severs the identity plane as well: device keys and passkeys go, alongside the refresh-token families and browser sessions. Reinstating does not restore them; the device must re-enroll. See Suspend or reinstate a user.
  • Suspending a group — its members lose what it grants, as if they had been removed from it, while the group keeps its members, applications and roles. See Suspend or reinstate a group.
  • Deleting a user — permanent, with the same full credential revocation.
  • Detaching all groups from an app — closes the app to everyone again.
  • Suspending an application — closes it to everyone at once without changing anything about it, and reinstating opens it again unchanged. See Suspend or reinstate an application.

Because the access gate re-runs on every refresh and permissions recompute on every mint, these changes propagate on the next token — you don't have to wait for access tokens to expire.

Revoke a user's device

When one of a user's phones is lost, stolen, sold or replaced, you can revoke that phone alone without touching the rest of the user's access.

  1. In the admin console, open Auth Users and select the user.
  2. Open the Devices tab. It lists the phones the user has added, with the device name and OS the phone reported when it was added, when it was added, and when it was last used. A long list scrolls inside the tab.
  3. Select Revoke next to the phone, then confirm with Revoke device.

The phone disappears from the list. Revoked devices are not listed; the audit log records each revocation as auth_user.device_key_revoked, with the user's email as the target label. Phones added before device names were recorded show as "Unknown device" until the user adds them again.

What the user sees. The phone can no longer sign in or approve requests for this user, and the passkey on it stops working. The next time it makes a request for that user, the request is refused and the SenseCrypt Authenticator removes the account from the phone and offers to add it again. An app built on the Mobile SDK receives deviceKeyRevoked, or deviceKeyTerminated when the user no longer has an application they could enroll through. The user's other phones are not affected, and neither are other accounts on the same phone. Sign-in sessions and refresh tokens the user already holds in your applications are not ended either.

Revoke or delete?

  • Revoke a device when one phone should stop working for this user. It is not a ban: the user can add the phone again by enrolling, exactly as they did the first time.
  • Delete (or suspend) the user to stop them signing in at all. That revokes every device, passkey, refresh token and browser session they hold, and they cannot enroll again unless you reactivate or re-add them, or they sign up again through an application that allows self sign-up (see Closing access is immediate and eager).

Seeing the Devices tab needs the View users capability (auth_users:read); the Revoke button appears only for administrators who also hold Edit users (auth_users:update). You can revoke a device for a user provisioned by SCIM: devices are managed in SenseCrypt, not by your directory.

From the Management API, list the user's devices and revoke one by its id:

curl https://{tenant-host}/v1/admin/auth_users/{auth_user_id}/device_keys \
  -H "Authorization: Bearer {mgmtToken}"

curl -X DELETE https://{tenant-host}/v1/admin/auth_users/{auth_user_id}/device_keys/{key_id} \
  -H "Authorization: Bearer {mgmtToken}"

The list returns items, total, limit and offset, most recently used first. The revoke returns 204 No Content; an unknown user or device, or one already revoked, returns 404.

Suspend or reinstate a user

Suspend a user to stop them signing in without deleting them, for example while someone is on leave or while you look into a lost phone. Their profile, sub, attributes, roles and group memberships are kept.

  1. In the admin console, open Auth Users and select the user.
  2. Select Suspend, then confirm with Suspend user.

The user can no longer sign in to any application in the tenant, and everything they hold is revoked at once: every device key and passkey, every refresh token, and every browser session. Each application that registered a back-channel logout URI with the session-revoked initiator receives a logout token for each of the user's browser sessions that had signed in to it (see Back-channel logout). The user shows as Suspended in the list and in their detail. The user is emailed that an administrator has suspended their account for your organisation, that their phones and passkeys were removed and their sign-ins ended, and to contact your administrator if they think it is a mistake; the email has no link or button. Sign-in attempts are refused the same way as any other refusal, so a suspended user can't tell why from the product itself. A suspended user who tries to add a phone in the SenseCrypt Authenticator receives no code, exactly as an address that was never added, and a backchannel request for them is accepted but delivers nothing; the only place they learn about the suspension is a sign-in with a phone they already set up, which shows that access was revoked.

To let them back in, open the user and select Reinstate. They sign in again as the same user, with the same sub, so your applications see the same person. Credentials revoked by the suspend stay revoked: the user adds their phone again in the SenseCrypt Authenticator, with a code sent to their email and a face check, exactly as when they first enrolled. The user is emailed that the account was reinstated and how to add the phone again, naming your own app when your tenant runs one. You don't need to upload a new photo.

Both Suspend and Reinstate need the Edit users capability (auth_users:update). A user provisioned by SCIM can't be suspended or reinstated from the console, because your identity provider owns that state and its next sync would undo the change; set the user inactive in your identity provider instead. A SCIM active change sends the user no email from SenseCrypt; your identity provider notifies its users itself. The audit log records each suspend as auth_user.suspended, with the number of credentials revoked, and each reinstate as auth_user.reinstated.

From the Management API:

curl -X POST https://{tenant-host}/v1/admin/auth_users/{auth_user_id}/suspend \
  -H "Authorization: Bearer {mgmtToken}"

curl -X POST https://{tenant-host}/v1/admin/auth_users/{auth_user_id}/reinstate \
  -H "Authorization: Bearer {mgmtToken}"

Each returns the user, with active set to false or true. Repeating a call changes nothing, sends no email and returns the current state. An unknown user returns 404, and a user provisioned by SCIM returns 409 with scim_managed.

Suspend or reinstate a group

Suspend a group to stop it granting access without deleting it or changing its members, for example while you review who belongs in it. The group keeps its members, the applications it is attached to and the roles it confers.

  1. In the admin console, open Groups and select the group.
  2. Select Suspend, then confirm with Suspend group.

Its members lose what the group grants at once, exactly as if they had been removed from it: sign-in to the applications it is attached to, unless another group still grants them that application, and the roles it confers. Their refresh tokens for those applications are revoked, and each of those applications that registered a back-channel logout URI with the access-revoked initiator receives a logout token for every live browser session of theirs that had signed in to it (see Back-channel logout). A member left with access to no application has their browser session ended. Members keep their accounts, phones and passkeys. A member of a suspended group is refused the same way as anyone who is not a member, so they can't tell why. The group shows as Suspended in the list and in its detail.

If the group is an application's sign-up group, self sign-up stops on that application while the group is suspended: a new address gets no code, exactly as on an application with sign-up off. The confirm says so, since several applications can share one sign-up group. A suspended group can't be newly chosen as an application's sign-up group; saving the sign-up settings of an application that already uses it still works.

To restore it, open the group and select Reinstate. It grants again to whoever is a member at that moment, with the phones and passkeys they already have; nothing needs re-enrolling and no application needs editing, and self sign-up reopens. Browser sessions ended by the suspend stay ended, so members sign in again.

Both Suspend and Reinstate need the Edit groups capability (groups:update). They also work on a group provisioned by SCIM: your identity provider owns the group's name and members, not what it grants, and no SCIM sync can undo a suspend. Members your identity provider adds while the group is suspended get nothing until you reinstate it. The audit log records each as group.suspended and group.reinstated, with the member count. Suspending does not change your bill: members of a suspended group still count as seats.

From the Management API:

curl -X POST https://{tenant-host}/v1/admin/groups/{group_id}/suspend \
  -H "Authorization: Bearer {mgmtToken}"

curl -X POST https://{tenant-host}/v1/admin/groups/{group_id}/reinstate \
  -H "Authorization: Bearer {mgmtToken}"

Each returns the group, with suspended_at set or null. Repeating a call changes nothing and returns the current state. An unknown group returns 404.

Suspend or reinstate an application

Suspend an application to stop it working without deleting it, for example while you investigate a leaked client secret or a misbehaving integration. Its settings, client credentials, keys, groups and branding are all kept.

  1. In the admin console, open Applications and select the application.
  2. Select Suspend in its header, then confirm.

The application stops serving at once. New sign-ins are refused, every token request is refused (refresh included), and so are backchannel requests and userinfo, all exactly as for an application that doesn't exist, so neither your users nor the application can tell it was suspended. Its refresh tokens are revoked, and sign-ins and backchannel requests already in progress are failed, so an approval given before the suspend can't be used afterwards. If the application registered a back-channel logout URI with the access-revoked initiator, it receives a logout token for each live browser session that had signed in to it (see Back-channel logout); the user's sessions with your other applications are unaffected. Users keep their phones and passkeys. The application shows as Suspended in the list and in its header.

For a SAML application, sign-ins are refused and any in progress are failed before an assertion is issued.

To restore it, open the application and select Reinstate. It serves again with the same settings and credentials; nothing needs reconfiguring, and users sign in with the phones and passkeys they already have. Tokens and sign-ins ended by the suspend stay ended.

Suspending and reinstating need Edit applications (oidc_apps:update, or saml_apps:update for a SAML application). The audit log records application.suspended / application.reinstated (saml_app.suspended / saml_app.reinstated for SAML). Suspending doesn't change your bill or your discovery document. Machine-to-machine applications have their own enable / disable switch on their page and are not suspended this way.

From the Management API:

curl -X POST https://{tenant-host}/v1/admin/oidc-apps/{client_id}/suspend \
  -H "Authorization: Bearer {mgmtToken}"

curl -X POST https://{tenant-host}/v1/admin/saml-apps/{sp_id}/reinstate \
  -H "Authorization: Bearer {mgmtToken}"

Each returns the application with suspended_at set or null. Repeating a call changes nothing and returns the current state. An unknown application returns 404.

Groups provisioned by SCIM

If you provision from an external IdP (Okta, Entra ID, Ping), the groups it creates are SCIM-managed and become read-only in the admin console — your external IdP stays the source of truth. You still attach them to apps, confer roles on them and suspend them like any other group; you just manage their membership from your IdP.

See SCIM provisioning to connect an external directory.

A worked example

Say you have a Finance app and want only the finance team to sign in, with managers able to write:

  1. Create groups finance-team and finance-managers.
  2. Attach both to the Finance app (so any member of either can sign in).
  3. Add the finance staff to finance-team; add managers to finance-managers.
  4. (Optional RBAC) Confer a Finance Reader role on finance-team and a Finance Writer role on finance-managers, so the permissions claim reflects each person's level. See RBAC.

Now, when someone leaves the finance team, removing them from finance-team (and finance-managers) both blocks future sign-in and revokes their live sessions immediately.

Common pitfalls

  • "Nobody can sign in to my new app." The app has no attached groups. Attach one.
  • "A removed user still has a valid access token." Group removal revokes refresh-token families eagerly (and, for a member left in no app-attached group, ends their browser session too), but an already-issued access token remains valid until exp unless separately revoked. Keep access-token lifetimes short.
  • "I get an opaque error, not a helpful one." By design — the gate never reveals why a user was refused, to avoid an enumeration oracle.

Using the Management API

Everything above can be driven from automation with the Management API, so an onboarding or offboarding script can open and close app access without touching the console.

You need an M2M access token. These calls run with no user present, so authenticate with a machine-to-machine app using the client-credentials grant (see Machine-to-machine). Pass the token as Authorization: Bearer {mgmtToken}. Its capabilities are the union of the app's bound roles, so the token's console role must grant the relevant capability for each call: groups:create to create a group, groups:update to manage membership, groups:read to read a group's attachments, and oidc_apps:read / oidc_apps:update to view and change an app's attached groups. A missing capability returns 403.

The base path is the tenant admin API on your tenant's issuer host: https://{tenant-host}/v1/admin. All examples use the placeholders {mgmtToken}, {tenant-host}, and resource ids like {group_id}, {client_id}, and {auth_user_id}.

1. Create a group

POST /v1/admin/groups — name is required; description is optional.

curl -X POST https://{tenant-host}/v1/admin/groups \
  -H "Authorization: Bearer {mgmtToken}" \
  -H "Content-Type: application/json" \
  -d '{"name": "acme-employees", "description": "All Acme staff"}'

Returns 201 with the created group — note the id, which you'll use to add members and to attach the group to an app:

{
  "id": "3f1c…",
  "name": "acme-employees",
  "description": "All Acme staff",
  "scim_managed": false,
  "member_count": 0,
  "app_count": 0,
  "is_signup_group": false,
  "created_at": "2026-01-01T00:00:00Z",
  "updated_at": "2026-01-01T00:00:00Z"
}

2. Add users to the group

POST /v1/admin/groups/{group_id}/members — the body is a list of auth-user ids (auth_user_ids, up to 200 per call). The call is idempotent: re-adding an existing member is a no-op.

curl -X POST https://{tenant-host}/v1/admin/groups/{group_id}/members \
  -H "Authorization: Bearer {mgmtToken}" \
  -H "Content-Type: application/json" \
  -d '{"auth_user_ids": ["{auth_user_id}", "{auth_user_id}"]}'

Returns 201 with the number of valid users resolved from your request (already-members are counted too, since the insert itself is idempotent):

{ "added": 2 }

A SCIM-managed group is read-only here: creating members on it (like renaming or deleting it) returns an error — manage its membership in your external IdP instead.

3. Attach the group to an application

For an OIDC app, PUT /v1/admin/oidc-apps/{client_id}/groups sets the app's attached groups. This is a replace-set: the group_ids you send become the complete set of attached groups, so to add a group without detaching the others, include the current ids plus the new one.

Read the current set first with GET /v1/admin/oidc-apps/{client_id}/groups, then PUT the union:

curl -X PUT https://{tenant-host}/v1/admin/oidc-apps/{client_id}/groups \
  -H "Authorization: Bearer {mgmtToken}" \
  -H "Content-Type: application/json" \
  -d '{"group_ids": ["{group_id}", "{group_id}"]}'

Returns the app's resulting attached groups:

{
  "groups": [
    { "id": "3f1c…", "name": "acme-employees", "member_count": 42 }
  ]
}

Members of any attached group can now sign in (union semantics, exactly as in the console).

SAML SPs work the same way. A SAML service provider carries its own attached groups under PUT /v1/admin/saml-apps/{sp_id}/groups with the identical {"group_ids": [...]} replace-set body and the same default-closed behavior. See the API Reference for the full SAML admin surface.

4. Check which apps a group gates (reverse view)

GET /v1/admin/groups/{group_id}/apps lists the live apps whose sign-in this group opens — across both protocols.

curl https://{tenant-host}/v1/admin/groups/{group_id}/apps \
  -H "Authorization: Bearer {mgmtToken}"
{
  "apps": [
    { "protocol": "oidc", "id": "app_aBcD…", "name": "Finance", "subtitle": "app_aBcD…" }
  ]
}

5. Close access

To sever one user's access, remove them from the group with DELETE /v1/admin/groups/{group_id}/members/{auth_user_id}. As in the console, this is the eager session severance, scoped to what the membership was conferring: the refresh-token families for the applications the user can no longer reach through any remaining live group, plus — only for a user left in no app-attached group — every remaining refresh-token family in the tenant and their OP browser session. Device keys and passkeys are never cut here; a user who still reaches other applications keeps them, keeps their browser session, and keeps those applications. Offboarding is said explicitly: delete or deactivate the user.

curl -X DELETE https://{tenant-host}/v1/admin/groups/{group_id}/members/{auth_user_id} \
  -H "Authorization: Bearer {mgmtToken}"

Returns 204 No Content.

To close an app to everyone, detach all its groups by sending an empty group_ids set. This is the narrower, per-app revoke: for users who thereby lose access it cuts that one app's refresh-token families and tells the app through back-channel logout — one token per live browser session that had signed in to it, if the app opted into access-revoked — and nothing else — device keys and passkeys span apps and are left alone, and unlike a group removal a detach never ends the browser session, even for a member whose last app this was:

curl -X PUT https://{tenant-host}/v1/admin/oidc-apps/{client_id}/groups \
  -H "Authorization: Bearer {mgmtToken}" \
  -H "Content-Type: application/json" \
  -d '{"group_ids": []}'

Conferring roles on a group (the group's second job) is a separate endpoint and is covered in RBAC: roles and permissions, not here.

On this page