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-adminas 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:
- The Console’s own
Operation/OperationEventtables (identity-admin, built in Task 4 of the Identity Console P0 plan) — records Console-initiated management actions. - 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 anOperation/OperationEventrow 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.
Consequences
Section titled “Consequences”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.