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
- Create a group (for example
acme-employees) in the admin console, if you don't already have one. - Add users to the group.
- 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:
- Access — attaching a group to an app gates sign-in to that app (this guide).
- 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 eagerly revokes the user's device keys and refresh-token families immediately, rather than merely failing the gate on the next mint.
- Suspending a user (
active: false, e.g. via SCIM) — a reversible suspend that immediately revokes device keys and refresh-token families. Re-activating does not restore them; the device must re-enroll. - 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.
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:
- Create groups
finance-teamandfinance-managers. - Attach both to the
Financeapp (so any member of either can sign in). - Add the finance staff to
finance-team; add managers tofinance-managers. - (Optional RBAC) Confer a
Finance Readerrole onfinance-teamand aFinance Writerrole onfinance-managers, so thepermissionsclaim 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 device keys and refresh tokens eagerly, but an already-issued access token remains valid until
expunless 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, tenant-wide credential severance — it revokes every device key and refresh-token family the user holds (not a scoped group-leave), so a user still in other groups must re-authenticate.
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 — it cuts only the detached app's sessions for users who thereby lose access (device keys, which span apps, are left alone):
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.
Related
- Authorization — the access gate in the context of the full model.
- RBAC: roles and permissions — conferring roles through groups.
- SCIM provisioning — provisioning groups from your IdP.
- Refresh tokens and sessions — how revocation propagates.
RBAC: roles and permissions
Emit fine-grained authorization for your own APIs with SenseCrypt's Auth0-style RBAC — resource servers, permissions, roles, and the permissions claim your API enforces.
Customize the sign-in experience
Brand the SenseCrypt sign-in page per OIDC application — organization name, primary and accent colors, and a logo you upload — so users see your identity, not a generic prompt.