Passwordless Auth System
Plan Metadata
- Plan type:
plan - Parent plan:
N/A - Depends on:
infra-and-deployment.mdshared-lambda-interface.mdmobile-signin-flow.mdmobile-auth-gating.md- Status:
documentation
Status semantics: - draft: Plan is being created or updated and is not final. - approved: Plan is approved but not yet applied in code. - documentation: Code currently exists and matches the plan contract.
Update rule: - When an existing plan is edited, set status to draft until re-approved.
System Intent
- What is being built: Source-of-truth documentation of the existing passwordless auth runtime behavior, including auth APIs, Cognito trigger lambdas, handoff-token persistence, and UI verification execution.
- Primary consumer(s): Engineers maintaining auth APIs, Cognito trigger flow, and UI flows that complete sign-in.
- Boundary (black-box scope only): End-to-end auth behavior from sign-in request through token handoff consumption, plus runtime dependency keys required by auth endpoints.
Stage Gate Tracker
- [x] Stage 1 Mermaid approved
- [x] Stage 2 I/O contracts approved
- [x] Stage 3 pseudocode/technical details approved or skipped
1. Mermaid Diagram
Reference: .agent/skills/create-mermaid-diagram/SKILL.md
Auth system flow
flowchart LR
M[Mobile Signin Boundary - docs/plans/mobile-signin-flow.md]
M -->|start sign-in call| B[Auth Start API - main/server/api/auth/start/app.py]
M -->|handoff exchange call| D[Auth Handoff API - main/server/api/auth/handoff/app.py]
J[Web Verify UI - apps/landing-web/src/routes/auth/verify/+page.svelte] -->|POST /auth/verify email token session| C[Auth Verify API - main/server/api/auth/verify/app.py]
B -->|initiate custom auth and send magic link| H[Cognito and SES Surface - external aws/cognito-ses]
B -->|email delivers verify URL with email token session| J
H -->|invokes define create verify trigger lambdas| F[Auth Challenge Triggers - main/server/auth/challenges/define/app.py + main/server/auth/challenges/create/app.py + main/server/auth/challenges/verify/app.py]
F -->|returns challenge state and answer validation| H
C -->|respond to custom challenge| H
C -->|create handoff code| E[Handoff Store - main/server/layers/shared/python/shared/orm/handoff_orm.py]
D -->|consume handoff code| E
D -->|return token bundle| M
classDef unchanged fill:#d3d3d3,stroke:#666,stroke-width:1px;
classDef updated fill:#ffe58a,stroke:#666,stroke-width:1px;
classDef deleted fill:#f4a6a6,stroke:#666,stroke-width:1px;
classDef created fill:#a8e6a3,stroke:#666,stroke-width:1px;
class B,C,D,E,F,H,J,M unchanged; Ownership note: caller-side start/handoff orchestration is owned by mobile-signin-flow.md.
Auth service infra block (deployable unit)
flowchart LR
A[Auth Infra Inputs - main/devops]
B[Auth Infra Provisioning Surface - main/devops]
C1[Cognito User Pool Resource - external aws/cognito]
C2[Cognito App Client Resource - external aws/cognito]
C3[SES Sender Identity Resource - external aws/ses]
C4[Auth Parameter Resource - external aws/ssm]
D[Auth Infra Outputs - main/devops]
E[Auth Runtime Consumption - main/server/template.yaml and main/server/api/auth]
A -->|auth config values| B
B -->|provisions user pool| C1
B -->|provisions app client| C2
B -->|provisions sender identity| C3
B -->|writes auth parameter keys| C4
C1 -->|exports pool id| D
C2 -->|exports app client id| D
C4 -->|provides /encache/auth/* values| E
classDef unchanged fill:#d3d3d3,stroke:#666,stroke-width:1px;
classDef updated fill:#ffe58a,stroke:#666,stroke-width:1px;
classDef deleted fill:#f4a6a6,stroke:#666,stroke-width:1px;
classDef created fill:#a8e6a3,stroke:#666,stroke-width:1px;
class A,B,C1,C2,C3,C4,D,E unchanged; 2. Black-Box Inputs and Outputs
Stage 2 is defined from the caller/tooling perspective (docs/plans/mobile-signin-flow.md) that invokes auth APIs. Inputs are auth API requests. Outputs are API responses plus observable side effects (state changes and outbound email).
Global Types
StandardError {
status?: number
code?: string
message: string
}
AuthStartRequest {
email: string
}
AuthStartResponse {
session: string
}
AuthVerifyRequest {
email: string
token: string
session: string
}
AuthVerifyResponse {
handoffCode: string
expiresIn: number
}
AuthHandoffRequest {
code: string
}
AuthHandoffResponse {
accessToken: string
idToken: string
refreshToken: string
expiresIn: number
userId: string
username: string
}
Flow: main/server/api/auth/start/app.py, main/server/tests/unit/test_auth_start_app.py
Source: main/server/api/auth/start/app.py.implementation, main/server/tests/unit/test_auth_start_app.py
Type Definitions
Paths
| path-name | input | output/expected state change | path-type | notes | updated |
|---|---|---|---|---|---|
auth-start.success | FlowInput email valid | FlowOutput session; Cognito challenge session exists; magic-link email is sent with verify URL query values (email token session) | happy path | caller receives session for continuation tracking | |
auth-start.email-invalid | FlowInput email missing/invalid | StandardError status=400 code=AUTH_EMAIL_REQUIRED or AUTH_EMAIL_INVALID; no auth state change | error | request rejected before Cognito auth initiation | |
auth-start.runtime-failure | FlowInput valid but config/Cognito/SES operation fails | StandardError status=500 | error | no successful caller continuation token is returned |
Flow: main/server/api/auth/verify/app.py, main/server/tests/unit/test_auth_verify_app.py
Source: main/server/api/auth/verify/app.py.implementation, main/server/tests/unit/test_auth_verify_app.py
Type Definitions
Paths
| path-name | input | output/expected state change | path-type | notes | updated |
|---|---|---|---|---|---|
auth-verify.success | FlowInput valid | FlowOutput handoffCode expiresIn; Cognito challenge completes; user email_verified=true; handoff code is persisted | happy path | caller receives one-time code and TTL for next step | |
auth-verify.input-invalid | FlowInput with missing/invalid email token or session | StandardError status=400 code=AUTH_EMAIL_INVALID or AUTH_TOKEN_REQUIRED or AUTH_SESSION_REQUIRED | error | typed validation errors | |
auth-verify.challenge-invalid | FlowInput with invalid/expired token/session | StandardError status=400 code=AUTH_TOKEN_INVALID | error | no successful handoff code is created | |
auth-verify.runtime-failure | FlowInput valid but Cognito auth result missing/incomplete | StandardError status=500 | error | no handoff code should be persisted |
Flow: main/server/api/auth/handoff/app.py.implementation, main/server/tests/integration/test_auth_flow_integration.py
Source: main/server/api/auth/handoff/app.py.implementation, main/server/tests/integration/test_auth_flow_integration.py
Type Definitions
Paths
| path-name | input | output/expected state change | path-type | notes | updated |
|---|---|---|---|---|---|
auth-handoff.success | FlowInput valid unexpired code | FlowOutput token bundle; handoff code is consumed/deleted | happy path | one-time code exchange for caller session materialization | |
auth-handoff.code-missing | FlowInput code missing | StandardError status=400 code=AUTH_HANDOFF_CODE_REQUIRED | error | input validation failure | |
auth-handoff.code-invalid-expired-or-reused | FlowInput invalid/expired/already-consumed code | StandardError status=400 code=AUTH_HANDOFF_CODE_INVALID | error | replay and stale code attempts are rejected |
Auth service infra contract (microservice extraction bundle)
This block captures auth-only infrastructure surfaces so auth can be redeployed independently as a service boundary.
Inputs
| input | source | used for |
|---|---|---|
var.cognito_user_pool_name | main/devops/variables.tf | Cognito user pool naming |
var.magic_link_sender_email | main/devops/variables.tf | SES sender identity |
var.auth_lambda_trigger_arns | main/devops/variables.tf + main/server/template.yaml outputs | Cognito trigger bindings |
var.magic_link_base_url | main/devops/variables.tf | verify URL generation in auth start flow |
Main
| provisioning surface | source files | responsibility |
|---|---|---|
auth-infra-main | main/devops/main.tf | Creates auth-only AWS resources and publishes auth outputs/parameters |
Deployed resources
| resource | source | key fields |
|---|---|---|
cognito-user-pool | main/devops/main.tf | username_attributes=email, auto_verified_attributes=email, lambda_config trigger hooks |
cognito-app-client | main/devops/main.tf | generate_secret=false, ALLOW_CUSTOM_AUTH, ALLOW_REFRESH_TOKEN_AUTH |
ses-sender-identity | main/devops/main.tf | email=var.magic_link_sender_email |
auth-ssm-parameters | main/devops/main.tf | /encache/auth/user_pool_id, /encache/auth/app_client_id, /encache/auth/magic_link_base_url, /encache/auth/magic_link_sender_email |
Outputs
| output | source | consumed by |
|---|---|---|
cognito_user_pool_id | main/devops/outputs.tf | auth runtime in main/server/api/auth/* via template/env wiring |
cognito_app_client_id | main/devops/outputs.tf | auth runtime in main/server/api/auth/* via template/env wiring |
/encache/auth/* parameter values | main/devops/main.tf + main/server/template.yaml dynamic refs | auth start/verify/handoff runtime |
3. Pseudocode / Technical Details for Critical Flows (Optional)
-
Flow name::
auth-handshake-sequencecaller starts sign-in with POST /auth/start {email} server validates email, ensures user exists, initiates Cognito CUSTOM_AUTH, sends magic-link email email link opens web verify page with {email, token, session} web verify calls POST /auth/verify {email, token, session} server responds to Cognito challenge and creates one-time handoff code caller completes with POST /auth/handoff {code} server consumes code and returns token bundle for session persistence -
Flow name::
handoff-code-lifecycleon successful verify: generate numeric handoff code and store token bundle with expiry timestamp on handoff exchange: lookup code if missing or expired -> reject as AUTH_HANDOFF_CODE_INVALID if valid -> return token bundle and delete record replay with same code after success: reject as AUTH_HANDOFF_CODE_INVALID -
Flow name::
auth-infra-readiness-notes -
Implementation notes:
- This section documents execution-critical behavior only.
mobile-signin-flow.mdowns mobile request orchestration and local session persistence.- This plan includes the auth-only deployable infra bundle for microservice extraction readiness.
infra-and-deployment.mdremains the shared platform deployment plan and links to this auth-specific infra block.
4. Handoff to Related Plan Reconciliation
After all stages are approved, apply .agent/skills/reconcile-plans/SKILL.md to propagate contract updates across linked plans.