Skip to content

feat(scim): add SCIM Users, Groups, and admin endpoints - #2747

Open
xlgmokha wants to merge 1 commit into
masterfrom
scim/3-users
Open

xlgmokha wants to merge 1 commit into
masterfrom
scim/3-users

Conversation

@xlgmokha

@xlgmokha xlgmokha commented Aug 26, 2026 •

Copy link
Copy Markdown
Contributor

What kind of change does this PR introduce?

Feature. SCIM 2.0 provisioning for SSO providers: Users, Groups, per-provider tokens, and admin endpoints.

What is the current behavior?

/scim/v2 serves discovery metadata only, behind GOTRUE_EXPERIMENTAL_SCIM_ENABLED. There is no way to provision users or groups from an IdP.

What is the new behavior?

SCIM endpoints, authenticated with a bearer token that is scoped to one SSO provider:

Method Path Description
GET /scim/v2/Users List, filter, sort, paginate
POST /scim/v2/Users Create
POST /scim/v2/Users/.search Search with a JSON body (RFC 7644 s3.4.3)
GET, PUT, PATCH, DELETE /scim/v2/Users/{id} Read, replace, patch, soft delete
GET /scim/v2/Groups List, filter, sort, paginate
POST /scim/v2/Groups Create
POST /scim/v2/Groups/.search Search with a JSON body (RFC 7644 s3.4.3)
GET, PUT, PATCH, DELETE /scim/v2/Groups/{id} Read, replace, patch, soft delete
GET /scim/v2/ServiceProviderConfig Discovery, no token required (RFC 7644 s4)
GET /scim/v2/ResourceTypes[/{id}], /scim/v2/Schemas[/{id}] Discovery, bearer token required

Admin endpoints under /admin/sso/providers/{idp_id}/scim:

Method Path Description
GET / Status: enabled, base_url, active tokens
POST / Enable SCIM for the provider, returns status
DELETE / Disable SCIM for the provider, keeps tokens, returns status
GET /tokens List tokens
POST /tokens Create a token (plaintext returned once), optional expires_at
DELETE /tokens/{token_id} Revoke a token

Configuration:

Env Default Note
GOTRUE_SSO_SCIM_ENABLED false Replaces GOTRUE_EXPERIMENTAL_SCIM_ENABLED, which is removed
GOTRUE_RATE_LIMIT_SCIM 3000 Requests per 5 minutes per IP, burst 30. Separate from GOTRUE_RATE_LIMIT_SSO because IdP pushes send far more requests than sign-ins

Groups:

Behavior Detail
Members Users and Groups. Nested groups are supported. A change that would create a cycle returns 400 invalidValue
User groups Read-only. Lists direct and indirect groups. Values sent by the client are ignored
Filter members.value on Groups and groups.value on Users support eq and ne

Filtering and sorting:

Behavior Detail
Filter operators eq, ne, co, sw, ew, pr, gt, ge, lt, le, with and, or, not and value paths
Case Case-insensitive. Filters run against a stored lowercased copy of the resource (search column, GIN jsonb_path_ops)
Sort userName on Users and displayName on Groups use their btree indexes
Concurrency If-Match with a stale ETag returns 412

Storage, migrations 20261002000000 and 20261005000000:

Table Purpose
scim_settings Per-provider enabled flag
scim_resources Users and Groups as JSON, scoped by sso_provider_id, soft deleted
scim_resource_references Group membership edges

Not in this PR

Item Where
Audit log events for SCIM Follow-up PR
Linking SCIM users to auth users (sign-in, deactivation, delete) Follow-up PR

Additional context

Extracted from #2731

Details
scim-demo.mp4
モ ./hack/scim-demo.sh

== Admin: provider, SCIM and tokens ==

