CommBank ID + income

Design · docs/adr-notes.md

ADR (CBA) notes

CBA is the Accredited Data Recipient: participants/adr-cba. It is now an authentic Ping CDR Kit "data-in" participant. The kit's PingFederate, PingAccess, PingDirectory and PingDataSync components, with the kit's data-in modules, do every piece of CDR protocol work. CBA adds:

History

Spike objection How it's solved
Needs PingDirectory + PingAccess Both run with the kit profiles and data-in jars (PD 11.0.0.4, PA 8.3.2)
mTLS to the Register and DHs The kit's own pattern. The configurator gets a network certificate from the Register CA and installs it as PA's mTLS site authenticator. PF, PD and PDS reach each DH through PA, via the adapter's "Local Base URL" per DH
Hybrid flow The kit does it (code id_token, encrypted id_token, nonce, acr check). The kit DHs support it
No event hook A PingDataSync sync pipe on the kit's token store, with a Server SDK sync destination that publishes to Redis

Components (participants/adr-cba/)

Path What
server-profiles/pingfederate Kit PF profile (bulk config + pf-cdr-au-data-in-modules-1.5.11.jar) on PF 13.0.3 (the kit's 13.0 line; 13.1 is Jakarta and breaks the kit's javax /arrangements/revoke servlet). CBA changes listed below
server-profiles/pingaccess Kit PA profile (pa-cdr-au-data-in-modules-1.2.6.jar). The applications are CBA's: one data-in API app and one mTLS egress app per DH
server-profiles/pingdirectory Kit PD profile: data-in SCIM schemas (adr-token, adr-token-refresh, adr-token-revoke, adr-dataholder(-read), adr-software), pd-cdr-au-data-in-modules-1.2.6.jar (TokenMgt refresh plugin), the kit LDIF
server-profiles/pingdatasync Kit PDS profile: ADHRevokeConsentPipe (kit RevokeDataHolderConsentSyncDest) + the CBA CBAArrangementEventsPipe
profile-overlay/ CBA files laid over the kit profiles (client hook, branded error page, event pipe, event-bridge jar, captured PA config)
scripts/derive-profiles.py Rebuilds server-profiles/ from vendor/pingidentity-cdr-sandbox/server_profiles + the overlay. Every CBA change to the kit is in this script. The profiles are committed output, each usable on its own as a SERVER_PROFILE
scripts/gen-keys.sh Software-product key (PKCS12 → the PF signing / OIDC static keys = /pf/JWKS), PF TLS key, system keys, secrets → keys/ (gitignored)
scripts/capture-config.py Captures the configured PA (after configure) back into the profile, kit style: ${_data_…} placeholders, values in keys/pa.env
event-bridge/ Java source + build.sh for com.cba.cdr.adr.pds.ArrangementEventsSyncDestination (Server SDK; RESP client, no extra jars)
web/ @cba/adr-cba-web (Node 20). CX consent screens, the customer hand-off, OAuth callback, dashboard, /dev/token. No CDR protocol code
kit.env Non-secret config (the sandbox's cdr.env for CBA): hostnames, Register, the DH list, Redis
docker-compose.yml pingdirectory, pingfederate, pingaccess, pingdatasync, web, plus configure (profile configure: the kit's tamatping/datain-configure-pf:20231123)

Changes from the kit (all in derive-profiles.py)

How a consent runs

app ─302 (offers)─► web /consent/start?session  (HS256 handle → CBA customer)
  CX screens: purpose, clusters, uses, duration, deletion, policy → bank chooser
  (DHs = kit PD adr-dataholder-read: the DHs the kit registered with and the Register says are active)
web ─303─► kit PF /as/authorization.oauth2  client cba-adr-web, PKCE, data-holder=<issuer>,
           use_consents, deletion_election, purpose, listing_id, consent_type, override_sharing_duration
kit PF policy: cbaCustomerRef ─POST─► web /pf/authn ─dropoff {subject}─► PF ─resume(REF)
             → DataHolderSelector (kit): PAR + PS256 request object (via PA egress, mTLS) → DH authorize
DH (kit): login + OTP + account selection ─hybrid #code,id_token─► kit PF /ext/cdr/data-in/callback
kit: code exchange (private_key_jwt, PKCE), id_token decrypt + nonce/acr, refresh-token introspection,
     tokens → PD adr-token (+ metadata) → APC → adrATM code → web /consent/callback
web: code → adrATM claims (cdr_arrangement_id, data_holder) → "Bank connected" → cbaapp://consent-complete?status=success&…
PDS (CBA pipe): new tokenMgtInstance → ArrangementEstablished on cdr.arrangements

Cancel on CBA screens → status=cancelled. Refusal at the DH (access_denied) → the kit renders the CBA PF error page → /consent/abort → status=denied. Any kit or DH error → status=error.

Event bridge (requirement 2): PingDataSync

Why not PD notifications or the changelog read from Node? PDS is the kit's own mechanism for reacting to token-store changes (RevokeDataHolderConsentSyncDest is exactly this pattern), and it gives durable, resumable delivery from the changelog.

Pod data access (requirement 3): through the kit's PingAccess

The pod never holds a DH token.

Revocation (requirement 4)

CBA customer tokens (requirement 5)

POST {web}/dev/token {sub} → RS256, JWKS {web}/dev/jwks.json, unchanged from the contract. The CBA OIDC OP for the iOS app and MATTR is still the separate participants/adr-cba/pingfederate (Terraform-built, :9034). Not yet folded into the kit PF. To fold it, add its PCV / OTP form adapter / JWT ATM / clients to derive-profiles.py and the client hook.

Networking (CONTRACTS v1.7 §50)

adr-cba shares no Docker network with other participants. The kit containers sit on a private adr-cba-kit network under their kit hostnames (pingfederate, pingaccess, pingdirectory, pingdatasync). Each kit container maps the Register (cdrregister) and every DH name (sso|api.<brand>.localhost) to host-gateway (extra_hosts), so cross-participant calls go through the published host ports, as they would on Railway. The DHs map sso.commbank.localhost → host-gateway:9444.

Test results (7 Oct, against the Westpac kit DH + Register test harness)

Run

participants/adr-cba/scripts/gen-keys.sh                 # keys/ (gitignored); Register must be up so it issues the PF TLS cert
participants/adr-cba/event-bridge/build.sh               # PDS extension jar -> profile-overlay
python3 participants/adr-cba/scripts/derive-profiles.py  # kit profiles + CBA changes -> server-profiles/
# Register must publish CBA's SSA with the kit PF keys:
ADR_JWKS_URI=https://sso.commbank.localhost:9444/pf/JWKS ADR_REVOCATION_JWKS_URI=https://sso.commbank.localhost:9444/pf/JWKS \
  docker compose -p register -f participants/register/docker-compose.yml up -d
docker compose -p adr -f participants/adr-cba/docker-compose.yml up -d --build          # ~4 min (PD -> PF -> PA, PDS)
docker compose -p adr -f participants/adr-cba/docker-compose.yml --profile configure run --rm configure   # kit: certs + DCR (re-run after a DH or PA restart)
python3 participants/adr-cba/scripts/capture-config.py   # optional: freeze the configured PA into the profile
participants/adr-cba/scripts/smoke.sh                    # SMOKE_STAGE=health for kit checks only

Consoles (local only): PF admin https://localhost:9934/pingfederate/app, PA https://localhost:7311, PD https://localhost:7312/console. All use Administrator / «redacted», the kit default. Memory is about 4.5 GB for PD + PF + PA + PDS + web.

Railway (deployed, 8 Oct)

See deploy/railway/README.md and participants/adr-cba/railway/. The kit PF is behind its Railway HTTPS domain. The kit PA data proxy for the pod is behind another. The PA egress reaches each DH's PingAccess TCP proxy with mTLS (the kit site authenticator). PingDataSync publishes to the project Redis over the private network. PD has a volume. Kit hostnames resolve through /etc/hosts aliases written by railway/entrypoint.sh. Verified in a real browser (Playwright Chromium) for Westpac and ANZ, then event → pod income_ready → withdrawal.

Deviations and gaps