Flow: Login & Authentication
How users authenticate and how subsequent API requests are authorized.
Request Path: Login
graph TD
A["Browser (Console React app)"]
subgraph gw["API Gateway (Console API, anonymous route)"]
subgraph ecs["Monolith ECS (BFF Console)"]
B["Cognito: initiate_auth (USER_PASSWORD_AUTH)"]
C["JWT access_token + refresh_token + id_token"]
end
end
D["Browser stores token in cookie"]
A --> B
B --> C
C -->|returns access_token| D
Request Path: Authenticated API Call
graph TD
A["Browser"]
subgraph gw["API Gateway (Console API, authenticated route)"]
subgraph auth["Custom Authorizer Lambda (ControllerAuthLambda-{env})"]
B["Verify JWT signature against Cognito JWKS"]
C["Extract system_account_id from claims/membership"]
D["Allow/Deny policy"]
end
E["VPC Link → NLB → Monolith ECS"]
end
F["Response"]
A --> B
B --> C
C --> D
D --> E
E --> F
Step-by-Step: Login
1. Browser → Signin Endpoint
Where: API Gateway {env}-ConsoleApi, route POST /api/account/signin (anonymous — no auth required)
Input: email, password (8+ chars, 1 upper, 1 lower, 1 number, 1 special).
2. Cognito Authentication
Where: components/identity_service/ → backends/identity_aws.py
cognito_idp.initiate_auth(
AuthFlow="USER_PASSWORD_AUTH",
AuthParameters={"USERNAME": email, "PASSWORD": password, "SECRET_HASH": hmac_hash}
)
Returns AccessToken, RefreshToken, IdToken.
Error patterns:
| Cognito Exception | Public Message |
|---|---|
UserNotFoundException |
"Incorrect email or password" |
NotAuthorizedException |
"Incorrect email or password" |
UserNotConfirmedException |
"User has not confirmed their email" |
Inspect: Check Cognito user status — see Cognito.
3. Token Storage
Frontend stores access token in a cookie (config.tokenStorageName). API Gateway maps the token to the x-api-key header in the Lambda event (regardless of how the frontend sends it).
Step-by-Step: API Authorization
4. Custom Authorizer Lambda
Where: ControllerAuthLambda-{env} (provisioned concurrency: 5)
File: components/auth_lambda/authentication/lambda_handler.py
Both JWT tokens and API keys arrive via the same header: x-api-key (API Gateway maps the Authorization: Bearer header to x-api-key in the Lambda event).
Path A — JWT Bearer Token (value starts with "Bearer "):
- Strip
"Bearer "prefix from thex-api-keyheader value - Fetch JWKS from
https://cognito-idp.{region}.amazonaws.com/{pool_id}/.well-known/jwks.json(cached 1hr) - Verify RSA signature, check
expclaim, validateclient_idmatches - Fetch user from Cognito → extract
system_account_idfrom custom attributes or membership lookup
Path B — API Key (value does NOT start with "Bearer "):
- Take raw value from
x-api-keyheader - DES3 decrypt → extract
system_account_id,cell_id - Validate against
/key/validateendpoint
Inspect: Check auth Lambda logs — see Lambda.
aws logs tail /aws/lambda/ControllerAuthLambda-{env} --since 15m
5. BFF Console Authorizer (Secondary Layer)
Where: components/bff_console/bff_console/auth/authorizer.py
After the API Gateway authorizer allows the request:
- Validates token again (extracts username, email)
- Queries
UsersAccountsServicefor account membership - Verifies user is ACTIVE member of the requested account
- Enriches request with: cell_id, system_account_id, visible_account_id, role
Google SSO Login
- Browser redirects to Cognito hosted UI → Google OAuth
- Google authenticates → callback to Cognito
DefineAuthChallengeLambda immediately issues tokens (CUSTOM_AUTH flow)- Cognito returns tokens via OAuth callback URL
- Frontend stores token, same as password login
Logout
POST /api/account/logout → cognito_idp.global_sign_out() → invalidates all tokens. Frontend clears cookie.
What to Look For
| Symptom | Where to Check |
|---|---|
| Login failing (wrong credentials) | Cognito user status. Is user CONFIRMED? Is email_verified=true? |
| Login failing (500) | Monolith ECS logs. Cognito service health. |
| API calls returning 401 | Auth Lambda logs. Token expired? JWKS fetch failing? Token in wrong header (must be x-api-key, not Authorization)? |
| API calls returning 403 | Auth Lambda logs. User not member of requested account? |
| Slow auth | Auth Lambda provisioned concurrency cold starts. JWKS cache miss. |
| Google SSO broken | Cognito Google IDP config. PreSignUp Lambda logs. OAuth callback URLs. |
| "User has not confirmed email" | User in UNCONFIRMED state. Resend confirmation or admin-confirm. |
Metrics
Auth Lambda publishes to CloudWatch:
auth_lambda_success— successful authauthentication_error— auth-specific failureauth_lambda_forbidden— 403 access deniedauth_lambda_5xx— internal error
Inspect: CloudWatch dashboard CloudControllerDashboard-{env} — see CloudWatch.
Related Docs
- Control Plane — Cognito, API Gateway, Monolith
- Controller — auth Lambda, UsersAccountsTable
- Cognito — user pool inspection, user status
- Lambda — auth Lambda logs
- Signup — user creation flow