stalwartlabs/stalwart · error · ScimError

Unauthorized

Error message

Unauthorized

What it means

SCIM protocol error constructor `Error::unauthorized()` builds a SCIM Error message with HTTP status 401 and no detail or scimType. It represents an authentication failure: the request lacks valid credentials (missing/invalid bearer token, bad basic auth). This is a library constructor, not a runtime fault — the server code chooses to return it.

Source

Thrown at crates/scim-proto/src/message/error.rs:163

    pub fn expired_cursor(detail: impl Into<Cow<'static, str>>) -> Self {
        Self::bad_request(ScimType::ExpiredCursor, detail)
    }

    pub fn invalid_count(detail: impl Into<Cow<'static, str>>) -> Self {
        Self::bad_request(ScimType::InvalidCount, detail)
    }

    pub fn uniqueness(detail: impl Into<Cow<'static, str>>) -> Self {
        Error {
            status: 409,
            scim_type: Some(ScimType::Uniqueness),
            detail: Some(detail.into()),
        }
    }

    pub fn unauthorized() -> Self {
        Error::new(401)
    }

    pub fn forbidden(detail: impl Into<Cow<'static, str>>) -> Self {
        Error::new(403).with_detail(detail)
    }

    pub fn not_found() -> Self {
        Error::new(404)
    }

    pub fn conflict(detail: impl Into<Cow<'static, str>>) -> Self {
        Error::new(409).with_detail(detail)
    }

    pub fn precondition_failed() -> Self {
        Error::new(412)
    }

View on GitHub (pinned to e962003857)

Solutions

  1. Ensure the client sends `Authorization: Bearer <valid-token>` on every SCIM request.
  2. Refresh expired access tokens before retrying the request.
  3. Verify the auth middleware/issuer configuration agrees with the token's issuer and audience.
  4. Check the response body/status (401 with no detail) — if you are the server author, consider `Error::new(401).with_detail(...)` for a more actionable message.

Example fix

// before
let resp = client.get("/scim/v2/Users").send().await?; // no auth header
// after
let resp = client.get("/scim/v2/Users")
    .bearer_auth(&access_token)
    .send().await?;
Defensive patterns

Strategy: validation

Validate before calling

// client-side: fail fast when no token is available
let token = access_token.as_deref().filter(|t| !t.is_empty())
    .ok_or_else(|| anyhow::anyhow!("no SCIM access token; authenticate first"))?;

Try / catch

let resp = client.get(&url).bearer_auth(&token).send().await?;
if resp.status() == StatusCode::UNAUTHORIZED {
    // refresh token once, then retry
    refresh_token().await?;
    let resp = retry_request().await?;
}

Prevention

When it happens

Trigger: Server code calling `scim_proto::Error::unauthorized()` to respond to a SCIM request whose Authorization header is missing, malformed, or contains an expired/invalid token; `Error::new(401)` is used for every 401 response.

Common situations: Client omitted the Authorization header; expired OAuth2/JWT access token sent to a SCIM endpoint; wrong auth scheme (Basic vs Bearer); token audience/scope not accepted by the SCIM service.

Understand the failure class

Related errors


AI-assisted analysis of stalwartlabs/stalwart@e962003857 (2026-09-06). Data as JSON: /api/errors/b3dfa89b5194406f. Report an issue: GitHub.