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.
  • Deleting a user — permanent, with the same full credential revocation.
  • Detaching all groups from an app — closes the app to everyone again.

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. Sign-in attempts are refused the same way as any other refusal, so a suspended user can't tell why. A suspended user who tries to add a phone in the SenseCrypt Authenticator still receives the code, and once they enter it the app tells them the account is suspended and to contact an administrator; they are not led into self sign-up, and nothing is said to anyone who does not hold the mailbox.

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. 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. 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 and returns the current state. An unknown user returns 404, and a user provisioned by SCIM returns 409 with scim_managed.

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 and confer roles on 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