원클릭으로
identity-platform
Wire charms to the Canonical Identity Platform (Hydra / Kratos / login-ui) for OIDC, OAuth, and federated login.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Wire charms to the Canonical Identity Platform (Hydra / Kratos / login-ui) for OIDC, OAuth, and federated login.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Known-good Juju bundle shapes (COS Lite, 12-Factor + COS, Identity Platform, Charmed Kubeflow) — canonical app lists and relation edges
Implementing Juju actions for operational tasks in charms. WHEN: add Juju actions to a charm, implement backup / rotate-credentials / restore actions, declare actions in charmcraft.yaml, write action handlers, test actions with Scenario, run actions with juju run.
Adding and validating charm configuration options. WHEN: add charm configuration options, declare config in charmcraft.yaml, validate config values in config-changed, apply config to a Pebble layer, apply config to a machine charm, write Scenario tests for config.
End-to-end workflow for building ops-framework charms for custom applications (K8s and machine). WHEN: build a custom Juju charm, write src/charm.py with Pebble, write a machine charm with systemd, decide K8s vs machine substrate, scaffold a non-paas-charm with charmcraft init, customise the ops framework charm lifecycle.
Workflow for charming infrastructure software (databases, caches, message brokers, proxies, monitoring). WHEN: charm infrastructure software, charm a database / cache / message broker / proxy / monitoring system, implement peer relations, leader election, primary/replica failover, backup and restore actions, clustering.
Expert guidance for developing, building, testing, and publishing Juju charms using charmcraft. WHEN: edit charmcraft.yaml, run charmcraft pack, run charmcraft init, manage charm libraries, publish a charm to Charmhub, set up multi-base builds, configure relations / config options, set up storage or containers.
| name | identity-platform |
| description | Wire charms to the Canonical Identity Platform (Hydra / Kratos / login-ui) for OIDC, OAuth, and federated login. |
Use this skill when a charm needs login, OIDC tokens, OAuth clients, or service-to-service authentication backed by the Canonical Identity Platform — Hydra (OAuth 2.0 / OIDC issuer), Kratos (identity / sessions), and identity-platform-login-ui (the reference login UI). The bundle and the underlying charms live on Charmhub; the per-charm libraries that wire the relations live on Charmhub too (charmcraft fetch-libs route — none on PyPI yet).
The defaults here mirror the COS pattern Cantrip already prescribes for observability: tell the user to juju deploy canonical-identity-platform and integrate against the apps inside. The skill body covers when to deviate.
Reach for this skill when the request involves any of:
Do not use this skill for:
charms.vault_kv.* surface, separate from the identity platform.When the user says "add login" without further qualification, generate against the bundle:
juju deploy canonical-identity-platform
juju integrate my-app:oauth canonical-identity-platform.hydra:oauth
juju deploy canonical-identity-platform brings up Hydra + Kratos + identity-platform-login-ui + Traefik + a database backend as a single unit. login-ui is the user-facing door, Kratos owns user accounts and login flows, and Hydra is the OIDC issuer the relying-party (RP) charm talks to over the oauth relation.
This mirrors the COS-bundle pattern (juju deploy cos-lite → integrate). Pick another topology only when the user is explicit:
| Topology | Trigger | What changes |
|---|---|---|
| Bundle-based hybrid (default) | "add login", "OIDC", "SSO" | Suggest juju deploy canonical-identity-platform; relate oauth. |
| SaaS-style public Hydra | "use my existing Hydra at <URL>", production deployment | Same charm code; skip the bundle suggestion; point at the user's Hydra app. |
| Internal-only with mTLS | regulated / classified environments | Same charm code; add an mTLS profile to the relation handler; wire cert-manager. |
The customisation path is open in all three: the oauth relation works the same regardless of where Hydra lives.
Five interfaces matter for charm authors. Pick the single interface that matches the charm's role; do not declare more than one unless the charm genuinely plays multiple roles.
| Interface | Direction | When to use it | Charm library |
|---|---|---|---|
oauth | RP requires, Hydra provides | Web app registers as an OAuth/OIDC client; receives issuer URL + client ID + client secret per relation. The headline interface. | charms.hydra.v0.oauth |
oauth-cli | CLI requires, Hydra provides | CLI tool or daemon needs the OAuth 2.0 device-code flow. | charms.hydra.v0.oauth_cli |
oidc-info | Charm requires, Hydra provides | Charm wants Hydra's discovery URL (/.well-known/openid-configuration) and JWKs to validate ID tokens itself. | charms.hydra.v0.oidc_info |
hydra-token-introspect | Resource server requires, Hydra provides | Resource-server validates opaque access tokens via Hydra's introspection endpoint. | charms.hydra.v0.token_introspect |
kratos-external-idp | External-IdP charm provides, Kratos requires | Charm federates Google / GitHub / generic OIDC into Kratos. | charms.kratos.v0.kratos_external_idp |
Decision shortcut:
oauth, no questions asked.oauth-cli.oidc-info.hydra-token-introspect.kratos-external-idp.The five libraries above are not on PyPI today (per design/UPSTREAM_AUDIT.md, audit cutoff 2026-04-22). Fetch them via charmcraft. In charmcraft.yaml:
charm-libs:
- lib: hydra.oauth
version: "0"
# add only the ones the charm actually imports
Then run charmcraft fetch-libs once. The libraries land under lib/charms/hydra/v0/oauth.py (etc.) and import as from charms.hydra.v0.oauth import OAuthRequirer.
Revisit: When the libs land on PyPI under the charmlibs-* namespace, the LIB001 mapping (design/UPSTREAM_AUDIT.md) will be updated and this skill should switch to uv add charmlibs-hydra-oauth (or whatever the published name is). Until then, the fetch-libs instruction is correct.
Hydra writes the OAuth client secret into the oauth relation as a Juju secret, not as a plain databag value. The library handles the secret-fetch dance for you, but the charm needs to declare access:
provides-side secret declaration is needed. Hydra owns the secret; the relation databag carries the secret URI; the requirer charm fetches the secret on demand.oci-image runtime user has this on the Pebble side; on machine charms the Juju agent has it.paas-charm base injects the resolved client secret into the workload as OAUTH_CLIENT_SECRET (or the framework's prefixed equivalent). No charm Python is needed; the secret is materialised in the env.OAuthRequirer.get_provider_info() inside the relation-changed handler — the library transparently calls Secret.get_content() against the secret URI in the databag.Do not log the client secret, do not echo it into status messages, and do not place it in a config option — that defeats the secret-relation contract.
oauth)A Flask app needs OIDC login. The user accepts the bundle default.
charmcraft.yaml (in the charm/ subdirectory; the framework profile is already in place):
charm-libs:
- lib: hydra.oauth
version: "0"
requires:
oidc:
interface: oauth
optional: true # optionality must come from the fit handoff
limit: 1
The paas-charm base auto-injects relation env into the workload — no src/charm.py changes needed. On the workload side, the Flask app reads:
| Env var | Source | Purpose |
|---|---|---|
FLASK_OIDC_ISSUER_URL | oauth databag | Hydra's issuer URL |
FLASK_OIDC_CLIENT_ID | oauth databag | Per-relation client ID |
FLASK_OIDC_CLIENT_SECRET | resolved Juju secret | Per-relation client secret |
Framework prefix follows the paas-charm table in the twelve-factor skill (Flask: FLASK_, Django: DJANGO_, Express/FastAPI/Go/Spring Boot: APP_).
Deploy and integrate:
juju deploy canonical-identity-platform
juju deploy ./my-app_amd64.charm --resource oci-image=localhost:32000/my-app:latest
juju integrate my-app:oidc canonical-identity-platform.hydra:oauth
Verify with juju_status — once Hydra registers the client, the workload restarts with the new env and the login flow works end-to-end against the bundle's login-ui.
A custom ops-framework K8s charm that runs a non-PaaS web service and needs Kratos to own user accounts + login flows, with Hydra issuing tokens to the workload.
charmcraft.yaml:
charm-libs:
- lib: hydra.oauth
version: "0"
requires:
oauth:
interface: oauth
optional: false
limit: 1
src/charm.py:
import ops
import ops_tracing
from charms.hydra.v0.oauth import OAuthRequirer, ClientConfig
class MyAppCharm(ops.CharmBase):
def __init__(self, framework: ops.Framework) -> None:
super().__init__(framework)
self._tracing = ops_tracing.Tracing(self, "tracing")
self._oauth = OAuthRequirer(
self,
client_config=ClientConfig(
redirect_uri=f"https://{self.config['external-hostname']}/callback",
scope="openid profile email",
grant_types=["authorization_code", "refresh_token"],
),
relation_name="oauth",
)
framework.observe(self._oauth.on.oauth_info_changed, self._on_oauth_info_changed)
framework.observe(framework.on.config_changed, self._reconcile)
def _on_oauth_info_changed(self, event: ops.EventBase) -> None:
self._reconcile(event)
def _reconcile(self, _event: ops.EventBase) -> None:
info = self._oauth.get_provider_info()
if info is None:
self.unit.status = ops.WaitingStatus("waiting for hydra")
return
# ``info.client_secret`` is already resolved from the Juju secret.
self._push_pebble_layer(info)
self.unit.status = ops.ActiveStatus()
The login UI is owned by the bundle's identity-platform-login-ui — the workload does not render its own login page in this topology. After authentication, login-ui hands the user back to the workload with an authorisation code; the workload exchanges it for tokens via Hydra.
If the workload also needs to verify ID tokens locally (rather than calling introspection on every request), add the oidc-info relation alongside oauth and use OIDCInfoRequirer to fetch JWKs.
oauth-cliAn infrastructure charm (a backup daemon, a CLI-driven controller, a scheduled job runner) needs to authenticate to a downstream service that requires an OAuth 2.0 access token. No human is in the loop, so the device-code flow is wrong; what fits is oauth-cli issuing client-credentials tokens to the service.
charmcraft.yaml:
charm-libs:
- lib: hydra.oauth_cli
version: "0"
requires:
oauth-cli:
interface: oauth-cli
optional: false
limit: 1
src/charm.py (sketch):
from charms.hydra.v0.oauth_cli import OAuthCliRequirer
class BackupDaemonCharm(ops.CharmBase):
def __init__(self, framework: ops.Framework) -> None:
super().__init__(framework)
self._oauth_cli = OAuthCliRequirer(self, relation_name="oauth-cli")
framework.observe(self._oauth_cli.on.token_available, self._on_token_available)
def _on_token_available(self, event: ops.EventBase) -> None:
# Library has resolved the access token from the Juju secret on the
# databag. Pass it to the workload via Pebble env or a mounted file.
token = self._oauth_cli.get_access_token()
self._push_pebble_env({"DOWNSTREAM_API_TOKEN": token})
The downstream service it calls must, in turn, validate the token — typically by relating to the same Hydra over hydra-token-introspect (a resource-server pattern), or by trusting Hydra's JWKs over oidc-info.
The bundle is the default; the two alternatives are reachable via the prompt-driven escape hatches called out in design/IDENTITY_PLATFORM.md §4:
juju deploy canonical-identity-platform; relate against the user's Hydra app or its offer:
juju integrate my-app:oauth their-hydra-offer
The charm code is unchanged.In practice users will ask in prose — "use my existing Hydra at auth.example.com" — rather than reaching for a flag. Detect the prompt and skip the bundle suggestion accordingly.
oauth when the framework profile already provides it. The twelve-factor skill's OIDC section already documents adding an oidc relation against interface: oauth; do not also declare a separate oauth relation in the same charm. Pick one name; both work.paas-charm env injection — never read the databag directly.juju debug-log; secrets in logs are leaked secrets. Status messages are the same.oauth-cli for a human-driven web app. oauth-cli is the device-code / client-credentials path; for browser-based login flows the right interface is oauth.hydra directly and canonical-identity-platform in the same model — pick one. The bundle owns its own Hydra; a second copy will fight for the same relations.charms.hydra.v0.* and charms.kratos.v0.* are not on PyPI as of April 2026 — use charmcraft fetch-libs. Re-check design/UPSTREAM_AUDIT.md if a future change is suspected.canonical-identity-platform deploys its own backing database for Kratos; do not relate Kratos to the user's PostgreSQL unless explicitly asked.twelve-factor skill — the OIDC section there covers the simplest case (Flask/Django/FastAPI app + oauth relation + paas-charm env injection); load both skills together when working on a 12-factor charm with login.custom-charm skill — for non-PaaS apps, pair this skill with custom-charm for the broader workflow; the OAuth wiring in Example 2 above is the identity-specific delta.infrastructure-charm skill — for service-to-service auth in a Path C charm, pair with this skill's Example 3.observability skill — auth flows produce useful spans and metrics; instrument them with ops-tracing once the relation is wired.design/IDENTITY_PLATFORM.md — the research note that grounds the choices here, including the trade-off table for the three topologies and the revisit triggers.security-review for that.