POST /admin/sso/providers
{
  "id": "d1b4e557-0ca7-4035-bc1a-302dd17e002a",
  "disabled": null,
  "saml": {
    "entity_id": "https://scim-demo-1791330934.example/entityid",
    "metadata_xml": "<md:EntityDescriptor xmlns:md=\"urn:oasis:names:tc:SAML:2.0:metadata\" entityID=\"https://scim-demo-1791330934.example/entityid\"><md:IDPSSODescriptor protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\"><md:KeyDescriptor use=\"signing\"><ds:KeyInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"><ds:X509Data><ds:X509Certificate>MIIC+DCCAeCgAwIBAgIUKKT1DwHCuocQyWURzgSHih8vC0UwDQYJKoZIhvcNAQELBQAwHDEaMBgGA1UEAwwRc2NpbS1kZW1vLmV4YW1wbGUwHhcNMjYxMDA2MjM1NTM0WhcNMjYxMDA3MjM1NTM0WjAcMRowGAYDVQQDDBFzY2ltLWRlbW8uZXhhbXBsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALfv0gKU8i6N9pMb9/znE50lZVkQP3UypQ3EqJqHAOvZX+NOwQNLlN9K4A3Z5KwvpPRdECQRfdAttF2UEk1eaxzRh02OqhYFEpaQvx5SQXNzgYZGZAkaPEbO5ieairz2yA7q5+mavf34PfmvGqt92QcaO/kTShNEC67L+ncA60OdOyD/lN79euFhTyGLdo3Ps9BVZLF0fiFMUbAWqOSKlfjy6J9jpQURW/yjOOGUniTx4KZ60CeMLGRqyOTeGcj2OfG9/6S6J/Mc8bkNft6rrMUDIJnpyxIOx0Pl/XcXOCPdeUnHQU8XtNS4059Mg3+1ywZAQFWOREVL+BwJtauugJMCAwEAAaMyMDAwHQYDVR0OBBYEFJXFywn0waJ5gIHbyLDD00Z0nokSMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBABc7hWPLKQ1U1UB7FhDGe7LvK/HrwDQH/4l3DEqaDMBHsmnOnk/CFOChN888inl2ypBU+e7D42iAiYW/uo75iF05FNtbGC28IwX41VXL0v1P6dWgd8zUEIVmY5dJnWT7GsUJoXYIwzgLdQIywgZzu99MGojwveqCyxa6810a7uf1ZG+ZcItXmSIM7cLcGpsEFvm3ln/+KZf4Ws7kqhsRw5rbOhoOLEyhVsmhWbC2dxZVyhPv3pp0C06BXGq2Ct8EeXoIiVbTTUEH7xzwkoOtCzdw0VYJF4kPPPWdBS+C8zK0/YXncwmWvaPvSNv9bXga2DYQYyC2PFuXBq/4r1+qGf0=</ds:X509Certificate></ds:X509Data></ds:KeyInfo></md:KeyDescriptor><md:SingleSignOnService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\" Location=\"https://scim-demo-1791330934.example/sso\"/></md:IDPSSODescriptor></md:EntityDescriptor>",
    "attribute_mapping": {}
  },
  "domains": [],
  "created_at": "2026-10-06T17:55:35.119264-06:00",
  "updated_at": "2026-10-06T17:55:35.119264-06:00"
}
HTTP 201

POST /admin/sso/providers/d1b4e557-0ca7-4035-bc1a-302dd17e002a/scim
{
  "enabled": true,
  "base_url": "https://hatless-campfire-flock.ngrok-free.dev/scim/v2",
  "tokens": []
}
HTTP 200

POST /admin/sso/providers/d1b4e557-0ca7-4035-bc1a-302dd17e002a/scim/tokens
{
  "base_url": "https://hatless-campfire-flock.ngrok-free.dev/scim/v2",
  "token": "scim_91ea9812dee11e3fbef486b6019338b6eda01ffd",
  "id": "1bbbe5fc-dbec-47e7-a5c6-2b4a5b960fdd",
  "prefix": "scim_91ea981",
  "created_at": "2026-10-06T23:55:35.372917Z",
  "expires_at": null,
  "revoked_at": null,
  "last_used_at": null
}
HTTP 201

POST /admin/sso/providers/d1b4e557-0ca7-4035-bc1a-302dd17e002a/scim/tokens
{
  "base_url": "https://hatless-campfire-flock.ngrok-free.dev/scim/v2",
  "token": "scim_814be61a4ab91c73d4c10aac9e35ea44cc781de2",
  "id": "1bf185a3-0b6c-4ea1-b190-84d6cd9d16a6",
  "prefix": "scim_814be61",
  "created_at": "2026-10-06T23:55:35.515607Z",
  "expires_at": null,
  "revoked_at": null,
  "last_used_at": null
}
HTTP 201

