コンテンツにスキップ

ADR 0002: MyPage is a separate frontend from the admin Console; audit schema unifies before the write path does

  • Status: Accepted
  • Date: 2026-10-06

Decision 1 — MyPage ships as its own frontend application

Section titled “Decision 1 — MyPage ships as its own frontend application”
管理コンソール MyPage
利用者 数百名の管理者 協力会社を含む数万人
ネットワーク IP 制限・社内網に限定可能 公開インターネットに必須で露出
Keycloak client 管理者ロール、より強い MFA を要求できる 一般ユーザースコープ
リリース頻度 管理機能に追従 ユーザー体験改善に追従

Putting both in one app would expose the admin console’s code and routes to the public internet, which is a harder security review to pass for no benefit.

Structure:

  • Frontend: two apps in the monorepo, sharing a UI component package and an API client package, deployed to separate origins/domains.
  • Backend: MyPage also needs a BFF. Short term, it can live inside identity-admin as a separate route group (/admin/* vs /me/*), with its own Keycloak client, its own cookie name, and its own cookie path — not sharing a session cookie with the admin routes. Split into a fully separate service once scale justifies the operational cost; the application-service layer (ADR 0001) is already decoupled enough that this split is cheap when it happens.

What this requires of console/ right now: do not assume “authenticated session” implies “is an admin.” Every authorization decision is role/scope-based, not presence-of-session-based. No routing changes are needed in console/ today — this is a code-discipline requirement, not a structural one, for the current P0 scope.

Employee-specific restriction (carried over from an earlier note, easy to miss): employees’ passwords and MFA are managed by Entra ID, not this platform. MyPage, for employee accounts, only exposes profile viewing and session management — not password change or MFA enrollment. Those two actions are only available to non-employee (external/partner-company) accounts. This constraint belongs in MyPage’s own future spec, but is recorded here so it isn’t lost before that spec exists.

Decision 2 — Audit: unify the schema now, not the write path

Section titled “Decision 2 — Audit: unify the schema now, not the write path”

Two audit mechanisms already exist independently and are not being merged into one write path right now:

  1. The Console’s own Operation/OperationEvent tables (identity-admin, built in Task 4 of the Identity Console P0 plan) — records Console-initiated management actions.
  2. The Keycloak Event Listener SPI (keycloak-audit-log, PR #19) — records Keycloak-native login/admin events as structured log lines.

Rather than collapsing these into one writer (expensive, and premature before Epic #6’s full audit design lands), two cheaper steps now:

  • Define a common audit event schema — actor, action, target, result, source, timestamp, correlation_id — and have both mechanisms eventually emit events in this shape, converging at one sink. That sink is also the natural data source for the COSMO event/log pipeline from ADR 0001.
  • Correlate the two with a correlation_id. A single Console operation (e.g. a session revoke) produces both an Operation/OperationEvent row and a resulting Keycloak admin event. The Console’s call to Keycloak’s Admin API should carry its request id so the two records can be joined and de-duplicated later, once they land in the same sink.

This means Epic #6’s eventual unification work happens at the sink/schema level, not by rewriting either existing writer.

  • console/’s P0 code must not hardcode “has a session ⇒ admin” anywhere — reviewed against this ADR in later task reviews.
  • MyPage’s own implementation plan (P1, not yet started) starts from “separate app, separate BFF route group or service” as a given, not an open question.
  • The audit correlation_id requirement is a small addition to any future write-action task that calls Keycloak after recording an Operation — worth checking whether Task 5/6 (session revoke, user disable) already thread a request id through to the Keycloak call, or whether this needs a small follow-up.