Skip to content

feat(scim): add GET /scim/v2/Users/{id} - #2671

Open
xlgmokha wants to merge 17 commits into
xlgmokha/auth-1368afrom
xlgmokha/auth-1368b
Open

feat(scim): add GET /scim/v2/Users/{id}#2671
xlgmokha wants to merge 17 commits into
xlgmokha/auth-1368afrom
xlgmokha/auth-1368b

Conversation

@xlgmokha

@xlgmokha xlgmokha commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

What kind of change does this PR introduce?

Feature. Adds GET /scim/v2/Users/{id}, the first SCIM resource endpoint.

https://linear.app/supabase/issue/AUTH-1368/scim-users-get-by-id

What is the current behavior?

/scim/v2 serves the three discovery endpoints and nothing else.

Before:

curl -H "Authorization: Bearer $TOKEN" http://localhost:9999/scim/v2/Users/64db64f9-15e5-4da2-a06f-0627c9c76baa | jq
{
  "schemas": [
    "urn:ietf:params:scim:api:messages:2.0:Error"
  ],
  "detail": "A valid SCIM bearer token is required",
  "status": "401"
}

What is the new behavior?

scim.Server.Authenticate resolves the SSO provider that owns the presented bearer token and puts it on the request context. UserByID then returns that provider's user:

case response
user belongs to the token's provider 200 + User resource, application/scim+json
missing, malformed, or unknown token 401 + WWW-Authenticate: Bearer
token resolves but the provider is disabled 403
unknown id, another provider's user, soft deleted user, malformed id 404, identical body

Discovery stays unauthenticated. Authenticate is scoped to the /Users route so a deployment with no provisioned tenant still serves /Schemas.

After:

モ curl -H "Authorization: Bearer $TOKEN" http://localhost:9999/scim/v2/Users/64db64f9-15e5-4da2-a06f-0627c9c76baa | jq
{
  "schemas": [
    "urn:ietf:params:scim:schemas:core:2.0:User"
  ],
  "id": "64db64f9-15e5-4da2-a06f-0627c9c76baa",
  "userName": "mo@example.com",
  "emails": [
    {
      "value": "mo@example.com",
      "primary": true
    }
  ],
  "meta": {
    "resourceType": "User",
    "created": "2026-08-07T23:34:38.055886Z",
    "lastModified": "2026-08-07T23:34:38.064015Z",
    "location": "https://example.ngrok-free.dev/scim/v2/Users/64db64f9-15e5-4da2-a06f-0627c9c76baa"
  }
}

Additional context

@xlgmokha xlgmokha self-assigned this Aug 3, 2026
@xlgmokha
xlgmokha marked this pull request as ready for review August 3, 2026 21:06
@xlgmokha
xlgmokha requested a review from a team as a code owner August 3, 2026 21:06
@xlgmokha
xlgmokha force-pushed the xlgmokha/auth-1368b branch 2 times, most recently from bfdaf08 to edc69eb Compare August 4, 2026 16:43
@xlgmokha
xlgmokha force-pushed the xlgmokha/auth-1368b branch from edc69eb to eb57a35 Compare August 4, 2026 16:46
@xlgmokha
xlgmokha force-pushed the xlgmokha/auth-1368b branch 2 times, most recently from a66ea24 to b353b6d Compare August 4, 2026 17:27
@xlgmokha
xlgmokha force-pushed the xlgmokha/auth-1368b branch from b353b6d to 78515b5 Compare August 4, 2026 19:52
@xlgmokha
xlgmokha force-pushed the xlgmokha/auth-1368b branch from 78515b5 to f3d911f Compare August 4, 2026 20:05
@xlgmokha
xlgmokha force-pushed the xlgmokha/auth-1368b branch from f3d911f to 8f19172 Compare August 6, 2026 15:34
@xlgmokha
xlgmokha force-pushed the xlgmokha/auth-1368b branch 3 times, most recently from 189f978 to 5da63b3 Compare August 6, 2026 18:31
@xlgmokha
xlgmokha force-pushed the xlgmokha/auth-1368b branch 2 times, most recently from 7504838 to b741d18 Compare August 6, 2026 21:26
@blacksmith-sh

This comment has been minimized.

return nil, false
}

if !provider.IsEnabled() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Severity: LOW

The server returns HTTP 403 for a valid token whose provider is disabled, versus HTTP 401 for any unknown token. This status-code difference lets an attacker use a disabled SCIM provider as an oracle — submitting candidate tokens and distinguishing valid-but-forbidden ones (403) from invalid ones (401), effectively confirming token validity without gaining access.
Helpful? Add 👍 / 👎

💡 Fix Suggestion

Suggestion: Replace the HTTP 403 response for a disabled provider with the same unauthorized(w) call (HTTP 401) used for unknown tokens. This eliminates the status-code oracle by returning a uniform 401 response whether the token is unrecognised or resolves to a disabled provider, so an attacker can no longer confirm token validity by observing the response code.

⚠️ Experimental Feature: This code suggestion is automatically generated. Please review carefully.

Suggested change
if !provider.IsEnabled() {
if !provider.IsEnabled() {
unauthorized(w)
return nil, false
}

@xlgmokha
xlgmokha force-pushed the xlgmokha/auth-1368b branch from e354abc to ca92147 Compare August 12, 2026 15:29
@xlgmokha
xlgmokha force-pushed the xlgmokha/auth-1368b branch 2 times, most recently from d367afc to 8879494 Compare August 12, 2026 16:59
@xlgmokha
xlgmokha force-pushed the xlgmokha/auth-1368b branch from 8879494 to 97a60fc Compare August 12, 2026 17:13
@xlgmokha
xlgmokha force-pushed the xlgmokha/auth-1368b branch from 97a60fc to b66f29c Compare August 12, 2026 22:30
@xlgmokha
xlgmokha force-pushed the xlgmokha/auth-1368b branch from b66f29c to 2dc3da7 Compare August 12, 2026 22:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant