Authentication API (`auth.arda.cards`)
The Authentication API gives Arda applications a stable, Arda-owned HTTPS surface for interactive login and tenant
selection. Each partition receives its own endpoint under the auth.arda.cards domain family, for example
https://dev.alpha002.auth.arda.cards.
The first release exposes password login and tenant switching.
Problem
Section titled “Problem”Arda clients previously depended directly on Cognito-specific authentication calls and configuration. Tenant selection also required changing a Cognito attribute and refreshing tokens in the correct order. Repeating those details in every client application would expose the Cognito client secret, couple clients to AWS APIs, and subject the client implementation to complicated call sequences.
The platform needs one small authentication facade that:
- keeps the Cognito web-client secret in AWS Secrets Manager;
- presents a stable, partition-specific Arda URL;
- performs login and tenant-token rotation consistently;
- leaves tenant authorization with the domain component that owns current
AgentFormembership.
Intended outcome
Section titled “Intended outcome”A client can authenticate with username and password, then select a tenant and receive replacement access and ID tokens containing that tenant context. The client does not need AWS credentials, a Cognito client secret, or direct access to the Cognito SDK.
The active-tenant claim is context, not proof of access. A user may name a tenant during token rotation, but every
business component must validate the user’s current AgentFor relationship when serving a tenant-scoped request. This
keeps authorization current when a user is suspended or evicted, even if an older JWT has not expired.
The initial capability includes:
POST /v1/loginusing CognitoUSER_PASSWORD_AUTH;POST /v1/tenantSwitchusing a bearer access token and refresh token;- a regional API Gateway HTTP API with a custom DNS name and no public
execute-apiendpoint; - one Lambda function per operation;
- request validation derived from the OpenAPI contract;
- a CloudFormation export containing the partition’s authentication base URL;
- unit and CDK synthesis tests for handlers, routes, DNS, permissions, and configuration.
Success criteria
Section titled “Success criteria”- Every enabled partition exposes a TLS-protected endpoint at
https://<partition>.<infrastructure>.auth.arda.cards. - Login returns Cognito’s authentication result or challenge without exposing the web-client secret.
- Tenant switching rejects malformed requests, invalid tokens, and token pairs that identify different users.
- Successful tenant switching returns a token set generated after
custom:tenantis updated. - The source OpenAPI contract drives TypeScript types, runtime validation, and CDK route definitions.
- Downstream APIs continue to validate current tenant membership at access time.
Design
Section titled “Design”Public contract
Section titled “Public contract”The Authentication API OpenAPI specification is the source of truth for paths and schemas.
| Operation | Authentication | Request | Result |
|---|---|---|---|
POST /v1/login | None | AuthFlow, USERNAME, PASSWORD | Cognito authentication result or challenge |
POST /v1/tenantSwitch | Access token in Authorization header | TenantId, RefreshToken | Replacement access, ID, and refresh tokens |
Only USER_PASSWORD_AUTH is accepted for login. Both request schemas reject unknown properties and require non-empty
credential and tenant values. Responses use JSON and Cache-Control: no-store; contract failures return 400, while
invalid or mismatched tenant-switch credentials return 401.
Login flow
Section titled “Login flow”The login Lambda validates the JSON request, loads and caches the Cognito web-client secret from Secrets Manager, and
computes Cognito’s SECRET_HASH
from the username, client ID, and client secret. It then calls
InitiateAuth(USER_PASSWORD_AUTH).
Successful authentication results are returned unchanged. Cognito challenges are also passed through so a client can detect a challenge without the facade inventing a second authentication state model. Recognized Cognito service errors retain their HTTP status and error name; unexpected failures remain unhandled so Lambda and API Gateway record them as operational faults.
Tenant-switch flow
Section titled “Tenant-switch flow”Tenant switching deliberately proves token identity before changing the active tenant:
- Parse the bearer access token and the OpenAPI-validated request body.
- Call Cognito
GetUserand verify the access token’stoken_use,client_id, issuer, subject, and username against the partition’s user pool and web client. - Use the supplied refresh token once and validate the resulting access token.
- Require the refresh-token subject to equal the bearer-token subject.
- Update the Cognito user’s
custom:tenantattribute. - Refresh a second time so the pre-token-generation trigger copies the new tenant into the returned token claims.
The first refresh prevents one user’s bearer token from being paired with another user’s refresh token. The second refresh is required because tokens created before the attribute update contain the old tenant context.
Tenant membership is intentionally not checked in this flow. Membership can change at any time, whereas the token is a
time-bounded credential. Treating
custom:tenant as a selector and rechecking AgentFor in each component prevents a stale token from preserving access
after eviction. See
Access Rules for UserAccounts and Tenants
and
User-Tenant Onboarding and Membership
for the domain authorization model.
Deployment architecture
Section titled “Deployment architecture”For each partition, Route 53 directs the Arda-owned hostname to a regional API Gateway custom domain secured by an ACM certificate. API Gateway dispatches the two routes to dedicated Node.js Lambdas; the Lambdas use Cognito for identity operations, Secrets Manager for the web-client secret, and CloudWatch Logs for short-retention operational logs.
The DNS hierarchy separates infrastructure and partition concerns. The infrastructure ingress stack owns the
<infrastructure>.auth.arda.cards hosted zone and wildcard certificate. The partition authentication stack creates the
partition record, API custom domain, routes, Lambdas, and the <infrastructure>-<partition>-API-AuthBaseUrl
CloudFormation export.
Security and operational properties
Section titled “Security and operational properties”- API Gateway’s default
execute-apiendpoint is disabled, forcing use of the managed hostname and certificate. - Lambda IAM access is narrow: both functions can read the web-client secret, and only the tenant-switch function can
call
cognito-idp:AdminUpdateUserAttributeson its user pool. - Responses containing credentials are marked
no-store. - The client secret is cached in each warm Lambda environment but is never sent to the caller.
- Access-token validation checks Cognito state with
GetUseras well as token claims. The downstream API remains responsible for JWT signature validation and tenant authorization on business requests. - Function log groups use one-week retention and are deleted with the stack.
High-level implementation
Section titled “High-level implementation”Infrastructure composition
Section titled “Infrastructure composition”The existing AuthnServiceConstruct creates the Cognito user pool, resource server, web client, machine-to-machine
client, and their Secrets Manager secrets. When both an authentication hosted zone and certificate ARN are supplied, it
now composes a LoginApi construct.
LoginApi creates the regional custom domain and HTTP API, disables the default endpoint, creates the two Lambda
integrations, grants their permissions, and writes the Route 53 alias. The containing PartitionAuthn stack publishes
the base URL beside the existing user-pool, web-client, and M2M exports. Requiring the hosted zone and certificate
together prevents a partially configured public endpoint.
Contract generation
Section titled “Contract generation”src/main/openapi/auth-api.yaml is an application contract, not a CloudFormation input. The generator reads it and
produces three artifacts used by the TypeScript implementation:
- request and response types through
openapi-typescript; - JSON Schema definitions consumed by Ajv for runtime request validation;
- an operation map that supplies the HTTP methods and paths to CDK.
The generated directory is build output and is not committed. Build, test, and CDK synthesis jobs generate the contract first, check it, and then compile the code. This makes an OpenAPI edit fail early if required schemas or operation IDs are missing and prevents route strings, runtime validation, and client-facing documentation from drifting independently.
Verification
Section titled “Verification”Handler unit tests cover valid requests, malformed JSON, missing and additional properties, Cognito errors, bearer parsing, token-claim validation, subject mismatch, and the two-refresh tenant-switch sequence. CDK tests assert route mapping, the custom domain, the Route 53 alias, IAM grants, paired zone/certificate configuration, and publication of the authentication base URL.
Before rollout, synthesize every partition and verify DNS delegation, certificate validation, the exported base URL, and
both operations against a non-production user. Authorization verification must include deleting or suspending an
AgentFor relationship and confirming that downstream access is denied even when the caller still holds a token naming
that tenant.
References
Section titled “References”- PDEV-461 — Implement Access Rule for AgentFor record
- Infrastructure PR 515 — partition-scoped Authentication API
- Service Users
- Cognito Service
Copyright: (c) Arda Systems 2025-2026, All rights reserved
Copyright: © Arda Systems 2025-2026, All rights reserved