{"id":50337255,"url":"https://github.com/swisscom/keycloak-broker","last_synced_at":"2026-05-29T14:30:28.946Z","repository":{"id":349111679,"uuid":"1201105285","full_name":"swisscom/keycloak-broker","owner":"swisscom","description":"A service-broker for OIDC clients on Keycloak 🔑","archived":false,"fork":false,"pushed_at":"2026-04-05T13:20:31.000Z","size":3170,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-04-29T07:41:52.071Z","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":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/swisscom.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","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-04-04T08:08:02.000Z","updated_at":"2026-04-05T13:20:37.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/swisscom/keycloak-broker","commit_stats":null,"previous_names":["swisscom/keycloak-broker"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/swisscom/keycloak-broker","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swisscom%2Fkeycloak-broker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swisscom%2Fkeycloak-broker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swisscom%2Fkeycloak-broker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swisscom%2Fkeycloak-broker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/swisscom","download_url":"https://codeload.github.com/swisscom/keycloak-broker/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/swisscom%2Fkeycloak-broker/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33657690,"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-29T02:00:06.066Z","response_time":107,"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-29T14:30:28.002Z","updated_at":"2026-05-29T14:30:28.927Z","avatar_url":"https://github.com/swisscom.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# keycloak-broker\n\nAn [Open Service Broker](https://www.openservicebrokerapi.org/) (OSB) API implementation that wraps the Keycloak Admin REST API, enabling platforms like Cloud Foundry or the Swisscom DevOps portal to provision and manage OIDC clients in Keycloak through the standard OSB interface.\nSwisscom developers can leverage this to use OIDC integrations into their applications in a fully self-service mode.\n\n## Architecture\n\n```\nmain.go → router → Echo HTTP server\n                  ├── /health, /healthz        → Keycloak realm connectivity check\n                  ├── /metrics                 → Prometheus metrics\n                  └── /v2/* (basic auth)       → OSB API\n                        ├── GET    /catalog\n                        ├── PUT    /service_instances/:id          → Provision\n                        ├── PATCH  /service_instances/:id          → Update\n                        ├── GET    /service_instances/:id          → Fetch\n                        ├── DELETE /service_instances/:id          → Deprovision\n                        ├── PUT    /service_instances/:id/service_bindings/:bid  → Bind\n                        ├── GET    /service_instances/:id/service_bindings/:bid  → Fetch binding\n                        └── DELETE /service_instances/:id/service_bindings/:bid  → Unbind\n```\n\n### Packages\n\n| Package | Purpose |\n|---------|---------|\n| `config` | Singleton configuration from environment variables |\n| `logger` | Thin wrapper around `slog` with printf-style helpers |\n| `catalog` | Loads `catalog.yaml` on first access, provides service/plan lookups |\n| `validation` | UUID format and catalog membership validation |\n| `keycloak` | HTTP client for Keycloak Admin API (token management, CRUD, OIDC discovery caching) |\n| `broker` | OSB API handlers + Keycloak ↔ OSB response mapping |\n| `router` | Echo setup with middleware (security headers, basic auth, request logging) |\n| `health` | Health endpoint verifying Keycloak connectivity |\n| `metrics` | Prometheus `/metrics` endpoint |\n\n## Design Decisions\n\n- **Fully stateless**: No local database. The OSB `instance_id` becomes the Keycloak `clientId`. Service and plan IDs are stored as Keycloak client attributes for later retrieval.\n- **Idempotent provisioning**: PUT checks if the client already exists. Returns `200` with existing data if so, `201` on new creation.\n- **In-place updates**: PATCH merges updated parameters (e.g. `redirectURIs`, `standardFlowEnabled`, `implicitFlowEnabled`, `directAccessGrantsEnabled`, `consentRequired`, `serviceAccountsEnabled`, `pkceEnabled`, `refreshTokenLifetime`, `accessTokenLifetime`) into the existing client without changing `clientId` or `clientSecret`.\n- **Binding is a no-op**: Bind/unbind intentionally do not cycle client credentials. They return the existing client credentials in a format well-suited for Cloud Foundry compatibility.\n\n## Service Catalog\n\nDefined in `catalog.yaml`. Ships with one service (`corpid`) and two plans:\n\n| Plan | Type | Description |\n|------|------|-------------|\n| `standard-client` | Confidential | Standard OIDC client with client secret |\n| `public-client` | Public | Public OIDC client (no client secret) |\n\n## Usage\n\nAll examples below show the JSON body for `PUT /v2/service_instances/\u003cinstance-id\u003e`.\n\n### Authorization Code Grant - `authorization_code`\n\nThe most common flow for server-side web applications. The client authenticates with a client secret and exchanges an authorization code for tokens. This is the default for a service instance and thus requires no other parameter.\n\n```json\n{\n  \"service_id\": \"fff5b36a-da19-4dc2-bd28-3dd331146290\",\n  \"plan_id\": \"40627d0f-dedd-4d68-8111-2ebae510ba1b\",\n  \"parameters\": {\n    \"redirectURIs\": [\"https://myapp.example.com/callback\"]\n  }\n}\n```\n\nUses the \"**standard-client**\" plan. The authorization code flow can be controlled via the parameter `standardFlowEnabled`, it is enabled by default and thus optional. PKCE is also enabled by default.\n\n### Authorization Code Grant - `public`\n\nFor single-page applications (SPAs) or mobile apps that cannot securely store a client secret. PKCE is also here enabled by default.\n\n```json\n{\n  \"service_id\": \"fff5b36a-da19-4dc2-bd28-3dd331146290\",\n  \"plan_id\": \"ece8f4ad-1ba0-481b-94b0-8fb4f066aa38\",\n  \"parameters\": {\n    \"redirectURIs\": [\"https://myapp.example.com/callback\"],\n    \"pkceEnabled\": false\n  }\n}\n```\n\nUses the \"**public-client**\" plan. No client secret is issued. PKCE is also enabled by default, and can be disabled by setting `pkceEnabled` to `false`, though this is not recommended.\n\n### Implicit Grant - `implicit`\n\nLegacy flow for SPAs that receive tokens directly from the authorization endpoint. Deprecated in OAuth 2.1, please prefer to use authorization code with PKCE instead.\n\n```json\n{\n  \"service_id\": \"fff5b36a-da19-4dc2-bd28-3dd331146290\",\n  \"plan_id\": \"40627d0f-dedd-4d68-8111-2ebae510ba1b\",\n  \"parameters\": {\n    \"redirectURIs\": [\"https://myapp.example.com/callback\"],\n    \"implicitFlowEnabled\": true\n  }\n}\n```\n\n### Client Credentials Grant - `client_credentials`\n\nFor service-to-service communication where no user is involved. The client authenticates directly with its client ID and secret to obtain an access token.\n\n```json\n{\n  \"service_id\": \"fff5b36a-da19-4dc2-bd28-3dd331146290\",\n  \"plan_id\": \"40627d0f-dedd-4d68-8111-2ebae510ba1b\",\n  \"parameters\": {\n    \"standardFlowEnabled\": false,\n    \"serviceAccountsEnabled\": true\n  }\n}\n```\n\nUse the `standard-client` plan (confidential). Disable `standardFlowEnabled` if the client is purely machine-to-machine and does not need the authorization code flow.\n\n## OSB Parameters\n\nThe following parameters can be passed in the `parameters` object when provisioning (PUT) or updating (PATCH) a service instance:\n\n| Parameter | Type | Default | Description |\n|-----------|------|---------|-------------|\n| `redirectURIs` | `[]string` | `[]` | List of allowed redirect URIs for the OIDC client |\n| `standardFlowEnabled` | `bool` | `true` | Enable the OAuth 2.0 authorization code flow (`authorization_code` grant) |\n| `implicitFlowEnabled` | `bool` | `false` | Enable the OAuth 2.0 implicit flow (`implicit` grant) |\n| `directAccessGrantsEnabled` | `bool` | `false` | Enable Resource Owner Password Credentials (`direct_access` grant) |\n| `consentRequired` | `bool` | `false` | Require user consent before granting access |\n| `serviceAccountsEnabled` | `bool` | `false` | Enable service accounts (`client_credentials` grant) |\n| `pkceEnabled` | `bool` | `true` | Enable Proof Key for Code Exchange (PKCE) |\n| `refreshTokenLifetime` | `int` | `0` | Refresh token lifetime in seconds (`0` = use system default) |\n| `accessTokenLifetime` | `int` | `0` | Access token lifetime in seconds (`0` = use system default) |\n\nOn update (PATCH), all parameters are optional. Only provided fields will be merged into the existing client configuration.\n\n## Configuration\n\nAll configuration is via environment variables:\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `PORT` | `8080` | HTTP listen port |\n| `BROKER_USERNAME` | _(empty)_ | Basic auth username (auth disabled if empty) |\n| `BROKER_PASSWORD` | _(empty)_ | Basic auth password |\n| `BROKER_LOG_LEVEL` | `info` | Log level (`debug`, `info`, `warn`, `error`) |\n| `BROKER_LOG_TIMESTAMP` | `false` | Include timestamps in log output |\n| `KEYCLOAK_URL` | `http://localhost:8080` | Keycloak base URL |\n| `KEYCLOAK_REALM` | _(empty)_ | Target Keycloak realm |\n| `KEYCLOAK_ADMIN` | _(empty)_ | Keycloak admin username |\n| `KEYCLOAK_PASSWORD` | _(empty)_ | Keycloak admin password |\n\n## Development\n\n### Prerequisites\n\n- Go 1.25+\n- Docker (for local Keycloak)\n- [air](https://github.com/air-verse/air) (optional, for hot-reload)\n\n### Quick Start\n\n```bash\n# start a local Keycloak dev server with a pre-configured realm\nmake start-keycloak\n\n# run the broker with hot-reload\nmake run\n\n# or run directly with race detector\nmake dev\n```\n\n### Example OSB Operations\n\n```bash\n# provision an OIDC client\nmake provision-instance\n\n# update parameters on an existing instance (e.g. redirectURIs)\nmake update-instance\n\n# fetch the instance\nmake fetch-instance\n\n# bind (returns credentials)\nmake bind-instance\n\n# fetch the binding (returns credentials)\nmake fetch-binding\n\n# unbind (does nothing except to satisfy the OSB spec)\nmake delete-binding\n\n# deprovision (deletes the OIDC client)\nmake deprovision-instance\n```\n\n### Other Targets\n\n```bash\nmake health-check    # check broker health\nmake metrics-check   # check Prometheus metrics\nmake build           # build binary\nmake test            # run tests with race detector\nmake init            # initializes and updates all golang module vendoring\nmake install-air     # installs \"air\" hot-reloader\n```\n\n## Complete test flow with OIDC clients\n\n```bash\nmake start-keycloak\nmake run\nmake provision-instance\nmake bind-instance\nmake deprovision-instance\nmake test-oidc-client\nmake test-public-client\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fswisscom%2Fkeycloak-broker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fswisscom%2Fkeycloak-broker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fswisscom%2Fkeycloak-broker/lists"}