{"record":{"id":"8881a7b733d70fd4","repo":"stalwartlabs/stalwart","slug":"forbidden","errorCode":null,"errorMessage":"Forbidden","messagePattern":"Forbidden","errorType":"http","errorClass":"ScimError","httpStatus":403,"severity":"error","filePath":"crates/scim-proto/src/message/error.rs","lineNumber":167,"sourceCode":"\n    pub fn invalid_count(detail: impl Into<Cow<'static, str>>) -> Self {\n        Self::bad_request(ScimType::InvalidCount, detail)\n    }\n\n    pub fn uniqueness(detail: impl Into<Cow<'static, str>>) -> Self {\n        Error {\n            status: 409,\n            scim_type: Some(ScimType::Uniqueness),\n            detail: Some(detail.into()),\n        }\n    }\n\n    pub fn unauthorized() -> Self {\n        Error::new(401)\n    }\n\n    pub fn forbidden(detail: impl Into<Cow<'static, str>>) -> Self {\n        Error::new(403).with_detail(detail)\n    }\n\n    pub fn not_found() -> Self {\n        Error::new(404)\n    }\n\n    pub fn conflict(detail: impl Into<Cow<'static, str>>) -> Self {\n        Error::new(409).with_detail(detail)\n    }\n\n    pub fn precondition_failed() -> Self {\n        Error::new(412)\n    }\n\n    pub fn max_operations_exceeded(max_operations: usize) -> Self {\n        Error::new(413).with_detail(format!(\n            \"The number of operations in the bulk request exceeds the maxOperations ({max_operations}).\"\n        ))","sourceCodeStart":149,"sourceCodeEnd":185,"githubUrl":"https://github.com/stalwartlabs/stalwart/blob/e96200385781a6a9995a8b839ac27d6c75a983ee/crates/scim-proto/src/message/error.rs#L149-L185","documentation":"SCIM protocol constructor `Error::forbidden(detail)` builds a SCIM Error with HTTP status 403 and a human-readable detail. It means authentication succeeded but the authenticated principal is not allowed to perform the operation. Returned by server code via this constructor; the detail string explains what was denied.","triggerScenarios":"Server code calls `Error::forbidden(detail)` (i.e. `Error::new(403).with_detail(detail)`) when a request targets a resource or operation outside the caller's granted scopes/permissions — e.g. reading another tenant's Users, writing with a read-only token.","commonSituations":"Access token lacking required scope (e.g. scim:write) for a PUT/POST/PATCH; non-admin user hitting admin-only SCIM endpoints; cross-tenant access attempts in multi-tenant deployments; RBAC policy changes revoking previously granted roles.","solutions":["Read the `detail` field in the SCIM error body — it names the denied operation or resource.","Request the missing scope/role via the OAuth2 flow and retry with a new token.","Confirm the target resource belongs to the caller's tenant/organization.","If you are the server author, ensure 403 details avoid leaking information about resources the caller should not know exist."],"exampleFix":"// before\nlet token = get_token(&client_id, scopes=[\"scim:read\"]);\nclient.put(\"/scim/v2/Users/2819\", body).bearer_auth(token) // 403\n// after\nlet token = get_token(&client_id, scopes=[\"scim:read\", \"scim:write\"]);\nclient.put(\"/scim/v2/Users/2819\", body).bearer_auth(token)","handlingStrategy":"fallback","validationCode":"// check required scope client-side before a write request\nfn can_write(scopes: &[String]) -> bool {\n    scopes.iter().any(|s| s == \"scim:write\" || s == \"scim:admin\")\n}","typeGuard":null,"tryCatchPattern":"let resp = client.put(&url).bearer_auth(&token).json(&body).send().await?;\nif resp.status() == StatusCode::FORBIDDEN {\n    let scim_err: ScimError = resp.json().await?;\n    anyhow::bail!(\"SCIM forbidden: {:?}\", scim_err.detail); // surface detail, don't retry blindly\n}","preventionTips":["Request all needed scopes (read + write) up front when provisioning tokens.","Read the SCIM error detail — it usually names the denied resource or operation.","Do not auto-retry 403s; they require a permission change, not a retry.","In multi-tenant systems, verify the target resource ID belongs to the caller's tenant first."],"tags":["scim","http-403","authorization","permissions"],"backgroundTag":"permission-denied","analyzedSha":"e96200385781a6a9995a8b839ac27d6c75a983ee","analyzedAt":"2026-09-06T22:07:17.982Z","contentChangedAt":"2026-09-06T22:07:17.982Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}