[PR #1002] feat: Add SCIM 2.0 provisioning support for user and group synchronization #528

Closed
opened 2025-10-09 16:52:06 +03:00 by OVERLORD · 0 comments
Owner

Original Pull Request: https://github.com/pocket-id/pocket-id/pull/1002

State: closed
Merged: No


Overview

This PR implements SCIM 2.0 (System for Cross-domain Identity Management) provisioning support, enabling automated user and group synchronization with external identity providers like Cloudflare Zero Trust, Okta, Azure AD, and other SCIM-compliant systems.

Motivation

As requested in the original issue, this feature enables integration with Cloudflare Zero Trust's Generic OIDC provider, which supports SCIM v2 for synchronizing user and group changes. This allows users to manage Pocket ID accounts centrally through their identity provider.

Implementation

Core Features

The implementation provides a complete SCIM 2.0 server with the following capabilities:

User Management:

  • List users with pagination and filtering (GET /api/scim/v2/Users)
  • Get individual user details (GET /api/scim/v2/Users/{id})
  • Create new users (POST /api/scim/v2/Users)
  • Update users via full replacement (PUT /api/scim/v2/Users/{id})
  • Partial updates via PATCH operations (PATCH /api/scim/v2/Users/{id})
  • Soft delete users by disabling accounts (DELETE /api/scim/v2/Users/{id})

Group Management:

  • List groups with pagination and filtering (GET /api/scim/v2/Groups)
  • Get group details with member lists (GET /api/scim/v2/Groups/{id})
  • Create groups with members (POST /api/scim/v2/Groups)
  • Update groups and memberships (PUT /api/scim/v2/Groups/{id})
  • Add/remove members via PATCH (PATCH /api/scim/v2/Groups/{id})
  • Delete groups (DELETE /api/scim/v2/Groups/{id})

Discovery Endpoints:

  • Service provider configuration (GET /api/scim/v2/ServiceProviderConfig)
  • Supported resource types (GET /api/scim/v2/ResourceTypes)
  • Schema definitions (GET /api/scim/v2/Schemas)

Technical Details

Architecture:

  • backend/internal/dto/scim_dto.go - SCIM DTOs following RFC 7643 schemas
  • backend/internal/service/scim_service.go - Business logic for provisioning operations
  • backend/internal/controller/scim_controller.go - REST endpoints and request handling

Authentication:
Uses Pocket ID's existing API key infrastructure. Users create an API key and provide it as a Bearer token:

Authorization: Bearer <api-key>

Design Decisions:

  • Soft Delete: User deletion sets the disabled flag rather than removing records, preserving audit history
  • Group Name Normalization: Display names are normalized to lowercase alphanumeric with underscores for the internal name field
  • Error Handling: Leverages existing error middleware with proper HTTP status codes
  • Simplicity Focus: Implements core SCIM features without enterprise bloat (no bulk operations, advanced filtering, or ETags)

Usage Example

# List users with pagination
curl -H "Authorization: Bearer <api-key>" \
  "https://your-pocket-id.com/api/scim/v2/Users?startIndex=1&count=10"

# Create a user
curl -X POST -H "Authorization: Bearer <api-key>" \
  -H "Content-Type: application/scim+json" \
  -d '{
    "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
    "userName": "john.doe",
    "name": {
      "givenName": "John",
      "familyName": "Doe"
    },
    "emails": [{"value": "john@example.com", "primary": true}],
    "active": true
  }' \
  https://your-pocket-id.com/api/scim/v2/Users

# Disable a user via PATCH
curl -X PATCH -H "Authorization: Bearer <api-key>" \
  -H "Content-Type: application/scim+json" \
  -d '{
    "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
    "Operations": [{"op": "replace", "path": "active", "value": false}]
  }' \
  https://your-pocket-id.com/api/scim/v2/Users/{id}

