{"id":50448257,"url":"https://github.com/cerberauth/scimply","last_synced_at":"2026-05-31T23:01:26.290Z","repository":{"id":357010692,"uuid":"1186489386","full_name":"cerberauth/scimply","owner":"cerberauth","description":null,"archived":false,"fork":false,"pushed_at":"2026-05-10T21:58:06.000Z","size":152,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-10T23:33:31.426Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cerberauth.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":"audit/audit.go","citation":null,"codeowners":".github/CODEOWNERS","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},"funding":{"github":["emmanuelgautier"],"buy_me_a_coffee":"emmanuelgautier"}},"created_at":"2026-03-19T17:18:18.000Z","updated_at":"2026-05-10T21:57:42.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/cerberauth/scimply","commit_stats":null,"previous_names":["cerberauth/scimply"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/cerberauth/scimply","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cerberauth%2Fscimply","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cerberauth%2Fscimply/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cerberauth%2Fscimply/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cerberauth%2Fscimply/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cerberauth","download_url":"https://codeload.github.com/cerberauth/scimply/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cerberauth%2Fscimply/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33752286,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-31T02:00:06.040Z","response_time":95,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":[],"created_at":"2026-05-31T23:01:25.460Z","updated_at":"2026-05-31T23:01:26.275Z","avatar_url":"https://github.com/cerberauth.png","language":"Go","funding_links":["https://github.com/sponsors/emmanuelgautier","https://buymeacoffee.com/emmanuelgautier"],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# scimply\n\n**A complete SCIM 2.0 (and 1.1) server library for Go — RFC-compliant, zero-dependency core, and multiple backends.**\n\n[![Join Discord](https://img.shields.io/discord/1242773130137833493?label=Discord\u0026style=for-the-badge)](https://www.cerberauth.com/community)\n[![Build](https://img.shields.io/github/actions/workflow/status/cerberauth/scimply/ci.yml?branch=main\u0026label=build\u0026style=for-the-badge)](https://github.com/cerberauth/scimply/actions/workflows/ci.yml)\n![Latest version](https://img.shields.io/github/v/release/cerberauth/scimply?sort=semver\u0026style=for-the-badge)\n[![Coverage](https://img.shields.io/codecov/c/gh/cerberauth/scimply?style=for-the-badge)](https://codecov.io/gh/cerberauth/scimply)\n[![Go Report Card](https://goreportcard.com/badge/github.com/cerberauth/scimply?style=for-the-badge)](https://goreportcard.com/report/github.com/cerberauth/scimply)\n[![GoDoc](https://img.shields.io/badge/godoc-reference-5272B4.svg?style=for-the-badge)](https://pkg.go.dev/github.com/cerberauth/scimply)\n[![Stars](https://img.shields.io/github/stars/cerberauth/scimply?style=for-the-badge)](https://github.com/cerberauth/scimply)\n[![License](https://img.shields.io/github/license/cerberauth/scimply?style=for-the-badge)](https://github.com/cerberauth/scimply/blob/main/LICENSE)\n\n\u003c/div\u003e\n\n---\n\nA complete SCIM 2.0 (and 1.1) server library for Go.\n\n## Features\n\n- **Full SCIM 2.0** (RFC 7642, RFC 7643, RFC 7644) implementation\n- **SCIM 1.1 support** via a conversion layer\n- **Zero-dependency core** — `schema/`, `resource/`, `protocol/`, `store/`, `server/` use only the Go standard library\n- **Filter parser** — recursive-descent ABNF parser for the full RFC 7644 filter grammar\n- **PATCH engine** — complete RFC 7644 §3.5.2 semantics with atomicity\n- **Multiple backends** — in-memory, PostgreSQL, MySQL/MariaDB, MongoDB, SCIM proxy\n- **Compliance test suite** — reusable against any SCIM server\n- **Structured audit logging**\n\n## Quick Start\n\n```go\npackage main\n\nimport (\n    \"log\"\n    \"net/http\"\n\n    \"github.com/cerberauth/scimply/schema\"\n    \"github.com/cerberauth/scimply/server\"\n    \"github.com/cerberauth/scimply/store\"\n)\n\nfunc main() {\n    reg := schema.NewRegistry()\n    reg.RegisterDefaults() // User, Group, EnterpriseUser\n\n    srv, err := server.New(\n        server.WithStore(store.NewMemoryStore()),\n        server.WithSchemaRegistry(reg),\n        server.WithBasePath(\"/scim/v2\"),\n        server.WithBearerTokenAuth(func(token string) (bool, error) {\n            return token == \"my-secret-token\", nil\n        }),\n    )\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    log.Println(\"SCIM 2.0 server listening on :8080\")\n    log.Fatal(http.ListenAndServe(\":8080\", srv))\n}\n```\n\n## Standalone Server\n\nscimply also ships as a ready-to-run binary. No Go code needed — point it at a YAML config file and start serving SCIM.\n\n```sh\n# install\nbrew install cerberauth/tap/scimply\n# or: go install github.com/cerberauth/scimply/cmd/scimply@latest\n\n# create scimply.yaml\ncat \u003e scimply.yaml \u003c\u003cEOF\nserver:\n  addr: \":8080\"\n  base_path: \"/scim/v2\"\n\nauth:\n  type: bearer_token\n  tokens:\n    - \"my-secret-token\"\n\nstore:\n  type: memory\nEOF\n\n# run\nscimply serve\n```\n\nUse `-c` to pass a custom config path: `scimply serve -c /etc/scimply/config.yaml`.\n\nSee the [configuration reference](https://cerberauth.com/docs/scimply/reference/configuration/) for all options (store backends, TLS, audit logging, etc.).\n\n## API Endpoints\n\n| Method | Path | Description |\n|--------|------|-------------|\n| GET | `/scim/v2/ServiceProviderConfig` | Server capabilities |\n| GET | `/scim/v2/ResourceTypes` | Supported resource types |\n| GET | `/scim/v2/Schemas` | Schema definitions |\n| GET | `/scim/v2/Users` | List users (supports filter, sort, pagination) |\n| POST | `/scim/v2/Users` | Create user |\n| GET | `/scim/v2/Users/{id}` | Get user |\n| PUT | `/scim/v2/Users/{id}` | Replace user |\n| PATCH | `/scim/v2/Users/{id}` | Patch user |\n| DELETE | `/scim/v2/Users/{id}` | Delete user |\n| POST | `/scim/v2/Users/.search` | Query via POST body |\n| POST | `/scim/v2/Bulk` | Bulk operations |\n| *(same for Groups)* | | |\n\n## Backends\n\n### In-Memory (development and testing)\n\n```go\nstore.NewMemoryStore()\n```\n\n### PostgreSQL\n\n```go\nimport \"github.com/cerberauth/scimply/connector/postgres\"\n\npgStore, err := postgres.New(\n    postgres.WithDSN(\"postgres://user:pass@localhost:5432/scimdb\"),\n    postgres.WithAutoMigrate(true),\n    postgres.WithTablePrefix(\"scim_\"),\n    postgres.WithSchemaRegistry(reg),\n)\nif err != nil { log.Fatal(err) }\ndefer pgStore.Close(context.Background())\nif err := pgStore.Init(context.Background()); err != nil { log.Fatal(err) }\n```\n\n### MySQL / MariaDB\n\n```go\nimport \"github.com/cerberauth/scimply/connector/mysql\"\n\nmyStore, err := mysql.New(\n    mysql.WithDSN(\"user:pass@tcp(localhost:3306)/scimdb?parseTime=true\"),\n    mysql.WithAutoMigrate(true),\n)\n```\n\n### MongoDB\n\n```go\nimport \"github.com/cerberauth/scimply/connector/mongodb\"\n\nmgStore, err := mongodb.New(\n    mongodb.WithURI(\"mongodb://localhost:27017\"),\n    mongodb.WithDatabase(\"scimdb\"),\n    mongodb.WithAutoMigrate(true),\n)\n```\n\n### Table \u0026 Field Mapping (existing schemas)\n\nAll SQL connectors support mapping each SCIM resource type to a specific table and each SCIM attribute to a specific column. Columns can live in different tables — declare JOINs with `JoinDef`. MongoDB supports mapping to a specific collection and arbitrary BSON field paths, with optional cross-collection `$lookup`.\n\nWhen `FieldMappings` is set for a resource type the connector enters **column mode**: it reads and writes individual columns instead of a JSON blob, and `AutoMigrate` is skipped for that type.\n\n```go\nimport (\n    sqlconn \"github.com/cerberauth/scimply/connector/sql\"\n    \"github.com/cerberauth/scimply/connector/postgres\"\n)\n\npgStore, err := postgres.New(\n    postgres.WithDSN(dsn),\n\n    // Map \"User\" to existing tables — no data column required.\n    postgres.WithResourceConfig(\"User\", postgres.ResourceTableConfig{\n        Table: \"accounts\",\n        FieldMappings: map[string]sqlconn.ColumnRef{\n            \"id\":                {Column: \"id\"},\n            \"userName\":          {Column: \"email\"},\n            \"active\":            {Column: \"is_active\"},\n            \"meta.created\":      {Column: \"created_at\"},\n            \"meta.lastModified\": {Column: \"updated_at\"},\n            // Attributes in a joined table:\n            \"name.givenName\":  {Table: \"profiles\", Column: \"first_name\"},\n            \"name.familyName\": {Table: \"profiles\", Column: \"last_name\"},\n        },\n        Joins: []postgres.JoinDef{{\n            Table:      \"profiles\",\n            Condition:  \"profiles.account_id = accounts.id\",\n            JoinType:   \"LEFT\",\n            ForeignKey: \"account_id\", // FK column used for writes\n            DeleteJoin: true,\n        }},\n    }),\n\n    // Convenience helpers build up the same config incrementally:\n    //   postgres.WithTableName(\"Group\", \"teams\")\n    //   postgres.WithFieldMapping(\"Group\", \"displayName\", \"name\")\n    //   postgres.WithTableFieldMapping(\"Group\", \"members.value\", \"team_members\", \"user_id\")\n    //   postgres.WithJoin(\"Group\", postgres.JoinDef{...})\n\n    postgres.WithSchemaRegistry(reg),\n)\n\n// Access the raw connection pool for queries beyond the declarative config.\npool := pgStore.Pool() // *pgxpool.Pool\n```\n\nFor MongoDB:\n\n```go\nimport \"github.com/cerberauth/scimply/connector/mongodb\"\n\nmgStore, err := mongodb.New(\n    mongodb.WithURI(uri),\n    mongodb.WithDatabase(\"myapp\"),\n    // Map \"User\" to the \"users\" collection with custom field paths.\n    mongodb.WithResourceConfig(\"User\", mongodb.ResourceCollectionConfig{\n        Collection: \"users\",\n        FieldMappings: map[string]string{\n            \"id\":       \"_id\",\n            \"userName\": \"email\",\n            \"active\":   \"account.active\",\n        },\n        // Cross-collection lookup for 1:1 profile documents:\n        Lookups: []mongodb.LookupConfig{{\n            From: \"profiles\", LocalField: \"_id\", ForeignField: \"userId\", As: \"profile\",\n            FieldMappings: map[string]string{\n                \"name.givenName\":  \"profile.firstName\",\n                \"name.familyName\": \"profile.lastName\",\n            },\n        }},\n    }),\n)\n\ndb := mgStore.Database() // *mongo.Database — escape hatch for complex queries\n```\n\n### SCIM-to-SCIM Proxy\n\n```go\nimport scimclient \"github.com/cerberauth/scimply/connector/scim\"\n\nupstream, err := scimclient.New(\n    scimclient.WithBaseURL(\"https://api.example.com/scim/v2\"),\n    scimclient.WithBearerToken(\"upstream-token\"),\n)\n```\n\n## Filter Support\n\nFull RFC 7644 filter syntax:\n\n```\nGET /scim/v2/Users?filter=userName eq \"bjensen\"\nGET /scim/v2/Users?filter=emails[type eq \"work\" and value co \"@example.com\"]\nGET /scim/v2/Users?filter=active eq true and userType eq \"Employee\"\nGET /scim/v2/Users?filter=not (userName eq \"bjensen\")\n```\n\n## PATCH Operations\n\nFull RFC 7644 §3.5.2 PATCH semantics:\n\n```json\nPATCH /scim/v2/Users/123\n{\n  \"schemas\": [\"urn:ietf:params:scim:api:messages:2.0:PatchOp\"],\n  \"Operations\": [\n    {\"op\": \"replace\", \"path\": \"active\", \"value\": false},\n    {\"op\": \"add\", \"path\": \"emails\", \"value\": [{\"type\": \"home\", \"value\": \"home@example.com\"}]},\n    {\"op\": \"replace\", \"path\": \"emails[type eq \\\"work\\\"].value\", \"value\": \"new@example.com\"},\n    {\"op\": \"remove\", \"path\": \"members[value eq \\\"user-456\\\"]\"}\n  ]\n}\n```\n\n## SCIM 1.1\n\nThe library supports SCIM 1.1 via a conversion layer in the `v1/` package. Resources are converted to/from the internal SCIM 2.0 representation at the boundary.\n\nKey 1.1 differences handled:\n- Schema URIs: `urn:scim:schemas:core:1.0` ↔ `urn:ietf:params:scim:schemas:core:2.0:User`\n- Service provider endpoint: `/ServiceProviderConfigs` (plural)\n- Error format: `{\"Errors\": [{\"code\": \"404\", \"description\": \"...\"}]}`\n- Simplified filter operators (no `ne`, `ew`, `not`, `[]`)\n\n## Compliance Test Suite\n\nRun the standard SCIM 2.0 compliance suite against any server:\n\n```go\nimport \"github.com/cerberauth/scimply/compliance\"\n\nfunc TestMyServer(t *testing.T) {\n    srv := startMyServer(t)\n    compliance.RunSuite(t, compliance.SuiteConfig{\n        BaseURL:    srv.URL + \"/scim/v2\",\n        AuthHeader: \"Bearer test-token\",\n    })\n}\n```\n\n## License\n\nThis repository is licensed under the [MIT License](https://github.com/cerberauth/scimply/blob/main/LICENSE) @ [CerberAuth](https://www.cerberauth.com/). You are free to use, modify, and distribute the contents of this repository for educational and testing purposes.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcerberauth%2Fscimply","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcerberauth%2Fscimply","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcerberauth%2Fscimply/lists"}