Skip to content

feat(iam): migrate MCM from Keycloak to Ory (Hydra + Kratos + Keto + Oathkeeper)#202

Draft
kirgene wants to merge 3 commits into
mainfrom
feat/mcm-ory-migration
Draft

feat(iam): migrate MCM from Keycloak to Ory (Hydra + Kratos + Keto + Oathkeeper)#202
kirgene wants to merge 3 commits into
mainfrom
feat/mcm-ory-migration

Conversation

@kirgene

@kirgene kirgene commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator

Story: mojaloop/project#4478 · DA: mojaloop/project#4472

Draft / WIP. Opening early for visibility on the architecture and to get feedback while the test suites and a few follow-ups are finished.

What this does

Migrates MCM's IAM from Keycloak to the Ory stack, following the Hub/Dfsp Keto OPL in #193 and the Oathkeeper rules in mojaloop/helm#804.

The guiding rule: MCM does no runtime authorization. It provisions IAM resources when a DFSP is onboarded and does nothing else with Ory. Every request is authenticated and authorized by the Oathkeeper gateway against Keto before it reaches the API, and MCM trusts the identity headers the gateway injects.

Provisioning (onboarding only)

DfspIamService is the one module that knows Hydra, Kratos and Keto exist. The business code (PkiService, CredentialsService) calls it and never touches an Ory SDK directly.

When a DFSP is created it provisions a Hydra OAuth2 client for PM4ML machine access (client_credentials, with client_id == dfspId), a Kratos identity for the admin (invited via a recovery magic-link), and the Keto tuples Dfsp:<id>#parent@Hub:<hub> plus Dfsp:<id>#members@<userId> for both the human identity and the machine client. A failure anywhere rolls the rest back.

Authorization (gateway, not MCM)

permissions/oathkeeper-rules.yml holds the per-route Keto checks, mirroring helm#804. MCM owns the route-to-permission contract; deployments just render the placeholders (MCM_FQDN, KETO_READ_URL, KETO_HUB_OBJECT). Keto loads the OPL from permissions/mcm.keto.ts, and iam-bootstrap seeds Hub:<hub>#admins.

All the in-app role filtering is gone. List endpoints scope their rows from the X-Roles header the gateway supplies: a hub-admin sees everything, a dfsp:{id} member sees their own DFSPs, and a user can hold several (system integrators). The hub object id and role names are configurable rather than hardcoded.

Invitation and accounts

Invitations go out as a Kratos recovery magic-link with branded HTML and plaintext courier templates ("Set up your Mojaloop Hub account"). Completion is password-only, and self-registration is disabled in Kratos.

Local stack

docker-compose gains the Traefik + Oathkeeper gateway, Ory config rendering, and the OPL mount. Keycloak, the legacy PKI CSR fixtures, vault.hcl, and the kubernetes/ manifests are removed, since deployment uses the helm charts.

Verified end to end (compose full)

DFSP onboarding provisions Hydra, Kratos and Keto and rolls back on failure. An unauthenticated request gets 401; a DFSP member gets 403 on hub and other-DFSP routes; a hub-admin is allowed. All of that is decided by the gateway against Keto, not by MCM. PM4ML client_credentials tokens issue correctly, and the branded invitation email leads through magic-link, password set, and into a role-correct portal.

kirgene added 2 commits June 2, 2026 13:58
Replace Keycloak with the Ory stack for MCM IAM:
- HydraService: per-DFSP OAuth2 clients (client_credentials) for PM4ML
- KratosService: per-admin identities + recovery-flow invitation, Keto role
  assignment incl. hub-admin subject_set transitivity and identity cleanup
- CredentialsService + PkiService onboarding routed through Hydra/Kratos
- AuthMiddleware trusts Oathkeeper headers (X-User/X-Email/X-DFSP-ID/X-Roles);
  session/token validation and /auth endpoints removed
- KetoClient read-side queries for membership/cleanup
- docker-compose: Hydra + Kratos + Keto + Mailpit + Kratos self-service UI;
  Vault switched to file-backend server mode with init/unseal
- mcm-test-setup CLI driven by Kratos + Hydra
- Integration tests for Hydra/Kratos/DFSP-IAM; Keycloak tests removed
# Conflicts:
#	.env-example
#	.nvmrc
#	docker-compose.yaml
#	docker/keycloak/dfsps-realm.json
#	docker/vault/docker-entrypoint.sh
#	package-lock.json
#	package.json
#	src/appLoader.js
#	test/.env-func
#	test/int/test-env-setup.js
…ation flow

Realign the Ory migration to the Hub/Dfsp Keto OPL so MCM does no runtime
authorization: it only provisions IAM at DFSP onboarding, and the Oathkeeper
gateway enforces every request against Keto.

- Rewrite KetoClient to the Hub/Dfsp/User relation-tuple model
- Add DfspIamService: the single facade that knows Hydra/Kratos/Keto exist;
  business code (PkiService, CredentialsService) calls it and nothing else
- Drop all in-app authorization; list endpoints scope rows from the
  gateway-supplied X-Roles header (hub-admin sees all, dfsp:{id} members
  see their own; multi-DFSP supported)
- permissions/oathkeeper-rules.yml: per-route Keto checks (MCM owns the
  route<->permission contract; deployments render the placeholders)
- Keto loads the OPL; iam-bootstrap seeds Hub:mojaloop#admins
- Kratos invitation via recovery magic-link + branded courier templates;
  password-only completion; self-registration disabled
- Configurable hub object / role names (no hardcoded mojaloop/hub-admin)
- docker-compose: Oathkeeper gateway, OPL mount, Ory config rendering;
  drop Keycloak/legacy PKI/vault/kubernetes assets
@kirgene kirgene force-pushed the feat/mcm-ory-migration branch from 02beecc to ed92641 Compare June 18, 2026 14:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migrate MCM API from Keycloak to Ory (Hydra + Kratos)

1 participant