~/bpabatao
← back to index
~/case/identity

Identity & Access

Closing the paths that let someone reach an account that was not theirs - at the front door, in the browser, and in the console.

Role
Primary author
Period
2026
Ownership
Primary author
Stack
AWS Cognito · Oracle CCS · Terraform · IAM · Fastify · TypeScript
no direct accessbrowserholds no credentialscore apipermissions declared oncecognitoreal client ip forwardedoracle ccsidentity verificationregistration: lockout on repeat attempts, zip disambiguation on the last-four space

Problem

Three separate ways existed to end up somewhere you should not be. Identity verification at registration matched on data thin enough to hit the wrong person. The browser held credentials to talk to the identity provider directly. And engineer and pipeline access to AWS had grown by accretion rather than design.

Constraints

Real utility customers register through this flow, so a fix that locks out legitimate people is not a fix. The portals are multi-tenant and the same identity plumbing serves all of them, which means a change lands everywhere at once.

Architecture

At the front door: an identity-verification gap that enabled account takeover was closed - wrong-person matches on roughly 2.4% of accounts fixed, attempt lockout added, and ZIP-based disambiguation put in front of the brute-forceable last-four space.

In the browser: direct client-side access to the identity provider was removed, so email and password changes go through the API instead of from the page. A build-time content-security policy injects environment-aware provider URLs, and the real client IP is forwarded to the provider so its adaptive threat protection sees the actual source rather than the load balancer - with an alarm when that forwarding falls back.

In the console: route permissions moved from scattered checks to one declarative config behind a global guard, with every destructive operation permission-gated. IAM roles are recorded in Terraform as the source of truth - least-privilege developer policy, per-client task roles, deploy roles named and documented rather than inherited.

Outcome

← all projects