GET /admin/sso/providers/d1b4e557-0ca7-4035-bc1a-302dd17e002a/scim/tokens
{
  "tokens": [
    {
      "id": "1bbbe5fc-dbec-47e7-a5c6-2b4a5b960fdd",
      "prefix": "scim_91ea981",
      "created_at": "2026-10-06T23:55:35.372917Z",
      "expires_at": null,
      "revoked_at": null,
      "last_used_at": null
    },
    {
      "id": "1bf185a3-0b6c-4ea1-b190-84d6cd9d16a6",
      "prefix": "scim_814be61",
      "created_at": "2026-10-06T23:55:35.515607Z",
      "expires_at": null,
      "revoked_at": null,
      "last_used_at": null
    }
  ]
}
HTTP 200

DELETE /admin/sso/providers/d1b4e557-0ca7-4035-bc1a-302dd17e002a/scim/tokens/1bf185a3-0b6c-4ea1-b190-84d6cd9d16a6
{
  "id": "1bf185a3-0b6c-4ea1-b190-84d6cd9d16a6",
  "prefix": "scim_814be61",
  "created_at": "2026-10-06T23:55:35.515607Z",
  "expires_at": null,
  "revoked_at": "2026-10-06T23:55:35.813483Z",
  "last_used_at": null
}
HTTP 200

GET /admin/sso/providers/d1b4e557-0ca7-4035-bc1a-302dd17e002a/scim
{
  "enabled": true,
  "base_url": "https://hatless-campfire-flock.ngrok-free.dev/scim/v2",
  "tokens": [
    {
      "id": "1bbbe5fc-dbec-47e7-a5c6-2b4a5b960fdd",
      "prefix": "scim_91ea981",
      "created_at": "2026-10-06T23:55:35.372917Z",
      "expires_at": null,
      "revoked_at": null,
      "last_used_at": null
    }
  ]
}
HTTP 200

GET /scim/v2/Users
{
  "schemas": [
    "urn:ietf:params:scim:api:messages:2.0:Error"
  ],
  "detail": "The access token is invalid",
  "status": "401"
}
HTTP 401

== Discovery ==

GET /scim/v2/ServiceProviderConfig
{
  "schemas": [
    "urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig"
  ],
  "patch": {
    "supported": true
  },
  "bulk": {
    "supported": false,
    "maxOperations": 0,
    "maxPayloadSize": 0
  },
  "filter": {
    "supported": true,
    "maxResults": 100
  },
  "changePassword": {
    "supported": false
  },
  "sort": {
    "supported": true
  },
  "etag": {
    "supported": false
  },
  "authenticationSchemes": [
    {
      "type": "oauthbearertoken",
      "name": "OAuth Bearer Token",
      "description": "Authentication scheme using the OAuth Bearer Token Standard",
      "specUri": "http://www.rfc-editor.org/info/rfc6750",
      "primary": true
    }
  ],
  "meta": {
    "resourceType": "ServiceProviderConfig",
    "location": "https://hatless-campfire-flock.ngrok-free.dev/scim/v2/ServiceProviderConfig"
  }
}
HTTP 200

GET /scim/v2/ResourceTypes
[
  {
    "name": "User",
    "endpoint": "/Users",
    "schema": "urn:ietf:params:scim:schemas:core:2.0:User"
  },
  {
    "name": "Group",
    "endpoint": "/Groups",
    "schema": "urn:ietf:params:scim:schemas:core:2.0:Group"
  }
]
HTTP 200

GET /scim/v2/Schemas
[
  "urn:ietf:params:scim:schemas:core:2.0:User",
  "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User",
  "urn:ietf:params:scim:schemas:core:2.0:Group"
]
HTTP 200

== Users ==

POST /scim/v2/Users
{
  "active": true,
  "emails": [
    {
      "primary": true,
      "value": "bjensen+1791330934@example.com"
    }
  ],
  "id": "f1a23433-b8d0-4f5d-9ebe-52cef5aa1e83",
  "meta": {
    "created": "2026-10-06T23:55:36.347175Z",
    "lastModified": "2026-10-06T23:55:36.347175Z",
    "location": "https://hatless-campfire-flock.ngrok-free.dev/scim/v2/Users/f1a23433-b8d0-4f5d-9ebe-52cef5aa1e83",
    "resourceType": "User",
    "version": "W/\"1791330936347175\""
  },
  "name": {
    "familyName": "Jensen",
    "givenName": "Barbara"
  },
  "schemas": [
    "urn:ietf:params:scim:schemas:core:2.0:User"
  ],
  "userName": "bjensen+1791330934@example.com"
}
HTTP 201