Integration with Cloudflare Zero Trust

  1. Create an API key in Pocket ID's admin interface
  2. In Cloudflare Zero Trust, navigate to Settings > Authentication > SCIM
  3. Select "Generic SCIM" as the provider
  4. Enter SCIM endpoint: https://your-pocket-id.com/api/scim/v2
  5. Enter the API key as the Bearer token
  6. Configure synchronization settings

Cloudflare will now automatically sync users and groups from Pocket ID.

Documentation

Added comprehensive SCIM.md documentation including:

  • Detailed endpoint descriptions
  • Usage examples with curl commands
  • Integration guide for Cloudflare Zero Trust
  • Supported features and current limitations
  • Standards compliance information (RFC 7643, RFC 7644)

Standards Compliance

This implementation follows:

Testing

  • Backend builds successfully with Go 1.25
  • Server starts without errors
  • All endpoints properly routed and authenticated
  • Error handling verified with middleware integration

Limitations

To maintain Pocket ID's focus on simplicity, the following SCIM features are intentionally not implemented:

  • Bulk operations
  • Advanced filtering (only basic userName/displayName equality filters)
  • Sorting
  • ETags for conditional updates
  • Password changes via SCIM (passkey authentication only)

These can be added in future iterations if needed.

Files Changed

  • Created:
    • backend/internal/dto/scim_dto.go (177 lines)
    • backend/internal/service/scim_service.go (748 lines)
    • backend/internal/controller/scim_controller.go (437 lines)
    • SCIM.md (199 lines)
  • Modified:
    • backend/internal/bootstrap/services_bootstrap.go (service registration)
    • backend/internal/bootstrap/router_bootstrap.go (route registration)

Total: 1,565 lines added

