Add Canonical Identity Platform login

Reach for the bundled identity-platform skill when a charm needs OIDC tokens, OAuth clients, or federated login backed by Hydra, Kratos, and login-ui.

When to use this skill

Cantrip ships a bundled identity-platform skill that teaches the agent how to wire charms to the Canonical Identity Platform — Hydra (the OAuth 2.0 / OIDC issuer), Kratos (identity and sessions), and identity-platform-login-ui (the reference login UI). The skill loads automatically when the agent recognises an identity request; you do not need to install or configure anything.

Reach for it when the charm needs any of:

Do not reach for it for custom IAM (LDAP, SAML, ad-hoc OAuth), in-workload auth library wiring (PKCE, JWT verification — that is application code), or Vault-backed secrets (use the charms.vault_kv.* interface instead).

Trigger it from a prompt

The skill activates on prose, not a slash command. Describe what the charm needs in natural language and the agent will pick the skill up on its next planning step:

cantrip> add OIDC login to this Flask charm against the
Canonical Identity Platform bundle

You can also load it explicitly when steering a stuck plan:

cantrip> /skill identity-platform

/skill lists the bundled skills if you want to confirm the name. See Add a custom skill for the discovery order and how to override a bundled skill from your own directory.

Default topology — bundle-based hybrid

When the request is "add login" without further qualification, the skill generates against the published bundle:

$ juju deploy canonical-identity-platform
$ juju integrate my-app:oauth canonical-identity-platform.hydra:oauth

canonical-identity-platform brings up Hydra + Kratos + identity-platform-login-ui + Traefik + a database backend as a single unit. The relying-party charm only talks to Hydra over the oauth relation; login-ui is the user-facing door; Kratos owns user accounts and login flows. This mirrors the COS-bundle pattern (juju deploy cos-lite → integrate).

The skill keeps two other topologies in reserve when the user is explicit:

Topology Trigger phrase What changes
Bundle-based hybrid (default) "add login", "OIDC", "SSO" Suggests juju deploy canonical-identity-platform and integrates oauth.
SaaS-style public Hydra "use my existing Hydra at <URL>", production reuse Same charm code; skips the bundle suggestion; points at the user's Hydra app.
Internal-only with mTLS regulated or classified environments Same charm code; adds an mTLS profile to the relation handler; wires cert-manager.

The charm code is portable across all three — only the deployment shape changes.

Pick the right relation

The skill chooses one of five interfaces based on the charm's role. Charms generally play exactly one role; declare a second only when the charm genuinely needs both.

Interface Direction When 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 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 Provider, Kratos requires Charm federates Google / GitHub / generic OIDC into Kratos. charms.kratos.v0.kratos_external_idp

A quick decision shortcut:

Charm libraries — fetch-libs route

These five libraries are not yet on PyPI. Cantrip declares them in charmcraft.yaml and runs charmcraft fetch-libs at scaffold time:

charm-libs:
  - lib: hydra.oauth
    version: "0"
  # add only the interfaces the charm actually imports

Once fetched, the libraries land under lib/charms/hydra/v0/oauth.py (etc.) and import as from charms.hydra.v0.oauth import OAuthRequirer. When the libs land on PyPI under the charmlibs-* namespace the skill will switch to uv add charmlibs-hydra-oauth; the agent tracks the upstream audit so this is a one-line change when the time comes.

For 12-factor charms (Path A — Flask, Django, FastAPI, Express, Spring Boot, Go), the paas-charm base injects the resolved relation values into the workload as environment variables — no charm Python is needed:

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

The framework prefix follows the paas-charm table — FLASK_, DJANGO_, or APP_ for the rest. Hydra writes the client secret as a Juju secret (not a plain databag value); the library handles the secret-fetch dance, so the charm only needs to declare the relation.

For custom (Path B) charms, call OAuthRequirer.get_provider_info() inside the relation-changed handler — the library transparently resolves the secret URI in the databag.

Verify the wiring

Once the agent finishes, drive the usual verification loop. The most useful checks are:

cantrip> @juju status
cantrip> @juju show-relation oauth

@juju show-relation is the fastest way to confirm Hydra has written the issuer URL, client ID, and secret URI into the relation databag — see Use @-mention context for the full provider catalogue. If the workload still cannot reach Hydra, check juju_debug_log for relation-changed errors and confirm the charm runs with permission to read Juju secrets.

Never log the client secret, never echo it into status messages, and never place it in a config option — that defeats the secret-relation contract. Cantrip's generator follows this rule; audit before merging if you hand-edit the relation handler.