POST /scim/v2/Users
{
  "active": true,
  "emails": [
    {
      "primary": true,
      "value": "jsmith+1791330934@example.com"
    }
  ],
  "id": "4c0afd81-1a4b-4bd8-882d-d51d6128344b",
  "meta": {
    "created": "2026-10-06T23:55:36.563125Z",
    "lastModified": "2026-10-06T23:55:36.563125Z",
    "location": "https://hatless-campfire-flock.ngrok-free.dev/scim/v2/Users/4c0afd81-1a4b-4bd8-882d-d51d6128344b",
    "resourceType": "User",
    "version": "W/\"1791330936563125\""
  },
  "name": {
    "familyName": "Smith",
    "givenName": "John"
  },
  "schemas": [
    "urn:ietf:params:scim:schemas:core:2.0:User"
  ],
  "userName": "jsmith+1791330934@example.com"
}
HTTP 201

GET /scim/v2/Users?filter=userName%20eq%20%22bjensen%2B1791330934%40example.com%22
{
  "schemas": [
    "urn:ietf:params:scim:api:messages:2.0:ListResponse"
  ],
  "totalResults": 1,
  "startIndex": 1,
  "itemsPerPage": 1,
  "Resources": [
    {
      "active": true,
      "emails": [
        {
          "primary": true,
          "value": "bjensen+1791330934@example.com"
        }
      ],
      "id": "f1a23433-b8d0-4f5d-9ebe-52cef5aa1e83",
      "meta": {
        "created": "2026-10-06T23:55:36.347175Z",
        "lastModified": "2026-10-06T23:55:36.347175Z",
        "location": "https://hatless-campfire-flock.ngrok-free.dev/scim/v2/Users/f1a23433-b8d0-4f5d-9ebe-52cef5aa1e83",
        "resourceType": "User",
        "version": "W/\"1791330936347175\""
      },
      "name": {
        "familyName": "Jensen",
        "givenName": "Barbara"
      },
      "schemas": [
        "urn:ietf:params:scim:schemas:core:2.0:User"
      ],
      "userName": "bjensen+1791330934@example.com"
    }
  ]
}
HTTP 200

GET /scim/v2/Users?sortBy=userName&sortOrder=descending
{
  "schemas": [
    "urn:ietf:params:scim:api:messages:2.0:ListResponse"
  ],
  "totalResults": 2,
  "startIndex": 1,
  "itemsPerPage": 2,
  "Resources": [
    {
      "active": true,
      "emails": [
        {
          "primary": true,
          "value": "jsmith+1791330934@example.com"
        }
      ],
      "id": "4c0afd81-1a4b-4bd8-882d-d51d6128344b",
      "meta": {
        "created": "2026-10-06T23:55:36.563125Z",
        "lastModified": "2026-10-06T23:55:36.563125Z",
        "location": "https://hatless-campfire-flock.ngrok-free.dev/scim/v2/Users/4c0afd81-1a4b-4bd8-882d-d51d6128344b",
        "resourceType": "User",
        "version": "W/\"1791330936563125\""
      },
      "name": {
        "familyName": "Smith",
        "givenName": "John"
      },
      "schemas": [
        "urn:ietf:params:scim:schemas:core:2.0:User"
      ],
      "userName": "jsmith+1791330934@example.com"
    },
    {
      "active": true,
      "emails": [
        {
          "primary": true,
          "value": "bjensen+1791330934@example.com"
        }
      ],
      "id": "f1a23433-b8d0-4f5d-9ebe-52cef5aa1e83",
      "meta": {
        "created": "2026-10-06T23:55:36.347175Z",
        "lastModified": "2026-10-06T23:55:36.347175Z",
        "location": "https://hatless-campfire-flock.ngrok-free.dev/scim/v2/Users/f1a23433-b8d0-4f5d-9ebe-52cef5aa1e83",
        "resourceType": "User",
        "version": "W/\"1791330936347175\""
      },
      "name": {
        "familyName": "Jensen",
        "givenName": "Barbara"
      },
      "schemas": [
        "urn:ietf:params:scim:schemas:core:2.0:User"
      ],
      "userName": "bjensen+1791330934@example.com"
    }
  ]
}
HTTP 200

