--- title: "Identity Provider Setup" audience: [admin, developer] summary: "Configure an OIDC identity provider (Keycloak-focused) so the console can authenticate users and assign the operator role." --- # Identity Provider Setup This guide covers configuring your OIDC identity provider to work with the member console. The examples use Keycloak but the concepts apply to any OIDC-compliant provider. ## Prerequisites - An OIDC identity provider with a client configured for Authorization Code + PKCE flow - The client ID and secret configured in `mc-config.yaml` (or equivalent flags) ## Standard OIDC Claims The member console reads the following standard claims from the **ID token**: | Claim | Used for | |-------|----------| | `sub` | Unique user identifier (OIDC subject) | | `email` | User's primary email | | `email_verified` | Whether the email is verified | | `name` | Display name | | `preferred_username` | Username (used for org slug derivation) | On first login, the member console auto-provisions a person, organization, and workspace from these claims. ## Role-Based Access: Operator Role The member console uses a single application-level role to gate access to the operator panel: - **Role name:** `operator-member` - **Where it's checked:** `internal/server/operator.go` and `operator_partials.go` ### How roles are extracted The member console extracts roles from the **access token** by decoding the JWT payload and reading every claim location an identity provider might use to carry roles, rather than trusting only one: 1. `roles` (top-level array) 2. `groups` (top-level array) 3. `realm_access.roles` (Keycloak realm roles) 4. `resource_access..roles` (Keycloak client-scoped roles, read only for this app's configured client ID; roles belonging to other clients are never included) The member console reads all four locations and merges their roles into one deduplicated list: a role that appears in more than one location is kept once, at the position where it first appears (the locations are read in the order listed above, and within a location, in the order the claim lists them). Because every location is read, a deployment can put `operator-member` in whichever claim its identity provider produces and the member console still recognizes it. ### Keycloak setup There are two ways to make the `operator-member` role visible in the access token: #### Option A: Client role with a protocol mapper (recommended) 1. Go to **Clients > member-console > Roles** 2. Create a role named `operator-member` 3. Assign it to the desired user(s) under **Users > (user) > Role Mappings > member-console** 4. Ensure there is a protocol mapper that emits client roles into a top-level `roles` claim in the access token: - **Mapper type:** User Client Role - **Client ID:** `member-console` - **Token Claim Name:** `roles` - **Claim JSON Type:** String - **Multivalued:** On - **Add to access token:** On This is the approach used by the test stack's Keycloak seed (`test/seed/keycloak/seed-keycloak.sh`). #### Option B: Realm role 1. Go to **Realm Roles** 2. Create a role named `operator-member` 3. Assign it to the desired user(s) 4. The role will appear in `realm_access.roles` in the access token by default This is simpler but mixes application roles with realm-level roles. ### Other identity providers For providers other than Keycloak, ensure that the access token includes `operator-member` in one of the four checked claim locations (`roles`, `groups`, `realm_access.roles`, or `resource_access..roles`). Most providers support custom claims or group mappings that can achieve this. ## Temporal Authorization Temporal requires its own role setup for JWT-based authorization. See [Temporal Authorization Setup](temporal-authorization-setup.md) for the full Temporal permissions configuration. ## Test Environment The test stack's Keycloak seed (`test/seed/keycloak/seed-keycloak.sh`) pre-configures: - A `member-console` client with the `operator-member` client role defined - The admin user mapped to the `operator-member` role - Protocol mappers that emit roles into the access token To use the test environment: ```bash cd test docker compose up -d member-console start ``` Login with `admin` / `admin` at `http://localhost:8081`. > **Run app OIDC on a dedicated realm, not Keycloak's `master` admin realm.** The member-facing "Identity and Access" link opens Keycloak's account console; on the `master` admin realm that console errors (*"Something went wrong"*) because the minted token carries no `aud: account` claim. The test stack therefore creates and runs on a dedicated `test` realm, where the account console works out of the box — do the same in production. See `status/issues.md`. > **Note:** The first `member-console start` after a fresh `docker compose up -d` used to fail with a Temporal auth error. This is fixed — compose now gates Temporal on its database and Keycloak being healthy, and member-console retries the initial Temporal connection. See `status/issues.md`.