Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ A collection of Helm charts primarily used for **Lifecycle** application deploym
| :--- | :--- | :--- | :--- |
| [keycloak-operator](./charts/keycloak-operator) | `0.1.0` | `26.4.7` | Helm chart for Keycloak operator based on the [official manifests](https://www.keycloak.org/operator/installation#_installing_by_using_kubectl_without_operator_lifecycle_manager) |
| [lifecycle](./charts/lifecycle) | `0.9.7` | `0.1.15` | A Helm umbrella chart for full Lifecycle stack |
| [lifecycle-keycloak](./charts/lifecycle-keycloak) | `0.7.3` | `0.0.0` | Keycloak instance for Lifecycle stack with automated Operator-driven setup and imports |
| [lifecycle-keycloak](./charts/lifecycle-keycloak) | `0.8.0` | `0.0.0` | Keycloak instance for Lifecycle stack with automated Operator-driven setup and imports |
| [lifecycle-ui](./charts/lifecycle-ui) | `0.3.2` | `0.1.4` | A Helm chart for Lifecycle UI (Next.js) |
2 changes: 1 addition & 1 deletion charts/lifecycle-keycloak/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ apiVersion: v2
name: lifecycle-keycloak
description: Keycloak instance for Lifecycle stack with automated Operator-driven setup and imports
type: application
version: 0.7.3
version: 0.8.0
appVersion: 0.0.0
home: https://goodrxoss.github.io/helm-charts/charts/lifecycle-keycloak/

Expand Down
114 changes: 112 additions & 2 deletions charts/lifecycle-keycloak/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# lifecycle-keycloak

![Version: 0.7.3](https://img.shields.io/badge/Version-0.7.3-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.0.0](https://img.shields.io/badge/AppVersion-0.0.0-informational?style=flat-square)
![Version: 0.8.0](https://img.shields.io/badge/Version-0.8.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 0.0.0](https://img.shields.io/badge/AppVersion-0.0.0-informational?style=flat-square)

Keycloak instance for Lifecycle stack with automated Operator-driven setup and imports

Expand Down Expand Up @@ -111,6 +111,50 @@ clients:

> **Important:** These values act as **bootstrap** values. After the initial creation, if you change them in the Keycloak Admin UI, the values in Helm will no longer stay in sync.

### 4. Lifecycle MCP OAuth Client

The chart can configure a public PKCE client and anonymous Dynamic Client Registration policies for the Lifecycle MCP endpoint. These values are disabled by default and can be enabled when the Lifecycle app exposes `/mcp`.

```yaml
clients:
lifecycleMcp:
enabled: true
resourceUrl: "https://app.example.com/mcp"

clientScopes:
lifecycleMcp:
realmDefault: true

clientRegistrationPolicies:
anonymous:
enabled: true
trustedHosts:
hostSendingRegistrationRequestMustMatch: false
clientUrisMustMatch: true
hosts:
- 127.0.0.1
- localhost
allowedRegistrationWebOrigins:
origins:
- "http://localhost:6274"
- "http://127.0.0.1:6274"
allowedClientAuthenticatorTypes:
types:
- none
```

The MCP client scope emits the configured resource URL as the access-token audience and maps the `githubUsername` user attribute into the `github_username` claim.

By default, the MCP client scope is added to `defaultDefaultClientScopes` when the MCP client or scope is enabled. This makes DCR-registered clients receive the MCP audience and `github_username` claim without requiring every client harness to request `scope=lifecycle-mcp` correctly. Operators can set `clientScopes.lifecycleMcp.realmDefault=false` if they prefer to require explicit scope requests.

`clients.lifecycleMcp.resourceUrl` is required when the MCP client is enabled. It should be the externally reachable MCP resource URL, for example `https://app.example.com/mcp`; do not leave it as a localhost value in shared environments.

The pre-registered MCP client is public and uses PKCE with loopback redirect URI wildcards for CLI clients. Anonymous DCR is constrained to loopback client/redirect URI hosts, public clients (`none` authenticator), configured client scopes, and no caller-supplied protocol mappers by default. Registration web origins are for browser-based testing tools such as MCP Inspector; CLI OAuth flows do not use them.

Registration web origins and redirect URIs are different controls: registration web origins are CORS origins for browser-based DCR callers, while redirect URIs are OAuth callback URLs for the authorization flow.

MCP tokens are expected to use the MCP resource URL as `aud`. The existing Lifecycle API middleware validates the existing core audience (`lifecycle-core`) and should not be reused for MCP traffic without a separate MCP audience validator.

---

## Life-cycle Management & Realm Import
Expand All @@ -128,7 +172,7 @@ This chart uses the `KeycloakRealmImport` resource for the initial setup.
```shell
helm upgrade -i lifecycle-keycloak \
oci://ghcr.io/goodrxoss/helm-charts/lifecycle-keycloak \
--version 0.7.3 \
--version 0.8.0 \
-f values.yaml \
-n lifecycle-keycloak \
--create-namespace
Expand All @@ -139,8 +183,74 @@ helm upgrade -i lifecycle-keycloak \
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| annotations | object | `{}` | |
| clientRegistrationPolicies.anonymous.allowedClientAuthenticatorTypes.enabled | bool | `true` | |
| clientRegistrationPolicies.anonymous.allowedClientAuthenticatorTypes.name | string | `"Allowed Client Authenticator Types"` | |
| clientRegistrationPolicies.anonymous.allowedClientAuthenticatorTypes.types[0] | string | `"none"` | |
| clientRegistrationPolicies.anonymous.allowedClientScopes.enabled | bool | `true` | |
| clientRegistrationPolicies.anonymous.allowedClientScopes.name | string | `"Allowed Client Scopes"` | |
| clientRegistrationPolicies.anonymous.allowedClientScopes.scopes[0] | string | `"roles"` | |
| clientRegistrationPolicies.anonymous.allowedClientScopes.scopes[1] | string | `"profile"` | |
| clientRegistrationPolicies.anonymous.allowedClientScopes.scopes[2] | string | `"basic"` | |
| clientRegistrationPolicies.anonymous.allowedClientScopes.scopes[3] | string | `"email"` | |
| clientRegistrationPolicies.anonymous.allowedClientScopes.scopes[4] | string | `"offline_access"` | |
| clientRegistrationPolicies.anonymous.allowedClientScopes.scopes[5] | string | `"lifecycle-mcp"` | |
| clientRegistrationPolicies.anonymous.allowedProtocolMappers.enabled | bool | `true` | |
| clientRegistrationPolicies.anonymous.allowedProtocolMappers.mappers | list | `[]` | |
| clientRegistrationPolicies.anonymous.allowedProtocolMappers.name | string | `"Allowed Protocol Mappers"` | |
| clientRegistrationPolicies.anonymous.allowedRegistrationWebOrigins.enabled | bool | `true` | |
| clientRegistrationPolicies.anonymous.allowedRegistrationWebOrigins.name | string | `"Allowed Registration Web Origins"` | |
| clientRegistrationPolicies.anonymous.allowedRegistrationWebOrigins.origins[0] | string | `"http://localhost:6274"` | |
| clientRegistrationPolicies.anonymous.allowedRegistrationWebOrigins.origins[1] | string | `"http://127.0.0.1:6274"` | |
| clientRegistrationPolicies.anonymous.allowedRegistrationWebOrigins.origins[2] | string | `"http://localhost:5173"` | |
| clientRegistrationPolicies.anonymous.allowedRegistrationWebOrigins.origins[3] | string | `"http://127.0.0.1:5173"` | |
| clientRegistrationPolicies.anonymous.allowedRegistrationWebOrigins.origins[4] | string | `"http://localhost:3000"` | |
| clientRegistrationPolicies.anonymous.allowedRegistrationWebOrigins.origins[5] | string | `"http://127.0.0.1:3000"` | |
| clientRegistrationPolicies.anonymous.enabled | bool | `false` | |
| clientRegistrationPolicies.anonymous.extraPolicies | list | `[]` | |
| clientRegistrationPolicies.anonymous.maxClients.count | int | `1000` | |
| clientRegistrationPolicies.anonymous.maxClients.enabled | bool | `true` | |
| clientRegistrationPolicies.anonymous.maxClients.name | string | `"Max Clients Limit"` | |
| clientRegistrationPolicies.anonymous.trustedHosts.clientUrisMustMatch | bool | `true` | |
| clientRegistrationPolicies.anonymous.trustedHosts.enabled | bool | `true` | |
| clientRegistrationPolicies.anonymous.trustedHosts.hostSendingRegistrationRequestMustMatch | bool | `false` | |
| clientRegistrationPolicies.anonymous.trustedHosts.hosts[0] | string | `"127.0.0.1"` | |
| clientRegistrationPolicies.anonymous.trustedHosts.hosts[1] | string | `"localhost"` | |
| clientRegistrationPolicies.anonymous.trustedHosts.name | string | `"Trusted Hosts"` | |
| clientScopes.lifecycleMcp.audienceMapper.audience | string | `nil` | |
| clientScopes.lifecycleMcp.audienceMapper.enabled | bool | `true` | |
| clientScopes.lifecycleMcp.audienceMapper.name | string | `"Lifecycle MCP resource audience"` | |
| clientScopes.lifecycleMcp.description | string | `"Claims required by Lifecycle MCP clients."` | |
| clientScopes.lifecycleMcp.enabled | bool | `false` | |
| clientScopes.lifecycleMcp.githubUsernameMapper.claimName | string | `"github_username"` | |
| clientScopes.lifecycleMcp.githubUsernameMapper.enabled | bool | `true` | |
| clientScopes.lifecycleMcp.githubUsernameMapper.name | string | `"Github username"` | |
| clientScopes.lifecycleMcp.githubUsernameMapper.userAttribute | string | `"githubUsername"` | |
| clientScopes.lifecycleMcp.name | string | `"lifecycle-mcp"` | |
| clientScopes.lifecycleMcp.protocolMappers | list | `[]` | |
| clientScopes.lifecycleMcp.realmDefault | bool | `true` | |
| clients.lifecycleCore.clientId | string | `"lifecycle-core"` | |
| clients.lifecycleCore.enabled | bool | `true` | |
| clients.lifecycleMcp.attributes | object | `{}` | |
| clients.lifecycleMcp.clientId | string | `"lifecycle-mcp"` | |
| clients.lifecycleMcp.defaultClientScopes[0] | string | `"roles"` | |
| clients.lifecycleMcp.defaultClientScopes[1] | string | `"profile"` | |
| clients.lifecycleMcp.defaultClientScopes[2] | string | `"basic"` | |
| clients.lifecycleMcp.defaultClientScopes[3] | string | `"email"` | |
| clients.lifecycleMcp.defaultClientScopes[4] | string | `"lifecycle-mcp"` | |
| clients.lifecycleMcp.directAccessGrantsEnabled | bool | `false` | |
| clients.lifecycleMcp.enabled | bool | `false` | |
| clients.lifecycleMcp.fullScopeAllowed | bool | `true` | |
| clients.lifecycleMcp.name | string | `"Lifecycle MCP"` | |
| clients.lifecycleMcp.optionalClientScopes[0] | string | `"offline_access"` | |
| clients.lifecycleMcp.pkceCodeChallengeMethod | string | `"S256"` | |
| clients.lifecycleMcp.protocolMappers | list | `[]` | |
| clients.lifecycleMcp.publicClient | bool | `true` | |
| clients.lifecycleMcp.redirectUris[0] | string | `"http://127.0.0.1/*"` | |
| clients.lifecycleMcp.redirectUris[1] | string | `"http://localhost/*"` | |
| clients.lifecycleMcp.resourceUrl | string | `nil` | |
| clients.lifecycleMcp.serviceAccountsEnabled | bool | `false` | |
| clients.lifecycleMcp.standardFlowEnabled | bool | `true` | |
| clients.lifecycleMcp.webOrigins[0] | string | `"+"` | |
| clients.lifecycleUi.clientId | string | `"lifecycle-ui"` | |
| clients.lifecycleUi.clientSecret.secretKeyRef.key | string | `nil` | |
| clients.lifecycleUi.clientSecret.secretKeyRef.name | string | `nil` | |
Expand Down
44 changes: 44 additions & 0 deletions charts/lifecycle-keycloak/README.md.gotmpl
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,50 @@ clients:

> **Important:** These values act as **bootstrap** values. After the initial creation, if you change them in the Keycloak Admin UI, the values in Helm will no longer stay in sync.

### 4. Lifecycle MCP OAuth Client

The chart can configure a public PKCE client and anonymous Dynamic Client Registration policies for the Lifecycle MCP endpoint. These values are disabled by default and can be enabled when the Lifecycle app exposes `/mcp`.

```yaml
clients:
lifecycleMcp:
enabled: true
resourceUrl: "https://app.example.com/mcp"

clientScopes:
lifecycleMcp:
realmDefault: true

clientRegistrationPolicies:
anonymous:
enabled: true
trustedHosts:
hostSendingRegistrationRequestMustMatch: false
clientUrisMustMatch: true
hosts:
- 127.0.0.1
- localhost
allowedRegistrationWebOrigins:
origins:
- "http://localhost:6274"
- "http://127.0.0.1:6274"
allowedClientAuthenticatorTypes:
types:
- none
```

The MCP client scope emits the configured resource URL as the access-token audience and maps the `githubUsername` user attribute into the `github_username` claim.

By default, the MCP client scope is added to `defaultDefaultClientScopes` when the MCP client or scope is enabled. This makes DCR-registered clients receive the MCP audience and `github_username` claim without requiring every client harness to request `scope=lifecycle-mcp` correctly. Operators can set `clientScopes.lifecycleMcp.realmDefault=false` if they prefer to require explicit scope requests.

`clients.lifecycleMcp.resourceUrl` is required when the MCP client is enabled. It should be the externally reachable MCP resource URL, for example `https://app.example.com/mcp`; do not leave it as a localhost value in shared environments.

The pre-registered MCP client is public and uses PKCE with loopback redirect URI wildcards for CLI clients. Anonymous DCR is constrained to loopback client/redirect URI hosts, public clients (`none` authenticator), configured client scopes, and no caller-supplied protocol mappers by default. Registration web origins are for browser-based testing tools such as MCP Inspector; CLI OAuth flows do not use them.

Registration web origins and redirect URIs are different controls: registration web origins are CORS origins for browser-based DCR callers, while redirect URIs are OAuth callback URLs for the authorization flow.

MCP tokens are expected to use the MCP resource URL as `aud`. The existing Lifecycle API middleware validates the existing core audience (`lifecycle-core`) and should not be reused for MCP traffic without a separate MCP audience validator.

---

## Life-cycle Management & Realm Import
Expand Down
Loading
Loading