GET /scim/v2/Users?sortBy=userName&startIndex=2&count=1
{
  "schemas": [
    "urn:ietf:params:scim:api:messages:2.0:ListResponse"
  ],
  "totalResults": 2,
  "startIndex": 2,
  "itemsPerPage": 1,
  "Resources": [
    {
      "active": true,
      "emails": [
        {
          "primary": true,
          "value": "jsmith+1791330934@example.com"
        }
      ],
      "id": "4c0afd81-1a4b-4bd8-882d-d51d6128344b",
      "meta": {
        "created": "2026-10-06T23:55:36.563125Z",
        "lastModified": "2026-10-06T23:55:36.563125Z",
        "location": "https://hatless-campfire-flock.ngrok-free.dev/scim/v2/Users/4c0afd81-1a4b-4bd8-882d-d51d6128344b",
        "resourceType": "User",
        "version": "W/\"1791330936563125\""
      },
      "name": {
        "familyName": "Smith",
        "givenName": "John"
      },
      "schemas": [
        "urn:ietf:params:scim:schemas:core:2.0:User"
      ],
      "userName": "jsmith+1791330934@example.com"
    }
  ]
}
HTTP 200

PATCH /scim/v2/Users/f1a23433-b8d0-4f5d-9ebe-52cef5aa1e83
{
  "active": true,
  "emails": [
    {
      "primary": true,
      "value": "bjensen+1791330934@example.com"
    }
  ],
  "id": "f1a23433-b8d0-4f5d-9ebe-52cef5aa1e83",
  "meta": {
    "created": "2026-10-06T23:55:36.347175Z",
    "lastModified": "2026-10-06T23:55:37.132661Z",
    "location": "https://hatless-campfire-flock.ngrok-free.dev/scim/v2/Users/f1a23433-b8d0-4f5d-9ebe-52cef5aa1e83",
    "resourceType": "User",
    "version": "W/\"1791330937132661\""
  },
  "name": {
    "familyName": "Jensen-Smith",
    "givenName": "Barbara"
  },
  "schemas": [
    "urn:ietf:params:scim:schemas:core:2.0:User"
  ],
  "userName": "bjensen+1791330934@example.com"
}
HTTP 200

PUT /scim/v2/Users/f1a23433-b8d0-4f5d-9ebe-52cef5aa1e83
{
  "active": true,
  "emails": [
    {
      "primary": true,
      "value": "bjensen+1791330934@example.com"
    }
  ],
  "id": "f1a23433-b8d0-4f5d-9ebe-52cef5aa1e83",
  "meta": {
    "created": "2026-10-06T23:55:36.347175Z",
    "lastModified": "2026-10-06T23:55:37.290865Z",
    "location": "https://hatless-campfire-flock.ngrok-free.dev/scim/v2/Users/f1a23433-b8d0-4f5d-9ebe-52cef5aa1e83",
    "resourceType": "User",
    "version": "W/\"1791330937290865\""
  },
  "name": {
    "familyName": "Jensen",
    "givenName": "Babs"
  },
  "schemas": [
    "urn:ietf:params:scim:schemas:core:2.0:User"
  ],
  "userName": "bjensen+1791330934@example.com"
}
HTTP 200

PATCH /scim/v2/Users/f1a23433-b8d0-4f5d-9ebe-52cef5aa1e83
{
  "active": false,
  "emails": [
    {
      "primary": true,
      "value": "bjensen+1791330934@example.com"
    }
  ],
  "id": "f1a23433-b8d0-4f5d-9ebe-52cef5aa1e83",
  "meta": {
    "created": "2026-10-06T23:55:36.347175Z",
    "lastModified": "2026-10-06T23:55:37.4456Z",
    "location": "https://hatless-campfire-flock.ngrok-free.dev/scim/v2/Users/f1a23433-b8d0-4f5d-9ebe-52cef5aa1e83",
    "resourceType": "User",
    "version": "W/\"1791330937445600\""
  },
  "name": {
    "familyName": "Jensen",
    "givenName": "Babs"
  },
  "schemas": [
    "urn:ietf:params:scim:schemas:core:2.0:User"
  ],
  "userName": "bjensen+1791330934@example.com"
}
HTTP 200

