{"id":47685324,"url":"https://github.com/marcosbarbero/scim2-sdk-jvm","last_synced_at":"2026-04-02T14:47:41.618Z","repository":{"id":346752038,"uuid":"1191379966","full_name":"marcosbarbero/scim2-sdk-jvm","owner":"marcosbarbero","description":"A modern, Kotlin-first SCIM 2.0 (RFC 7643/7644) SDK for the JVM with Spring Boot auto-configuration, pluggable persistence, IdP adapters, and full Java interop.","archived":false,"fork":false,"pushed_at":"2026-03-26T07:39:41.000Z","size":582,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-26T12:38:33.230Z","etag":null,"topics":["identity","java","kotlin","provisioning","rfc7643","rfc7644","scim","scim2","sdk","spring-boot"],"latest_commit_sha":null,"homepage":null,"language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/marcosbarbero.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-25T07:23:44.000Z","updated_at":"2026-03-26T07:34:49.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/marcosbarbero/scim2-sdk-jvm","commit_stats":null,"previous_names":["marcosbarbero/scim2-sdk-jvm"],"tags_count":68,"template":false,"template_full_name":null,"purl":"pkg:github/marcosbarbero/scim2-sdk-jvm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcosbarbero%2Fscim2-sdk-jvm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcosbarbero%2Fscim2-sdk-jvm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcosbarbero%2Fscim2-sdk-jvm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcosbarbero%2Fscim2-sdk-jvm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/marcosbarbero","download_url":"https://codeload.github.com/marcosbarbero/scim2-sdk-jvm/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcosbarbero%2Fscim2-sdk-jvm/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31308431,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T12:59:32.332Z","status":"ssl_error","status_checked_at":"2026-04-02T12:54:48.875Z","response_time":89,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["identity","java","kotlin","provisioning","rfc7643","rfc7644","scim","scim2","sdk","spring-boot"],"created_at":"2026-04-02T14:47:37.452Z","updated_at":"2026-04-02T14:47:41.612Z","avatar_url":"https://github.com/marcosbarbero.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build](https://github.com/marcosbarbero/scim2-sdk-jvm/actions/workflows/ci.yml/badge.svg)](https://github.com/marcosbarbero/scim2-sdk-jvm/actions/workflows/ci.yml)\n[![codecov](https://codecov.io/gh/marcosbarbero/scim2-sdk-jvm/graph/badge.svg)](https://codecov.io/gh/marcosbarbero/scim2-sdk-jvm)\n[![Maven Central](https://img.shields.io/maven-central/v/com.marcosbarbero/scim2-sdk-core)](https://central.sonatype.com/search?q=com.marcosbarbero.scim2-sdk)\n[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)\n[![Kotlin](https://img.shields.io/badge/Kotlin-2.2-purple.svg)](https://kotlinlang.org)\n\n# SCIM 2.0 SDK for JVM\n\nA modern, Kotlin-first SCIM 2.0 (RFC 7643/7644) SDK for the JVM with full Java interop.\n\n## Features\n\n- **Complete RFC compliance**: All SCIM 2.0 operations (CRUD, search, bulk, patch, discovery)\n- **Kotlin-first with Java interop**: Kotlin DSLs, extension functions, coroutines, plus @Jvm annotations for Java users\n- **Hexagonal architecture**: Framework-agnostic core, pluggable persistence and identity\n- **Spring Boot starter**: Auto-configuration with sensible defaults\n- **Works without Spring**: Use with any JVM HTTP framework\n- **OOTB persistence**: JPA adapter with reference schemas for PostgreSQL, MySQL, Oracle, MSSQL, H2\n- **Observability**: Metrics (Micrometer), tracing (OpenTelemetry), structured logging (MDC), event system\n- **Type-safe client**: Fluent API with Kotlin DSLs for filters, patches, searches\n- **RFC 9457 ProblemDetail**: Content-negotiated error responses\n- **Extensible**: SPI for serialization, HTTP transport, identity, authorization, events\n\n## Architecture\n\nFor detailed architecture diagrams (module dependencies, request flow, authentication, outbox pattern), see [docs/architecture.md](docs/architecture.md).\n\n```mermaid\ngraph LR\n    CORE[scim2-sdk-core] --\u003e SERVER[scim2-sdk-server]\n    CORE --\u003e CLIENT[scim2-sdk-client]\n    SERVER --\u003e SPRING[Spring Boot\u003cbr/\u003eAuto-Configuration]\n    CLIENT --\u003e HTTPCLIENT[Java HttpClient]\n    CLIENT --\u003e OKHTTP[OkHttp]\n\n    style CORE fill:#4CAF50,color:#fff\n    style SERVER fill:#2196F3,color:#fff\n    style CLIENT fill:#FF9800,color:#fff\n    style SPRING fill:#9C27B0,color:#fff\n```\n\n## Quick Start\n\n### With Spring Boot\n\nAdd the starter dependency:\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.marcosbarbero\u003c/groupId\u003e\n    \u003cartifactId\u003escim2-sdk-spring-boot-starter\u003c/artifactId\u003e\n    \u003cversion\u003e${scim2-sdk.version}\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nConfigure in `application.yml`:\n\n```yaml\nscim:\n  base-path: /scim/v2\n  persistence:\n    enabled: true  # enables JPA-backed storage\n  bulk:\n    enabled: true\n  filter:\n    enabled: true\n```\n\nThat's it! The starter auto-configures:\n- SCIM endpoints at `/scim/v2/*` (Users, Groups, Schemas, ResourceTypes, ServiceProviderConfig, Bulk)\n- JPA persistence with H2/PostgreSQL/MySQL/Oracle/MSSQL\n- Jackson serialization with SCIM module\n- Micrometer metrics (when on classpath)\n- RFC 9457 ProblemDetail error responses\n\nTo provide your own `ResourceHandler`:\n\n```kotlin\n@Component\nclass CustomUserHandler(private val userService: UserService) : ResourceHandler\u003cUser\u003e {\n    override val resourceType = User::class.java\n    override val endpoint = \"/Users\"\n\n    override fun create(resource: User, context: ScimRequestContext) = userService.create(resource)\n    override fun get(id: ResourceId, context: ScimRequestContext) = userService.findById(id.value)\n    // ... other methods\n}\n```\n\n#### Java equivalent\n\n```java\n@Component\npublic class CustomUserHandler implements ResourceHandler\u003cUser\u003e {\n    private final UserService userService;\n\n    public CustomUserHandler(UserService userService) {\n        this.userService = userService;\n    }\n\n    @Override public Class\u003cUser\u003e getResourceType() { return User.class; }\n    @Override public String getEndpoint() { return \"/Users\"; }\n\n    @Override\n    public User create(User resource, ScimRequestContext context) {\n        return userService.create(resource);\n    }\n\n    @Override\n    public User get(ResourceId id, ScimRequestContext context) {\n        return userService.findById(id.getValue());\n    }\n    // ... other methods\n}\n```\n\nSee the [Spring Boot full-stack sample](scim2-sdk-samples/sample-fullstack-spring/) for a production-like example with Keycloak, PostgreSQL, and React.\n\n### Without Spring Boot\n\nThe SDK works with any JVM HTTP framework. See the [plain Java full-stack sample](scim2-sdk-samples/sample-fullstack-plain/) for a production-like example using only the JDK HTTP server with PostgreSQL (plain JDBC) — no Spring Boot.\n\n### Client Usage\n\n```kotlin\n// Create client\nval client = ScimClientBuilder()\n    .baseUrl(\"https://scim.example.com/scim/v2\")\n    .transport(HttpClientTransport())\n    .serializer(JacksonScimSerializer())\n    .authentication(BearerTokenAuthentication(\"your-token\"))\n    .build()\n\n// Type-safe operations (recommended)\nval response = client.createUser(user)\nval user = client.getUser(id).value\nval results = client.searchUsers(\"userName sw \\\"john\\\"\")\nclient.patchUser(id, patch)\nclient.deleteUser(id)\n\n// Group operations\nval group = client.createGroup(Group(displayName = \"Admins\"))\nval groups = client.searchGroups()\n\n// Generic typed operations (reads endpoint from @ScimResource annotation)\nval created = client.createResource(user) // detects /Users from annotation\nval fetched = client.getResource\u003cUser\u003e(id)\n\n// Low-level operations (explicit endpoint and type)\nval response = client.create(\"/Users\", user, User::class)\nval results = client.search(\"/Users\", searchRequest, User::class)\n```\n\n#### Java client usage\n\n```java\nScimClient client = new ScimClientBuilder()\n    .baseUrl(\"https://scim.example.com/scim/v2\")\n    .transport(new HttpClientTransport())\n    .serializer(new JacksonScimSerializer())\n    .authentication(new BearerTokenAuthentication(\"your-token\"))\n    .build();\n\n// Type-safe operations via ScimClients utility class\nScimResponse\u003cUser\u003e response = ScimClients.createUser(client, user);\nUser user = ScimClients.getUser(client, id).getValue();\nScimClients.deleteUser(client, id);\nScimClients.searchUsers(client, new SearchRequest());\n\n// Group operations\nScimClients.createGroup(client, group);\nScimClients.getGroup(client, id);\n```\n\n## Outbound Provisioning\n\nThe SDK supports automatic outbound provisioning — when resources are created, updated, or deleted on your SCIM server, changes can be pushed to a target SCIM endpoint.\n\n### With Spring Boot\n\nSet `scim.client.base-url` to enable automatic outbound provisioning:\n\n```yaml\nscim:\n  client:\n    base-url: https://target-scim-server.example.com/scim/v2\n```\n\nThe SDK auto-configures:\n1. `SpringScimEventPublisher` — bridges SCIM events to Spring `ApplicationEvent`\n2. `ScimOutboundProvisioningListener` — reacts to events and pushes via `ScimClient`\n\nAuthentication to the target is handled by providing an `AuthenticationStrategy` bean (e.g., `BearerTokenAuthentication`, OAuth2 client credentials).\n\nFor reliable delivery, implement a custom `ScimEventPublisher` using the [transactional outbox pattern](docs/outbox-pattern.md) with [namastack-outbox](https://github.com/namastack/namastack-outbox).\n\n### Without Spring Boot\n\nUse `ScimOutboundEventPublisher` with a `ScimOutboundTarget` — no Spring needed:\n\n```java\nScimOutboundTarget target = new ScimClientOutboundTarget(scimClient);\nScimEventPublisher publisher = new ScimOutboundEventPublisher(target, handlers);\n\nvar dispatcher = new ScimEndpointDispatcher(\n    handlers, ..., eventPublisher = publisher, ...\n);\n```\n\nChain multiple publishers with `CompositeEventPublisher`:\n\n```java\nvar publisher = new CompositeEventPublisher(outboundPublisher, auditPublisher);\n```\n\n## Observability\n\nThe SDK provides built-in observability:\n\n- **Metrics** — [Micrometer](https://micrometer.io/) auto-configured with Prometheus, Grafana dashboard included\n- **Tracing** — [OpenTelemetry](https://opentelemetry.io/) auto-configured when on classpath (spans, trace IDs, exception recording)\n- **Structured logging** — SLF4J MDC with `scim.correlationId`, `scim.operation`, `scim.resourceType`\n- **Event correlation** — all events carry the tracer's correlation ID\n\nSee the [Observability Guide](docs/observability.md) for configuration, metrics reference, and custom implementation examples.\n\n### Quick Start (Spring Boot)\n\n```yaml\nmanagement:\n  endpoints:\n    web:\n      exposure:\n        include: health,prometheus,metrics\n```\n\nMetrics and tracing are automatically recorded for all SCIM operations. See the [Spring Boot full-stack sample](scim2-sdk-samples/sample-fullstack-spring/) for a complete example with Prometheus + Grafana.\n\n## Sample Applications\n\n| Sample | Description |\n|---|---|\n| [sample-fullstack-spring](scim2-sdk-samples/sample-fullstack-spring/) | **Production-like**: Spring Boot + PostgreSQL + Keycloak + React + bidirectional SCIM sync. `docker compose up -d` |\n| [sample-fullstack-plain](scim2-sdk-samples/sample-fullstack-plain/) | **Production-like**: JDK HttpServer + plain JDBC + PostgreSQL + Keycloak + React + outbound provisioning. No Spring. `docker compose up -d` |\n| [sample-server-spring](scim2-sdk-samples/sample-server-spring/) | Minimal Spring Boot server (hosts E2E and contract tests) |\n\n## Modules\n\n| Module | Description | Details |\n|---|---|---|\n| [`scim2-sdk-core`](scim2-sdk-core/) | Domain model, filter/path parsing, PATCH engine, serialization SPI | [README](scim2-sdk-core/README.md) |\n| [`scim2-sdk-server`](scim2-sdk-server/) | SCIM Service Provider framework (ports + adapters) | [README](scim2-sdk-server/README.md) |\n| [`scim2-sdk-client`](scim2-sdk-client/) | Fluent client API with Kotlin DSLs | [README](scim2-sdk-client/README.md) |\n| [`scim2-sdk-client-httpclient`](scim2-sdk-client-httpclient/) | Java HttpClient transport adapter | [README](scim2-sdk-client-httpclient/README.md) |\n| [`scim2-sdk-client-okhttp`](scim2-sdk-client-okhttp/) | OkHttp transport adapter | [README](scim2-sdk-client-okhttp/README.md) |\n| [`scim2-sdk-spring-boot-autoconfigure`](scim2-sdk-spring-boot-autoconfigure/) | Spring Boot auto-configuration | [README](scim2-sdk-spring-boot-autoconfigure/README.md) |\n| [`scim2-sdk-spring-boot-starter`](scim2-sdk-spring-boot-starter/) | Spring Boot starter (aggregates dependencies) | [README](scim2-sdk-spring-boot-starter/README.md) |\n| [`scim2-sdk-test`](scim2-sdk-test/) | Test fixtures, contract tests, in-memory server | [README](scim2-sdk-test/README.md) |\n| [`scim2-sdk-bom`](scim2-sdk-bom/) | Bill of Materials for version management | [README](scim2-sdk-bom/README.md) |\n\n## Configuration Properties\n\nAll properties are optional with sensible defaults:\n\n| Property | Default | Description |\n|---|---|---|\n| `scim.base-path` | `/scim/v2` | Base URL path for all SCIM endpoints |\n| `scim.base-url` | *(none)* | Base URL for absolute `meta.location` URIs. When set, all responses include `meta.location`. Example: `http://localhost:8080` |\n| `scim.bulk.enabled` | `true` | Enable [Bulk Operations (RFC 7644 §3.7)](https://www.rfc-editor.org/rfc/rfc7644#section-3.7) — allows clients to batch multiple create/update/delete operations into a single HTTP request, reducing round-trips for large provisioning jobs |\n| `scim.bulk.max-operations` | `1000` | Maximum number of individual operations allowed in a single bulk request |\n| `scim.bulk.max-payload-size` | `1048576` | Maximum payload size (bytes) for bulk requests (default: 1 MB) |\n| `scim.filter.enabled` | `true` | Enable [Filtering (RFC 7644 §3.4.2.2)](https://www.rfc-editor.org/rfc/rfc7644#section-3.4.2.2) — allows clients to query resources using expressions like `userName eq \"john\"` or `emails[type eq \"work\"]` |\n| `scim.filter.max-results` | `200` | Maximum number of resources returned by a filtered query |\n| `scim.etag.enabled` | `true` | Enable [ETags (RFC 7644 §3.14)](https://www.rfc-editor.org/rfc/rfc7644#section-3.14) — optimistic concurrency control using `If-Match` / `If-None-Match` headers to prevent lost updates when multiple clients modify the same resource |\n| `scim.patch.enabled` | `true` | Enable [PATCH Operations (RFC 7644 §3.5.2)](https://www.rfc-editor.org/rfc/rfc7644#section-3.5.2) — partial resource updates (add/remove/replace individual attributes) without replacing the entire resource |\n| `scim.sort.enabled` | `false` | Enable [Sorting (RFC 7644 §3.4.2.3)](https://www.rfc-editor.org/rfc/rfc7644#section-3.4.2.3) — allows clients to sort search results using `sortBy` and `sortOrder` query parameters |\n| `scim.change-password.enabled` | `false` | Enable [Change Password (RFC 7644 §3.5.2.1)](https://www.rfc-editor.org/rfc/rfc7644#section-3.5.2) — indicates the service provider supports password changes via PATCH |\n| `scim.pagination.default-page-size` | `100` | Default number of resources returned per page when the client doesn't specify `count` |\n| `scim.pagination.max-page-size` | `1000` | Maximum allowed page size — the server caps the client's `count` parameter to this value |\n| `scim.persistence.enabled` | `false` | Enable the OOTB JPA persistence adapter — stores SCIM resources in a relational database |\n| `scim.persistence.table-name` | `scim_resources` | Database table name for SCIM resource storage |\n| `scim.persistence.schema-name` | *(none)* | Database schema name (e.g., `scim`) — if set, the table is qualified as `schema.table` |\n| `scim.persistence.auto-migrate` | `false` | Run [Flyway](https://flywaydb.org/) migration on startup to create the `scim_resources` table automatically |\n| `scim.tracing.enabled` | `true` | Enable OpenTelemetry tracing auto-configuration. Set to `false` to disable even when OpenTelemetry is on the classpath |\n| `scim.client.base-url` | *(none)* | Base URL of the remote SCIM Service Provider — when set, auto-configures a `ScimClient` bean |\n| `scim.client.connect-timeout` | `10s` | TCP connection timeout for the SCIM client |\n| `scim.client.read-timeout` | `30s` | Read timeout for the SCIM client |\n| `scim.idp.provider` | *(none)* | Identity Provider type: `keycloak`, `okta`, `azure-ad`, `ping-federate`, `auth0` — auto-configures the corresponding `IdentityResolver` |\n| `scim.idp.client-id` | *(none)* | Client ID for Keycloak client-role extraction |\n| `scim.idp.namespace` | *(none)* | Auth0 custom namespace for role claims |\n| `scim.idp.claims.subject` | `sub` | JWT claim for subject |\n| `scim.idp.claims.email` | `email` | JWT claim for email |\n| `scim.idp.claims.name` | `name` | JWT claim for name |\n| `scim.idp.claims.roles` | `roles` | JWT claim for roles |\n| `scim.idp.claims.groups` | `groups` | JWT claim for groups |\n| `scim.idp.claims.custom` | `{}` | Additional custom claim mappings (key-value pairs) |\n\n## Database Support\n\nWhen `scim.persistence.enabled=true`, the JPA adapter stores SCIM resources as JSON in a single table:\n\n```sql\nCREATE TABLE scim_resources (\n    id              VARCHAR(255) NOT NULL PRIMARY KEY,\n    resource_type   VARCHAR(100) NOT NULL,    -- \"User\", \"Group\", etc.\n    external_id     VARCHAR(255),\n    display_name    VARCHAR(500),\n    resource_json   TEXT NOT NULL,             -- Full SCIM resource as JSON\n    version         BIGINT NOT NULL DEFAULT 1, -- ETag version\n    created         TIMESTAMP NOT NULL,\n    last_modified   TIMESTAMP NOT NULL\n);\n```\n\nThis design stores any SCIM resource type in one table, discriminated by `resource_type`. The full resource is preserved as JSON in `resource_json`, enabling schema-less flexibility while maintaining queryable metadata columns.\n\nReference schemas for specific databases:\n- [PostgreSQL](scim2-sdk-spring-boot-autoconfigure/src/main/resources/db/scim/schema-postgresql.sql)\n- [MySQL](scim2-sdk-spring-boot-autoconfigure/src/main/resources/db/scim/schema-mysql.sql)\n- [Oracle](scim2-sdk-spring-boot-autoconfigure/src/main/resources/db/scim/schema-oracle.sql)\n- [MS SQL Server](scim2-sdk-spring-boot-autoconfigure/src/main/resources/db/scim/schema-mssql.sql)\n- [H2 (testing)](scim2-sdk-spring-boot-autoconfigure/src/main/resources/db/scim/schema-h2.sql)\n\nOpt-in auto-migration via Flyway: set `scim.persistence.auto-migrate=true`.\n\n### Database Migration\n\nThree options for managing the SCIM schema:\n\n1. **Hibernate DDL (development)**: Set `spring.jpa.hibernate.ddl-auto=create-drop` — used in samples.\n\n2. **Manual migration**: Copy the reference schema from `db/scim/schema-{database}.sql` into your own Flyway/Liquibase migrations.\n\n3. **Auto-migration (opt-in)**: Enable the built-in Flyway migration:\n   ```yaml\n   scim:\n     persistence:\n       enabled: true\n       auto-migrate: true        # runs Flyway migration for SCIM tables\n       schema-name: my_schema    # optional, defaults to the datasource default schema\n   ```\n   This uses a separate Flyway history table (`scim_flyway_history`) so it does not conflict with your application's migrations. Requires `org.flywaydb:flyway-core` on the classpath.\n\n## API Reference\n\n- **[OpenAPI 3.1 Specification](docs/scim2-openapi.yaml)** — machine-readable API spec, usable with Swagger UI, Redoc, or any OpenAPI tooling\n- **[HTTP Examples](docs/api-reference.md)** — curl command examples for quick reference\n\n## Requirements\n\n- Java 25+\n- Maven 3.9+\n- Spring Boot 4.0+ (for starter, optional)\n\n## License\n\n[Apache License 2.0](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcosbarbero%2Fscim2-sdk-jvm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarcosbarbero%2Fscim2-sdk-jvm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcosbarbero%2Fscim2-sdk-jvm/lists"}