UK Companies House explorer behind a shared .andrewreaassociates.com
passkey session.
┌──────────────────────────────────────────────────────────────┐
│ Browser (https://projectpluto.andrewreaassociates.com) │
│ - Vite + React + Tailwind │
│ - All API calls via credentials:'include' │
└─────────────────────────┬────────────────────────────────────┘
│ cookie: auth_token=<jwt>
▼
┌──────────────────────────────────────────────────────────────┐
│ bff.projectpluto.andrewreaassociates.com (CloudFront) │
│ CF Function viewer-request: │
│ cookie auth_token → Authorization: Bearer <jwt> │
└─────────────────────────┬────────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────────┐
│ api.projectpluto.andrewreaassociates.com (APIGW v2) │
│ Lambda authorizer verifies HS256 JWT against the shared │
│ /ara/jwt-signing-key SSM parameter. │
│ Routes: │
│ GET /companies → companies-search lambda │
│ GET /companies/{number} → companies-get lambda │
│ Lambdas call Companies House REST with HTTP Basic auth │
│ (key from /project-pluto/companies-house/api-key SSM). │
└──────────────────────────────────────────────────────────────┘
Login flow: client app sends user to
https://andrewreaassociates.com/admin.html?returnTo=<current page>
ara completes passkey login and sets
auth_token=<jwt>; Domain=.andrewreaassociates.com; HttpOnly; Secure
then redirects back. Browser now sends the cookie to bff.* on
subsequent calls. Cookie is never exposed to JS in the browser.
.
├── aws-setup/ OIDC + IAM role for CI (personal_iphone).
├── aws-setup-dns/ Cross-account dns-writer role (personal_legacy).
├── terraform/
│ ├── main.tf Site bucket + CloudFront + providers.
│ ├── lambda.tf Lambda exec role + functions.
│ ├── api.tf APIGW v2 + custom domain api.*
│ ├── bff.tf CloudFront BFF + CF Function + custom domain bff.*
│ └── cloudfront-functions/cookie-to-auth.js
├── lambdas/
│ ├── go.mod
│ ├── internal/auth JWT verification (HS256 vs ara key)
│ ├── internal/ch Companies House client (Basic auth wrapper)
│ ├── api-authorizer/ API Gateway Lambda authorizer
│ ├── companies-search/ GET /companies
│ └── companies-get/ GET /companies/{number}
├── frontend/ Vite + React + TS + Tailwind
└── .github/workflows/deploy.yml
| Host | What |
|---|---|
| https://projectpluto.andrewreaassociates.com | The React app (static, CloudFront) |
| https://bff.projectpluto.andrewreaassociates.com | BFF — only bff calls hit it |
| https://api.projectpluto.andrewreaassociates.com | API Gateway — should never be |
| called from the browser directly | |
| https://andrewreaassociates.com/admin.html | ara passkey login |
| https://auth.andrewreaassociates.com | ara API (where the cookie is set) |
# personal_iphone: state bucket + OIDC role + lambda/apigw/ssm policies
aws-vault exec personal_iphone -- aws s3api create-bucket \
--bucket project-pluto-tfstate-276447169330 \
--region eu-west-2 \
--create-bucket-configuration LocationConstraint=eu-west-2
cd aws-setup
aws-vault exec personal_iphone -- terraform init
aws-vault exec personal_iphone -- terraform apply
# personal_legacy: cross-account DNS writer role for andrewreaassociates.com
cd ../aws-setup-dns
aws-vault exec personal_legacy -- terraform init
aws-vault exec personal_legacy -- terraform applyThen in the reaandrew/project-pluto repo: set AWS_ROLE_ARN GitHub secret to the
github_actions_role_arn output of aws-setup. Push to main to deploy.