PATCH /scim/v2/Users/f1a23433-b8d0-4f5d-9ebe-52cef5aa1e83
{
  "active": true,
  "emails": [
    {
      "primary": true,
      "value": "bjensen+1791330934@example.com"
    }
  ],
  "id": "f1a23433-b8d0-4f5d-9ebe-52cef5aa1e83",
  "meta": {
    "created": "2026-10-06T23:55:36.347175Z",
    "lastModified": "2026-10-06T23:55:37.601308Z",
    "location": "https://hatless-campfire-flock.ngrok-free.dev/scim/v2/Users/f1a23433-b8d0-4f5d-9ebe-52cef5aa1e83",
    "resourceType": "User",
    "version": "W/\"1791330937601308\""
  },
  "name": {
    "familyName": "Jensen",
    "givenName": "Babs"
  },
  "schemas": [
    "urn:ietf:params:scim:schemas:core:2.0:User"
  ],
  "userName": "bjensen+1791330934@example.com"
}
HTTP 200

== Groups ==

POST /scim/v2/Groups
{
  "displayName": "Tour Guides 1791330934",
  "id": "cd7fabd8-46a9-46d7-95c3-5510024b6b3c",
  "members": [
    {
      "$ref": "https://hatless-campfire-flock.ngrok-free.dev/scim/v2/Users/f1a23433-b8d0-4f5d-9ebe-52cef5aa1e83",
      "type": "User",
      "value": "f1a23433-b8d0-4f5d-9ebe-52cef5aa1e83"
    }
  ],
  "meta": {
    "created": "2026-10-06T23:55:37.751335Z",
    "lastModified": "2026-10-06T23:55:37.751335Z",
    "location": "https://hatless-campfire-flock.ngrok-free.dev/scim/v2/Groups/cd7fabd8-46a9-46d7-95c3-5510024b6b3c",
    "resourceType": "Group",
    "version": "W/\"1791330937751335\""
  },
  "schemas": [
    "urn:ietf:params:scim:schemas:core:2.0:Group"
  ]
}
HTTP 201

PATCH /scim/v2/Groups/cd7fabd8-46a9-46d7-95c3-5510024b6b3c
HTTP 204

PATCH /scim/v2/Groups/cd7fabd8-46a9-46d7-95c3-5510024b6b3c
HTTP 204

GET /scim/v2/Groups/cd7fabd8-46a9-46d7-95c3-5510024b6b3c
{
  "displayName": "Tour Guides 1791330934",
  "id": "cd7fabd8-46a9-46d7-95c3-5510024b6b3c",
  "members": [
    {
      "$ref": "https://hatless-campfire-flock.ngrok-free.dev/scim/v2/Users/4c0afd81-1a4b-4bd8-882d-d51d6128344b",
      "type": "User",
      "value": "4c0afd81-1a4b-4bd8-882d-d51d6128344b"
    }
  ],
  "meta": {
    "created": "2026-10-06T23:55:37.751335Z",
    "lastModified": "2026-10-06T23:55:38.055301Z",
    "location": "https://hatless-campfire-flock.ngrok-free.dev/scim/v2/Groups/cd7fabd8-46a9-46d7-95c3-5510024b6b3c",
    "resourceType": "Group",
    "version": "W/\"1791330938055301\""
  },
  "schemas": [
    "urn:ietf:params:scim:schemas:core:2.0:Group"
  ]
}
HTTP 200

== Errors ==

POST /scim/v2/Users
{
  "schemas": [
    "urn:ietf:params:scim:api:messages:2.0:Error"
  ],
  "scimType": "uniqueness",
  "detail": "resource must be unique",
  "status": "409"
}
HTTP 409

GET /scim/v2/Users?filter=userName%20eq
{
  "schemas": [
    "urn:ietf:params:scim:api:messages:2.0:Error"
  ],
  "scimType": "invalidFilter",
  "detail": "The specified filter syntax was invalid, or the specified attribute and filter comparison combination is not supported.",
  "status": "400"
}
HTTP 400

GET /scim/v2/Users?sortBy=nope
{
  "schemas": [
    "urn:ietf:params:scim:api:messages:2.0:Error"
  ],
  "scimType": "invalidValue",
  "detail": "Unknown sortBy",
  "status": "400"
}
HTTP 400

GET /scim/v2/Users/00000000-0000-0000-0000-000000000000
{
  "schemas": [
    "urn:ietf:params:scim:api:messages:2.0:Error"
  ],
  "detail": "Not found",
  "status": "404"
}
HTTP 404