Closes #[issue-number]

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • download.maxmind.com
    • Triggering command: /tmp/pocket-id (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

This section details on the original issue you should resolve

<issue_title>🚀 Feature: SCIM Provisioning</issue_title>
<issue_description>### Feature description

Please add support for SCIM (System for Cross-domain Identity Management) provisioning. Specifically SCIM version 2.

Pitch

In my use case, Pocket ID users can access sites protected by Cloudflare's Zero Trust Access, via the Generic OIDC feature in Zero Trust. This feature supports SCIM version 2, which allows user and group changes made in the OIDC provider to synchronize with Zero Trust.

A couple links to documentation:

<agent_instructions>help create the basic scim server implementation, make sure to follow the codebase guidelines and to use proper svelte 5 runes syntax, the goal is to be simple for this project not bloated or advanced.</agent_instructions>

Comments on the Issue (you are @copilot in this section)

@kmendell SCIM is a "whole different spec" in itself, and is kind of a enterprise based feature, but if @stonith404 sees some value, we could think about implementing it. @stonith404 I’m not really sure I understand the full purpose of SCIM, but from what I understand, it allows user information to be updated in the background without requiring the user to sign in again.

For example, if a Pocket ID account is deleted, the current session would be invalidated automatically?

And if a user updates their name, that change would be reflected immediately, even if they haven’t signed out and back in?

@kmendell This is a big feature to implement, as it would require the full SCIM Server implmentation, ill look again at it once we get all caught up on PR's.
Fixes pocket-id/pocket-id#664

💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click here to start the survey.

**Original Pull Request:** https://github.com/pocket-id/pocket-id/pull/1002 **State:** closed **Merged:** No --- ## Overview This PR implements SCIM 2.0 (System for Cross-domain Identity Management) provisioning support, enabling automated user and group synchronization with external identity providers like Cloudflare Zero Trust, Okta, Azure AD, and other SCIM-compliant systems. ## Motivation As requested in the original issue, this feature enables integration with Cloudflare Zero Trust's Generic OIDC provider, which supports SCIM v2 for synchronizing user and group changes. This allows users to manage Pocket ID accounts centrally through their identity provider. ## Implementation ### Core Features The implementation provides a complete SCIM 2.0 server with the following capabilities: **User Management:** - List users with pagination and filtering (`GET /api/scim/v2/Users`) - Get individual user details (`GET /api/scim/v2/Users/{id}`) - Create new users (`POST /api/scim/v2/Users`) - Update users via full replacement (`PUT /api/scim/v2/Users/{id}`) - Partial updates via PATCH operations (`PATCH /api/scim/v2/Users/{id}`) - Soft delete users by disabling accounts (`DELETE /api/scim/v2/Users/{id}`) **Group Management:** - List groups with pagination and filtering (`GET /api/scim/v2/Groups`) - Get group details with member lists (`GET /api/scim/v2/Groups/{id}`) - Create groups with members (`POST /api/scim/v2/Groups`) - Update groups and memberships (`PUT /api/scim/v2/Groups/{id}`) - Add/remove members via PATCH (`PATCH /api/scim/v2/Groups/{id}`) - Delete groups (`DELETE /api/scim/v2/Groups/{id}`) **Discovery Endpoints:** - Service provider configuration (`GET /api/scim/v2/ServiceProviderConfig`) - Supported resource types (`GET /api/scim/v2/ResourceTypes`) - Schema definitions (`GET /api/scim/v2/Schemas`) ### Technical Details **Architecture:** - `backend/internal/dto/scim_dto.go` - SCIM DTOs following RFC 7643 schemas - `backend/internal/service/scim_service.go` - Business logic for provisioning operations - `backend/internal/controller/scim_controller.go` - REST endpoints and request handling **Authentication:** Uses Pocket ID's existing API key infrastructure. Users create an API key and provide it as a Bearer token: ``` Authorization: Bearer <api-key> ``` **Design Decisions:** - **Soft Delete:** User deletion sets the `disabled` flag rather than removing records, preserving audit history - **Group Name Normalization:** Display names are normalized to lowercase alphanumeric with underscores for the internal name field - **Error Handling:** Leverages existing error middleware with proper HTTP status codes - **Simplicity Focus:** Implements core SCIM features without enterprise bloat (no bulk operations, advanced filtering, or ETags) ### Usage Example ```bash # List users with pagination curl -H "Authorization: Bearer <api-key>" \ "https://your-pocket-id.com/api/scim/v2/Users?startIndex=1&count=10" # Create a user curl -X POST -H "Authorization: Bearer <api-key>" \ -H "Content-Type: application/scim+json" \ -d '{ "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"], "userName": "john.doe", "name": { "givenName": "John", "familyName": "Doe" }, "emails": [{"value": "john@example.com", "primary": true}], "active": true }' \ https://your-pocket-id.com/api/scim/v2/Users # Disable a user via PATCH curl -X PATCH -H "Authorization: Bearer <api-key>" \ -H "Content-Type: application/scim+json" \ -d '{ "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"], "Operations": [{"op": "replace", "path": "active", "value": false}] }' \ https://your-pocket-id.com/api/scim/v2/Users/{id} ``` ### Integration with Cloudflare Zero Trust 1. Create an API key in Pocket ID's admin interface 2. In Cloudflare Zero Trust, navigate to Settings > Authentication > SCIM 3. Select "Generic SCIM" as the provider 4. Enter SCIM endpoint: `https://your-pocket-id.com/api/scim/v2` 5. Enter the API key as the Bearer token 6. Configure synchronization settings Cloudflare will now automatically sync users and groups from Pocket ID. ## Documentation Added comprehensive `SCIM.md` documentation including: - Detailed endpoint descriptions - Usage examples with curl commands - Integration guide for Cloudflare Zero Trust - Supported features and current limitations - Standards compliance information (RFC 7643, RFC 7644) ## Standards Compliance This implementation follows: - [RFC 7643 - SCIM: Core Schema](https://tools.ietf.org/html/rfc7643) - [RFC 7644 - SCIM: Protocol](https://tools.ietf.org/html/rfc7644) ## Testing - ✅ Backend builds successfully with Go 1.25 - ✅ Server starts without errors - ✅ All endpoints properly routed and authenticated - ✅ Error handling verified with middleware integration ## Limitations To maintain Pocket ID's focus on simplicity, the following SCIM features are intentionally not implemented: - Bulk operations - Advanced filtering (only basic userName/displayName equality filters) - Sorting - ETags for conditional updates - Password changes via SCIM (passkey authentication only) These can be added in future iterations if needed. ## Files Changed - **Created:** - `backend/internal/dto/scim_dto.go` (177 lines) - `backend/internal/service/scim_service.go` (748 lines) - `backend/internal/controller/scim_controller.go` (437 lines) - `SCIM.md` (199 lines) - **Modified:** - `backend/internal/bootstrap/services_bootstrap.go` (service registration) - `backend/internal/bootstrap/router_bootstrap.go` (route registration) **Total:** 1,565 lines added Closes #[issue-number] > [!WARNING] > > <details> > <summary>Firewall rules blocked me from connecting to one or more addresses (expand for details)</summary> > > #### I tried to connect to the following addresses, but was blocked by firewall rules: > > - `download.maxmind.com` > - Triggering command: `/tmp/pocket-id` (dns block) > > If you need me to access, download, or install something from one of these locations, you can either: > > - Configure [Actions setup steps](https://gh.io/copilot/actions-setup-steps) to set up my environment, which run before the firewall is enabled > - Add the appropriate URLs or hosts to the custom allowlist in this repository's [Copilot coding agent settings](https://github.com/pocket-id/pocket-id/settings/copilot/coding_agent) (admins only) > > </details> <!-- START COPILOT CODING AGENT SUFFIX --> <details> <summary>Original prompt</summary> > > ---- > > *This section details on the original issue you should resolve* > > <issue_title>🚀 Feature: SCIM Provisioning</issue_title> > <issue_description>### Feature description > > Please add support for SCIM (System for Cross-domain Identity Management) provisioning. Specifically SCIM version 2. > > ### Pitch > > In my use case, Pocket ID users can access sites protected by Cloudflare's Zero Trust Access, via the Generic OIDC feature in Zero Trust. This feature supports SCIM version 2, which allows user and group changes made in the OIDC provider to synchronize with Zero Trust. > > A couple links to documentation: > > - https://developers.cloudflare.com/cloudflare-one/identity/users/scim/ > - https://developers.cloudflare.com/cloudflare-one/identity/idp-integration/generic-oidc/#synchronize-users-and-groups</issue_description> > > <agent_instructions>help create the basic scim server implementation, make sure to follow the codebase guidelines and to use proper svelte 5 runes syntax, the goal is to be simple for this project not bloated or advanced.</agent_instructions> > > ## Comments on the Issue (you are @copilot in this section) > > <comments> > <comment_new><author>@kmendell</author><body> > SCIM is a "whole different spec" in itself, and is kind of a enterprise based feature, but if @stonith404 sees some value, we could think about implementing it.</body></comment_new> > <comment_new><author>@stonith404</author><body> > I’m not really sure I understand the full purpose of SCIM, but from what I understand, it allows user information to be updated in the background without requiring the user to sign in again. > > For example, if a Pocket ID account is deleted, the current session would be invalidated automatically? > > And if a user updates their name, that change would be reflected immediately, even if they haven’t signed out and back in? > </body></comment_new> > <comment_new><author>@kmendell</author><body> > This is a big feature to implement, as it would require the full SCIM Server implmentation, ill look again at it once we get all caught up on PR's. </body></comment_new> > </comments> > </details> Fixes pocket-id/pocket-id#664 <!-- START COPILOT CODING AGENT TIPS --> --- 💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click [here](https://survey3.medallia.com/?EAHeSx-AP01bZqG0Ld9QLQ) to start the survey.
OVERLORD added the pull-request label 2025-10-09 16:52:06 +03:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/pocket-id-pocket-id-2#528