Skip to content

GSSO — Implementation plan

Version0.1, 2026-10-05
Basisdocs/reports/00..05 (SPEC-GSSO-2026), gsso.jdl, test-scenarios.md
Decisions taken with the ownerDocs + working MVP · hybrid realms (gstack / cetatean / tenant-*) · develop against a local Keycloak first (production only with explicit approval) · DEV-PLAYBOOK stack (JHipster 9.1.0 microservice + gateway + separate Angular/GDS SPA)

Today “GSSO” is only a hand-configured Keycloak. That means:

  • one shared realm (interdictii) for every app;
  • only two global roles (ROLE_ADMIN and ROLE_USER);
  • four inconsistent integration patterns;
  • no approval or expiry of access;
  • no durable audit;
  • no configuration in git.

The goal is a real platform that can:

  • synchronise a governed model into Keycloak;
  • authorise per user × platform role;
  • give every gStack PaaS and SaaS one login, through one integration standard.
gsso-web (Angular/GDS) → gsso-gateway (BFF) → gsso (catalog · grants · reconciler · events · app API)
│ Admin REST (gsso-reconciler) ▲ Kafka gsso.kc-events.v1
▼ │
Keycloak 26.6.x + gsso extensions + GDS theme ─────┘
realms: gstack (staff SSO) · cetatean (MPass) · tenant-*
apps ── OIDC/JWKS ──► Keycloak apps ── api/v1/app ──► gsso gsso ──► GLog, GNotify

Each line becomes one GitLab issue (estimate in days) and one commit/MR. Phases match report 05.

#TaskOutputEst.
S0-1Review and accept GSSO-ADR-001..014; resolve Q-GSSO-1, 4, 5, 10, 11ADRs Acceptat1
S0-2git init, remote govtech/gstack/gsso, .gitignore, .env.example, MR templaterepo0.5
S0-3keycloak/Dockerfile (26.6.3 + kc.sh build), deploy/docker-compose.yml, docker-compose.test.yml (Keycloak, postgres-keycloak, postgres-gsso, Kafka KRaft, SMTP mock)local stack1.5
S0-4Realm baselines gstack.json, cetatean.json, tenant-template.json + gsso-bootstrap (keycloak-config-cli); gsso-reconciler SA in master with minimal rolesAC-0012
S0-5GDS login, account and e-mail theme gstack (RO/RU/EN, IBM Plex, violet accent)theme2
S0-6Extensions Maven module skeleton (SPI 26.6.x), CI buildJAR0.5
S0-7jhipster jdl gsso.jdl with 9.1.0 → gsso, gsso-gateway; pin versions; Liquibase jsonb changelog; append-only trigger changeloggenerated apps1.5
S0-8gsso-web skeleton: GDS shell (side bar Identity/Access/Governance, realm switcher, RO/RU/EN switch), BFF loginAC-0022
S0-9/gsast baseline, api_audit.py wiringevidence0.5
#StateEvidence / note
S0-1doneADRs Acceptat (owner, 2026-10-06), report 01 v0.2 EN/RO/RU
S0-2donelocal git, remote set, nothing pushed
S0-3donedeploy/docker-compose.yml: Keycloak 26.6.3, 2×PostgreSQL 17, Kafka 3.9.1 (KRaft), Mailpit, gsso, gateway, web; ports 76xx
S0-4donekeycloak/realms/generate.py → gstack, cetatean, master (gsso-reconciler), dev users; keycloak-config-cli no-delete; reconciler gets 403 on master users (NFR-SEC-004)
S0-5done (login + e-mail parent)theme gstack on keycloak.v2; GDS HTML e-mail layout and account theme in S2
S0-6done, ahead of plangsso-kafka listener implemented (not a skeleton): async, after-commit, secrets redacted; 6 unit tests; verified on Kafka (admin + user events, no secret in 54 events)
S0-7doneJHipster 9.1.0 → Spring Boot 4.0.6 / Java 21; jsonb + append-only trigger changelog (UPDATE/DELETE/TRUNCATE rejected, AC-018 DB part)
S0-8donegsso-web Angular 20.3 + GDS, shell per GSSO.dc.html, RO/RU/EN, BFF via nginx; 8 unit tests; deploy/smoke/login_flow.py 22/22 (AC-001, AC-002)
S0-9open/gsast baseline and api_audit.py not run yet