GET /scim/v2/Users
{
  "schemas": [
    "urn:ietf:params:scim:api:messages:2.0:Error"
  ],
  "detail": "The access token is invalid",
  "status": "401"
}
HTTP 401

== Cleanup ==

DELETE /scim/v2/Groups/cd7fabd8-46a9-46d7-95c3-5510024b6b3c
HTTP 204

DELETE /scim/v2/Users/f1a23433-b8d0-4f5d-9ebe-52cef5aa1e83
HTTP 204

DELETE /scim/v2/Users/4c0afd81-1a4b-4bd8-882d-d51d6128344b
HTTP 204

GET /scim/v2/Users/f1a23433-b8d0-4f5d-9ebe-52cef5aa1e83
{
  "schemas": [
    "urn:ietf:params:scim:api:messages:2.0:Error"
  ],
  "detail": "Not found",
  "status": "404"
}
HTTP 404

DELETE /admin/sso/providers/d1b4e557-0ca7-4035-bc1a-302dd17e002a/scim
{
  "enabled": false,
  "base_url": "https://hatless-campfire-flock.ngrok-free.dev/scim/v2",
  "tokens": [
    {
      "id": "1bbbe5fc-dbec-47e7-a5c6-2b4a5b960fdd",
      "prefix": "scim_91ea981",
      "created_at": "2026-10-06T23:55:35.372917Z",
      "expires_at": null,
      "revoked_at": null,
      "last_used_at": "2026-10-06T23:55:36.123363Z"
    }
  ]
}
HTTP 200

GET /scim/v2/Users
{
  "schemas": [
    "urn:ietf:params:scim:api:messages:2.0:Error"
  ],
  "detail": "The access token is invalid",
  "status": "401"
}
HTTP 401

DELETE /admin/sso/providers/d1b4e557-0ca7-4035-bc1a-302dd17e002a
{
  "id": "d1b4e557-0ca7-4035-bc1a-302dd17e002a",
  "disabled": null,
  "saml": {
    "entity_id": "https://scim-demo-1791330934.example/entityid",
    "metadata_xml": "<md:EntityDescriptor xmlns:md=\"urn:oasis:names:tc:SAML:2.0:metadata\" entityID=\"https://scim-demo-1791330934.example/entityid\"><md:IDPSSODescriptor protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\"><md:KeyDescriptor use=\"signing\"><ds:KeyInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"><ds:X509Data><ds:X509Certificate>MIIC+DCCAeCgAwIBAgIUKKT1DwHCuocQyWURzgSHih8vC0UwDQYJKoZIhvcNAQELBQAwHDEaMBgGA1UEAwwRc2NpbS1kZW1vLmV4YW1wbGUwHhcNMjYxMDA2MjM1NTM0WhcNMjYxMDA3MjM1NTM0WjAcMRowGAYDVQQDDBFzY2ltLWRlbW8uZXhhbXBsZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALfv0gKU8i6N9pMb9/znE50lZVkQP3UypQ3EqJqHAOvZX+NOwQNLlN9K4A3Z5KwvpPRdECQRfdAttF2UEk1eaxzRh02OqhYFEpaQvx5SQXNzgYZGZAkaPEbO5ieairz2yA7q5+mavf34PfmvGqt92QcaO/kTShNEC67L+ncA60OdOyD/lN79euFhTyGLdo3Ps9BVZLF0fiFMUbAWqOSKlfjy6J9jpQURW/yjOOGUniTx4KZ60CeMLGRqyOTeGcj2OfG9/6S6J/Mc8bkNft6rrMUDIJnpyxIOx0Pl/XcXOCPdeUnHQU8XtNS4059Mg3+1ywZAQFWOREVL+BwJtauugJMCAwEAAaMyMDAwHQYDVR0OBBYEFJXFywn0waJ5gIHbyLDD00Z0nokSMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBABc7hWPLKQ1U1UB7FhDGe7LvK/HrwDQH/4l3DEqaDMBHsmnOnk/CFOChN888inl2ypBU+e7D42iAiYW/uo75iF05FNtbGC28IwX41VXL0v1P6dWgd8zUEIVmY5dJnWT7GsUJoXYIwzgLdQIywgZzu99MGojwveqCyxa6810a7uf1ZG+ZcItXmSIM7cLcGpsEFvm3ln/+KZf4Ws7kqhsRw5rbOhoOLEyhVsmhWbC2dxZVyhPv3pp0C06BXGq2Ct8EeXoIiVbTTUEH7xzwkoOtCzdw0VYJF4kPPPWdBS+C8zK0/YXncwmWvaPvSNv9bXga2DYQYyC2PFuXBq/4r1+qGf0=</ds:X509Certificate></ds:X509Data></ds:KeyInfo></md:KeyDescriptor><md:SingleSignOnService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\" Location=\"https://scim-demo-1791330934.example/sso\"/></md:IDPSSODescriptor></md:EntityDescriptor>",
    "attribute_mapping": {}
  },
  "domains": [],
  "created_at": "2026-10-06T17:55:35.119264-06:00",
  "updated_at": "2026-10-06T17:55:35.119264-06:00"
}
HTTP 200

