Skip to content

Passwordless Auth System

Plan Metadata

  • Plan type: plan
  • Parent plan: N/A
  • Depends on:
  • infra-and-deployment.md
  • shared-lambda-interface.md
  • mobile-signin-flow.md
  • mobile-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

FlowInput = AuthStartRequest
FlowOutput = AuthStartResponse

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

FlowInput = AuthVerifyRequest
FlowOutput = AuthVerifyResponse

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

FlowInput = AuthHandoffRequest
FlowOutput = AuthHandoffResponse

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-sequence

    caller 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-lifecycle

    on 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

    runtime auth endpoints require Cognito user pool and app client identifiers
    runtime auth start endpoint requires magic-link base URL and sender email
    these runtime values are provided through /encache/auth/* SSM parameters and server template dynamic references
    

  • Implementation notes:

  • This section documents execution-critical behavior only.
  • mobile-signin-flow.md owns mobile request orchestration and local session persistence.
  • This plan includes the auth-only deployable infra bundle for microservice extraction readiness.
  • infra-and-deployment.md remains the shared platform deployment plan and links to this auth-specific infra block.

After all stages are approved, apply .agent/skills/reconcile-plans/SKILL.md to propagate contract updates across linked plans.