This Repository contains two services (lambda functions) to operate a private certificate authority (CA). These two services are to verify a reader authentication by issuing a short-lived certificate (valid for approximately 24 Hrs) to access credentials from a Holder App.
Generates cryptographically secure, single-use nonces for replay protection. Each nonce:
- Is a UUID v4 (36 characters)
- Has a 5-minute TTL in DynamoDB
- Can only be consumed once by the certificate issuance service
Issues short-lived X.509 reader certificates (24-hours validity) after verifying:
- Firebase App Check token (via
X-Firebase-AppCheckheader) - Certificate Signing Request (CSR) validation
Mock JWKS Service (/mock-jwks): Returns public keys for Firebase App Check token verification in test environments.
Mock Issue Cert Service (/mock-issue-cert): Generates complete mock certificate requests with Firebase App Check tokens for testing.
{
"headers": {
"X-Firebase-AppCheck": "<firebase-app-check-jwt>"
},
"body": {
"csrPem": "-----BEGIN CERTIFICATE REQUEST-----\n...\n-----END CERTIFICATE REQUEST-----"
}
}- Node.js version 22 (use the provided
.nvmrcfile with nvm for easy version management) - AWS SAM CLI for deployment
- AWS CLI configured with appropriate credentials for Secrets Manager access
- Husky - For pre-push validations
This project uses ECMAScript Modules (ESM) as the module system:
- Native
import/exportsyntax throughout the codebase "type": "module"in package.json enables ESM- esbuild for fast TypeScript compilation and bundling
- Vitest for testing with native ESM support
Use getRequiredEnvironmentVariables as the standard way for lambdas to read required environment variables.
- Define a
REQUIRED_ENVIRONMENT_VARIABLESarray in a lambda config helper (e.g.*-config.tsor*-handler-config.ts) - Add each required env var key to that array
- Call
getRequiredEnvironmentVariables(env, REQUIRED_ENVIRONMENT_VARIABLES)and return the typed config result - In that same config helper, validate env var values before returning config (for example, valid URL shape or numeric values)
Example: See config.ts that defines REQUIRED_ENVIRONMENT_VARIABLES.
When a required env var is missing, this utility returns an error containing missingEnvVars, so the config helper can log details and the handler can fail fast.
Use the Result pattern for helper/service functions so handlers can evaluate success/failure explicitly and respond accordingly.
- Return
successResult(value)for success paths - Return
emptySuccess()when success has no payload - Return
errorResult(error)for failure paths with an error payload - Return
emptyFailure()when failure has no error payload - In handlers, check
result.isErrorto decide behaviour, status code and response body
This keeps service/helper code consistent and avoids ambiguous return types between success and failure flows.
Pre merge checks are documented in our quality gates manifest to align with the One Login quality gates schema. This is used to track which automated checks run before merging.
npm installThe npm postinstall script should take care of installing Husky.
Build the project for deployment:
npm run buildThis uses esbuild to:
- Compile TypeScript to ESM JavaScript
- Bundle Lambda functions for optimal performance
- Generate source maps for debugging
- Handle module resolution automatically
Run unit tests using Vitest (with native ESM support):
npm run testRun tests with coverage:
npm run test:covThese integration tests are implemented with Cucumber and exercise the deployed API end to end against the main build environment using the mock services.
The Cucumber feature files and step definitions live under tests/integrationTests.
Run the integration tests:
npm run test:integrationCurrent limitation: these tests are currently wired to the main build stack only. Support for running the integration tests against the dev environment will be added in a future iteration.
For testing in dev/build environments, mock services are available:
Retrieve Firebase App Check public keys for token verification:
curl https://mock.verifier-ca.dev.account.gov.uk/mock-jwksGenerate a complete mock certificate request with Firebase App Check token:
curl https://mock.verifier-ca.dev.account.gov.uk/mock-issue-cert-requestThis returns a JSON payload containing:
headers: Object withX-Firebase-AppCheckJWT tokenbody: Object withcsrPem(Certificate Signing Request)
You can use this payload directly to test the /issue-reader-cert endpoint.
The certificate issuer service uses these environment variables:
NONCE_TABLE_NAME: DynamoDB table for nonce storage
The service automatically configures:
- Dev/Build environments: Uses mock JWKS endpoint for Firebase App Check token verification
- Production environments: Uses official Firebase App Check JWKS endpoint
The mock infrastructure stores keys in AWS Secrets Manager:
<stack-name>-mock-device-keys: ECDSA P-384 key pair for CSR generation<stack-name>-mock-firebase-appcheck-keys: RSA 2048 key pair for Firebase App Check JWT signing
Note: Ensure your AWS credentials have access to Secrets Manager in the eu-west-2 region.
- Push your feature branch to the remote repository.
- Go to Actions > Feature Branch Deploy in GitHub.
- Click Run workflow, select your feature branch, and optionally enable monitoring:
Input parameter enable_monitoring - Defaults to false.
Set to true to enable CloudWatch monitoring on the deployed stacks.
- The workflow will:
- Derive the stack identifier from the branch name
- Build and validate both SAM templates
- Deploy
ca-base-<branch>(DynamoDB nonce table) - Deploy
ca-back-<branch>(Lambda functions and API Gateway)
Cleanup happens automatically when the pull request is closed (or merged).
The workflow uses the PR's head branch name to derive the same stack identifier that was used during deployment.
The Manaual cleanup is also supported by running the cleanup-feature-branch workflow from
GitHub Actions and providing the branch name as an input parameter.