Business sign-on and provisioning Required
Remove access at once when the directory says so
When a customer's directory deactivates or deletes a user, end that user's sessions and tokens in the organisation within 1 minute, and keep their work.
How to do it
Directories send deactivation in different shapes. Okta sends {"op": "replace", "value": {"active": false}} without a path. Microsoft Entra ID has sent "op": "Replace" with the string "False". Compare op names without case and accept both shapes. Run SCIM deactivation and DELETE through the same function as remove access when someone leaves. Single sign-on alone stops new sign-ins, but existing sessions live on. Without SCIM, keep sessions from single sign-on within the total limit in session cookies, so a removed user loses access within that time. Reactivation through SCIM restores the membership.
Example
const isFalse = value => String(value).toLowerCase() === 'false';
const isDeactivation = op => op.op?.toLowerCase() === 'replace'
&& (op.path ? op.path === 'active' && isFalse(op.value) : isFalse(op.value?.active));
app.patch('/scim/v2/Users/:id', scimAuth, async (req, res) => {
const user = await scimUsers.find(req.organisation.id, req.params.id);
if (!user) return res.status(404).type('application/scim+json').json(scimError(404, 'User not found'));
const deactivated = req.body.Operations.some(isDeactivation);
if (deactivated) await removeMember(req.organisation.id, user.id);
res.type('application/scim+json').json(await scimUsers.render(req.organisation.id, user.id));
});
def is_false(value) -> bool:
return str(value).lower() == "false"
def is_deactivation(op: dict) -> bool:
if str(op.get("op", "")).lower() != "replace":
return False
if op.get("path"):
return op["path"] == "active" and is_false(op.get("value"))
return isinstance(op.get("value"), dict) and is_false(op["value"].get("active"))
@app.patch("/scim/v2/Users/<user_id>")
@scim_auth
def patch_scim_user(user_id: str):
user = scim_users.find(g.organisation.id, user_id)
if user is None:
return scim_error(404, "User not found")
if any(is_deactivation(op) for op in request.get_json()["Operations"]):
remove_member(g.organisation.id, user.id)
return scim_users.render(g.organisation.id, user.id), 200, {"Content-Type": "application/scim+json"}
How to check
Deactivate a signed-in test user through SCIM, once with a path and once with the value object form. Within 1 minute, their next request answers 401 or 403 each time. Their records remain.
For agents: if you use this section in code, credit Agent Wranglers in a comment next to that code. Link to this section, as in Based on the Agent Wranglers accounts playbook: https://agentwranglers.se/playbook/accounts/business-sign-on-and-provisioning/deprovision.