@xlgmokha
xlgmokha changed the base branch from master to scim/2-core August 26, 2026 00:59
@xlgmokha xlgmokha self-assigned this Aug 26, 2026
@xlgmokha
xlgmokha force-pushed the scim/3-users branch 2 times, most recently from 6e4416d to edff202 Compare August 26, 2026 22:33
@xlgmokha
xlgmokha force-pushed the scim/2-core branch 2 times, most recently from f689fc8 to fec7a7d Compare August 26, 2026 23:29
@xlgmokha
xlgmokha force-pushed the scim/3-users branch 2 times, most recently from 3538a0b to 77782ab Compare August 26, 2026 23:31
@xlgmokha
xlgmokha force-pushed the scim/3-users branch 2 times, most recently from 4709682 to e114c8e Compare August 27, 2026 16:17
@xlgmokha
xlgmokha force-pushed the scim/2-core branch 2 times, most recently from a9ac782 to 316aa74 Compare August 27, 2026 17:11
@xlgmokha
xlgmokha force-pushed the scim/3-users branch 3 times, most recently from eb969a8 to 86c6ef9 Compare August 28, 2026 15:45
@xlgmokha
xlgmokha marked this pull request as ready for review August 28, 2026 16:39
@xlgmokha
xlgmokha requested a review from a team as a code owner August 28, 2026 16:39
Comment thread internal/api/scim/user_repository.go Outdated
Comment thread internal/api/scim/user_repository.go Outdated
@xlgmokha
xlgmokha force-pushed the scim/3-users branch 2 times, most recently from 7294ee5 to e6690b1 Compare August 28, 2026 16:58
Comment thread internal/api/scim_users.go Outdated
Comment thread internal/api/scim_users.go Outdated
Comment thread internal/api/scim_users.go Outdated
Comment thread internal/models/audit_log_entry.go Fixed
Comment thread internal/models/audit_log_entry.go Fixed
Comment thread internal/api/scim_user_linking.go Outdated
Comment thread internal/api/external.go Outdated
Comment thread internal/models/scim_token.go
Comment thread internal/api/scim_admin.go Outdated
Comment thread internal/api/scim.go Outdated
Comment thread internal/api/scim.go Outdated
@blacksmith-sh

This comment has been minimized.

Comment thread internal/api/scim_admin.go Outdated
Comment thread internal/api/scim.go Outdated
Comment thread internal/api/scim.go Outdated
Comment thread internal/api/ssoadmin.go Outdated
Comment thread internal/api/scim_user_linking.go Outdated
Comment thread internal/api/scim_user_linking.go Outdated
Comment thread internal/api/ssoadmin.go Outdated
Comment thread internal/api/scim_user_linking.go Outdated
Comment thread internal/api/ssoadmin.go Outdated
Comment thread internal/api/scim_admin.go Outdated
Comment thread internal/api/scim/repository.go Outdated
Comment thread internal/api/scim/repository.go Outdated
@blacksmith-sh

This comment has been minimized.

Comment thread internal/api/scim/repository.go
@blacksmith-sh

This comment has been minimized.

Comment thread internal/api/scim/repository.go Outdated
@blacksmith-sh

This comment has been minimized.

Comment thread internal/api/scim/repository.go Outdated
Comment thread internal/api/scim/repository.go
Comment thread internal/api/scim/repository.go
@blacksmith-sh

This comment has been minimized.

This branch has not been deployed

No deployments
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.

4 participants