Open items found in S0:

  • GDS npm registry: the token in glog/frontend/.npmrc is rejected (401); GDS 0.0.4/0.0.5 tarballs are vendored in gsso-web/vendor/ until the owner provides an npm token (GITLAB_NPM_TOKEN).
  • Gateway and microservice share client gsso-console for now; a dedicated gsso-api audience client and the microservice’s own client-credentials client come in S1 with the reconciler.
  • JHipster ./mvnw verify (Testcontainers) not run yet in the container build; S1 adds it with the Docker socket mounted.
  • Re-running the bootstrap logs invalidPasswordHistoryMessage for the dev users (same password re-applied); harmless, dev-only.
#TaskReqsEst.
S1-1KeycloakAdminGateway (keycloak-admin-client 26.x, natural-key lookups, managed marker, RFC 7807 translation)FR-072, 0762
S1-2Outbox JobSincronizare + ReconcilerWorker (SKIP LOCKED, per-realm advisory lock, backoff, FAILED alert)FR-071..073, 0793
S1-3Handlers: Realm, Client, Role (+composites), Group/Bundle, PolicyFR-001, 002, 012, 0164
S1-4RealmDiffer + drift policy REPORT/ENFORCE/IGNORE, periodic and on-demand, dry-runFR-074, 075, 0803
S1-5Realm adoption (dry-run report + import with mapping file)FR-0072
S1-6Facade api/v1/admin/* with realm/platform scoping, Idempotency-Key, RFC 7807FR-006, 0973
S1-7Users: list/search (projection), create/invite, edit, enable/disable, credentials, sessions, unlock; nightly syncFR-023..0334
S1-8GSSO’s own append-only events (catalog/console)FR-083, 0841
S1-9UI screens: Realms, Applications (clients), Platforms, Roles, Users + drawer, SyncFR-004, 005, 026, 037, 077, 078, 109..1166
S1-10IT: TS-GSSO-02..08, 26..28; Cypress realm/userstests3
#StateEvidence / note
S1-1doneservice/keycloak/KeycloakAdminClient (thin RestClient wrapper, GSSO-ADR-016), 403 → token refresh + retry
S1-2doneoutbox SyncOutbox, ReconcilerWorker (SKIP LOCKED, one realm at a time, backoff, FAILED + event)
S1-3done for realm, client, role (+composites); bundles and auth policy move to S2 with grants and policiesservice/sync/handlers/*
S1-4doneRealmDiffer: REPORT / ENFORCE / IGNORE, NEGUVERNAT, auto-close of resolved findings
S1-5done (clients, roles, composites; user role mappings → grants in S2)RealmAdoptionService, dry run rolls back
S1-6done (Idempotency-Key store moves to S3 with the app zone)web/rest/admin/*, AdminScope (realm/platform scoping from claims gsso_realm, gsso_platforma)
S1-7doneservice/users/*: search, create + invitation, edit, enable/disable (ends sessions), required actions, credentials, sessions, unlock; IDNP AES-GCM + masked; GSSO-ADR-015 (proposed)
S1-8doneEventStore: append-only, per-realm SHA-256 chain, verification endpoint
S1-9doneconsole: Realms (+create, adopt with dry run, reconcile), Platforms (+clients, roles, composites), Applications, Roles, Users (+drawer, create), Sync (findings, enforce, accept), Events (+chain check); RO/RU/EN 186 keys
S1-10donebackend 160 unit + 630 IT green (ReconcilerIT 8, UserAdminIT 6, CatalogAdminResourceIT 6 against Keycloak 26.6.3 / isolated DB); web 9 unit; deploy/smoke/login_flow.py 23/23, deploy/smoke/s1_catalog.py 20/20

Found and fixed by the S1 tests (now regression-tested):

  • the reconciler token lost rights on realms it had just created (403) and grew with every realm until HTTP 431 → lightweight access token (GSSO-ADR-016);
  • tenant realms had no ROLE_USER by default and failed to import once the template declared its default role → template declares Keycloak’s built-in roles and the default-role composites;
  • the template’s own ROLE_USER was marked catalog and would have been deleted by ENFORCE → ownership values baseline / catalog;
  • drift diffs and event payloads were serialised as Jackson-2 node internals by the Jackson-3 web layer → plain maps;
  • generated CRUD was open to any authenticated user → GSSO_ADMIN only.
#TaskReqsEst.
S2-1GrantLifecycleService + ApprovalPolicy (four-eyes, self-approval ban), direct grant, bundlesFR-038..043, 0463
S2-2Expiry scheduler + GNotify warningsFR-044, 0451.5
S2-3Ungoverned mapping detectionFR-0491
S2-4Org units + mappers org_unit, org_unit_path, roles_scoped (extension)FR-053..0552.5
S2-5Event listener gsso-kafka (extension), consumer, hash chain, back-fill, GLog relayFR-081..0884
S2-6Revocation publisher, gsso.user-changed.v1, gsso.grant.v1FR-0891
S2-7Auth policies: methods, browser flow view, conditional MFA authenticator, password/brute-force/session settings, LDAP config, MPass mock broker in cetatean, brandingFR-057..066, 0695
S2-8Dashboard aggregates + UI (KPIs, logins/24 h, events, auth mix), Grants inbox, Events, Authentication screensFR-047, 088, 091..0955
S2-9IT and E2E: TS-GSSO-09..19, 21..25, 29..31tests4
#StateEvidence / note
S2-1done (bundles open)service/grants/*: lifecycle state machine, four-eyes ApprovalPolicy, request / direct / approve / reject / revoke; GrantIT 8, ApprovalPolicyTest 7
S2-2expiry done; warnings openhourly GrantExpiryScheduler; GNotify warnings (−14 d / −1 d) not yet sent
S2-3doneungoverned mappings: REPORT finding / ENFORCE removal; adoption imports mappings as ACTIVA grants
S2-4doneorg-unit tree, org_unit_path, roles_scoped (JSON claim); OrgUnitIT reads real tokens
S2-5done except GLog relay and back-fillgsso-kafka → KcEventIngestion (idempotent, own event time, projection last login / failure); KcEventIngestionIT 6
S2-6donegsso.grant.v1, gsso.access-revoked.v1 (after commit, off the request thread); gsso.user-changed.v1 defined, not yet emitted
S2-7MFA + policy done; MPass mock and LDAP opengsso-browser flow with conditional OTP (sensitive roles contain GSSO_MFA_REQUIRED); policy API with secure floors, read-only for baseline / adopted realms; MfaFlowIT 2, AuthPolicyIT 2
S2-8donedashboard (KPIs, logins 24 h, auth mix, recent events), Grants screen, Authentication screen, unit management
S2-9done for the delivered itemsdeploy/smoke/s2_grants.py 20/20, plus S0 23/23 and S1 20/20 regression

Open items found in S2:

  • console MFA for the GSSO_* roles themselves (GSSO-NFR-SEC-008): to be switched on in the production baseline at the S4 takeover (it would put every dev login and smoke test behind TOTP);
  • GNotify (expiry warnings, alerts) and GLog (audit forwarding): need the target services and their client credentials;
  • MPass mock broker for cetatean (dev SAML IdP), role bundles (GSSO-FR-017), Keycloak events back-fill (GSSO-FR-087).
#TaskReqsEst.
S3-1gsso-spring-boot-starter (resource server, audience, roles mapping + aliases, principal, client-credentials and exchange clients, revocation deny list, health, Testcontainers helper)FR-104, 1075
S3-2@gstack/gsso-angular (PKCE/BFF, guard, directive, refresh, logout, locale)FR-1053
S3-3api/v1/app/* (self-registration, lookups, roles, sessions, grant requests, role→users)FR-098..103, 0513
S3-4Token-exchange permissions reconciliationFR-0192
S3-5Sample app + onboarding runbook (report 03 §8)FR-1061
S3-6Pilot gregistry (local branch): starter, realm gstackAC-010, 0202
S3-7Pilot glog (local branch): starter, scopes, tenant claim; GSSO as ingest client—2
S3-8Tests TS-GSSO-20, 32..35; /gtestgen authorization matrixtests2
#StateEvidence / note
S3-1done (0.1.0)sdk/gsso-spring-boot-starter: decoder (issuer, audience, revocation), roles → authorities + aliases, GssoPrincipal (hasRoleInUnit), GssoTokenClient (client credentials, exchange), deny list from gsso.access-revoked.v1, readiness; Spring Boot 3.2+ / Java 17; GssoPrincipalTest 5, GssoKeycloakIT 4 (stock Keycloak 26.6.3)
S3-2open@gstack/gsso-angular
S3-3openapi/v1/app/*
S3-4doneexchange audiences per client → gsso-exchange-<target> audience mappers + standard.token.exchange.enabled; ReconcilerIT (mappers added/removed, no drift); console editor on Clients
S3-5opensample app + runbook
S3-6done (local branch gsso-starter-pilot in gregistry, not pushed)starter replaces the hand-written decoder / AudienceValidator / converter; GssoStarterIT; gregistry suite 298/302, the 4 errors (RegistryAuditResourceIT, append-only trigger vs generated CRUD tests) also fail on main; live: deploy/smoke/s3_pilot.py 24/24 first run, 21/21 re-runs
S3-7openglog pilot
S3-8partlyTS-GSSO-32 (smoke), TS-GSSO-35 (GssoKeycloakIT); TS-GSSO-21 (cross-app SSO in a browser), 33, 34 open

Found and fixed during S3:

  • revocation gap: a re-login in the seconds before the reconciler removed the mapping got the role back; the mapping is now removed synchronously at revocation, before sessions are ended (job kept as retry); GrantIT asserts it without draining the worker;
  • starter: the deny list depended on an optional Kafka dependency and silently did nothing without it; kafka-clients is now a regular dependency;
  • full backend verify had not been run since S2: generated GssoKafkaResourceIT was broken by the S2 Kafka bindings (demo producer disabled, platform events retrying an absent broker); fixed with gsso.events.publish (off only in tests) and the test binder as default; modernizer findings fixed. Now 170 unit + 651 IT, no skips.

S4 — Migration (each step needs explicit owner approval)

Section titled “S4 — Migration (each step needs explicit owner approval)”
  1. Production takeover (report 04 §11.1):
    • fix TLS renewal;
    • back up and export;
    • swap in the GSSO image on 26.6.3;
    • enable the listener;
    • adopt interdictii as IGNORE;
    • apply the gstack and cetatean baselines.
  2. Rotate all secrets that were stored in plain-text notes.
  3. Migrate apps one by one, following report 03 §2.1:
    • gregistry and glog (from pilot);
    • interdictii, gdocs, gnotify (pattern A);
    • drumuri (C + MPass);
    • gstorage;
    • cancelarie;
    • platform and gportal.
  4. Retire ROLE_ADMIN and the obsolete realms, and create the tenant-* realms for the whitelabel sites.

Access reviews, SoD rules, dormant accounts, QR login authenticator for gsso_mob, magic link, Organizations, gsso-cli/Terraform, HA Helm.

  • S0–S3: about 90 person-days, roughly 14–18 person-weeks with 2–2.5 people.
  • The MVP (S0 + S1 + the S2 dashboard, events and grants) is the first demo target.
  • CAP-GSSO-01 (CRM F0 blocker) is delivered at the end of S1.

The detailed scenarios are in test-scenarios.md. The minimum demo proof:

  1. docker compose up: Keycloak comes up with gstack/cetatean, the GDS theme and the extensions.
  2. Console login through BFF + MFA.
  3. Register platform crm → clients and roles appear in Keycloak.
  4. Request and approve CRM_ADMIN (four-eyes) → the role is in the token → revoke it → the sample app denies within 60 s.
  5. Make a manual change in the Keycloak console → drift is shown → enforce it.
  6. Login events appear on the dashboard and in GLog.
  7. SSO across the console and the gregistry pilot.
  • The ADR approval gate.
  • Q-GSSO-1: migrate realm interdictii → gstack, or rename it.
  • Q-GSSO-10: hosting target for staging.
  • Shared Kafka availability (otherwise use a local single broker).
  • gsso.dc.html: copy from gstyle and add the Platforms, Grants, Sync and Events screens, keeping the existing 6 screens as designed. Also update the “Keycloak 24” label to 26.6.