Architecture · Deep dive · Identity
Cognito Day‑2 Identity Architecture for WordPress
A deep dive into the CloudFormation-backed identity backbone behind Gatey: Cognito triggers, IAM roles, branded email, reCAPTCHA, token scopes and API authorization.
Architecture thesis: The useful architecture is not “add Cognito login to WordPress.” It is a repeatable Cognito subsystem that turns sign-up, SSO, token design, group assignment, email delivery and API authorization into deployable infrastructure.
The real Cognito problem is day‑2 architecture
Creating a Cognito User Pool is easy. Running it as the identity backbone of a WordPress-based platform is where the real work starts.
You need branded email delivery, bot-resistant sign-up, social and enterprise IdP linking, group-to-scope mapping, post-confirmation group assignment, optional IAM credentials, API Gateway authorization and a predictable way to repeat all of it across sites and environments.
That is the purpose of the Cognito day‑2 template: it packages the parts that usually become console checklists, Lambda snippets and tribal knowledge into a repeatable identity subsystem.
System boundary
WordPress / Gutenberg page
│
▼
Gatey Authenticator block / shortcode / widget
│ browser-side auth flow, no WordPress client secret
▼
Amazon Cognito User Pool + App Client
│
├── Pre Sign-Up Lambda
│ reCAPTCHA validation, trusted domains, IdP linking
│
├── Custom Email Sender Lambda
│ S3 HTML templates, SES when configured, Cognito fallback
│
├── Pre Token Generation Lambda
│ Cognito groups → sc.group.<group> access-token scopes
│
└── Post Confirmation Lambda
confirmed users → Registered group
Optional Cognito Identity Pool
│
├── AuthenticatedRole: minimal identity exchange
└── RegisteredRole: execute-api permissions for confirmed users
WordPress is not the identity provider in this pattern. It renders the UI and stores non-sensitive configuration such as region, User Pool ID and App Client ID. Cognito owns authentication. Lambda triggers implement the day‑2 behavior. API Gateway and IAM consume the resulting tokens or credentials.
What the template provisions
| Building block | Why it exists | Key design choice | Operational note |
|---|---|---|---|
| User Pool + App Client | Primary user directory and OAuth client for browser-based WordPress login | No client secret; code/SRP-friendly public-client setup | Can be created by the stack or attached to an existing pool depending on template mode. |
| Optional custom domain | Branded sign-in and OAuth callback flows | ACM certificate and optional Route53 alias when the hosted zone is available | DNS and certificate ownership must be explicit before production rollout. |
| Identity Pool | Exchange authenticated Cognito users for AWS credentials when IAM-signed APIs are needed | Separate AuthenticatedRole from RegisteredRole | Useful for IAM-protected API Gateway calls from static frontends. |
| Custom Email Sender | Replace plain Cognito emails with HTML templates and branded flows | Templates live in S3; SES is used when configured, otherwise Cognito delivery can remain the fallback | Treat templates as versioned product assets, not inline console text. |
| Pre Sign-Up trigger | Validate sign-up quality before users enter the pool | Optional reCAPTCHA, trusted-domain behavior and external IdP linking by email | Failures should be clear enough for frontend UX and logged enough for debugging. |
| Pre Token Generation trigger | Project group membership into access-token scopes | Add scopes such as sc.group.registered or sc.group.admin | Makes API Gateway scope checks more declarative. |
| Post Confirmation trigger | Move confirmed users into the Registered group | Only handles real sign-up confirmations, not every confirmation event | Decouples “authenticated” from “registered enough to call APIs”. |
| Outputs | Let other stacks and plugins consume identity artifacts | Expose pool IDs, client IDs, domain, roles, groups and function ARNs | Outputs are the contract between this stack and the rest of the platform. |
The sign-up flow
Visitor opens a WordPress page
│
▼
Gatey renders the Cognito sign-up UI
│
├─ browser obtains reCAPTCHA token when enabled
│
▼
Cognito SignUp request
│
▼
Pre Sign-Up Lambda
├─ validate reCAPTCHA action and score
├─ evaluate trusted domains if configured
└─ link external IdP identities by email when possible
│
▼
Cognito creates the user and sends confirmation
│
▼
Custom Email Sender Lambda
├─ load matching HTML template from S3
├─ interpolate attributes and confirmation code/link
└─ send with SES or allow Cognito fallback
│
▼
User confirms account
│
▼
Post Confirmation Lambda adds user to Registered group
The important design detail is that bot filtering, email branding, account linking and group assignment are not WordPress plugin hooks. They happen inside Cognito and Lambda, where they remain valid whether the WordPress frontend is dynamic, statically exported or served behind CloudFront.
Token and authorization model
A common mistake is to treat login as the end of the identity story. For application features, the important question is what the authenticated user can call after login.
The day‑2 template supports a clean split: a user can be authenticated without automatically receiving every runtime permission. After confirmation, the Post Confirmation trigger places the user into the Registered group. The Pre Token Generation trigger can then add scopes based on group membership, such as sc.group.registered and sc.group.admin.
API Gateway methods can be protected with Cognito authorizers and scopes, while IAM-based patterns can use the Identity Pool role mapping. This keeps authorization at the API edge instead of burying it in WordPress templates or frontend visibility logic.
| Layer | Artifact | What it proves | What it should not do |
|---|---|---|---|
| Gatey/browser | Cognito tokens and local auth state | The user completed the configured Cognito flow | Store secrets on the WordPress server or proxy passwords through PHP. |
| User Pool groups | registered, admin or project-specific groups | The user belongs to a business role | Become the only runtime enforcement point. |
| Access-token scopes | sc.group. | The token carries API-readable role context | Replace backend authorization where resource ownership matters. |
| Identity Pool role | AuthenticatedRole or RegisteredRole | The browser can obtain temporary AWS credentials for allowed actions | Grant broad account-level permissions. |
| API Gateway method | Cognito scope or IAM authorization | The route enforces identity at the service boundary | Rely on hidden buttons or CSS-only restrictions. |
Why the two-role IAM model matters
The Identity Pool part of the architecture is especially interesting because it separates two ideas that are often collapsed into one: “the user is signed in” and “the user may call protected AWS APIs.”
Step 1
AuthenticatedRole
This role represents the minimal state of being authenticated through the Identity Pool. It should be conservative and only support the credential exchange path needed by the application.
Step 2
RegisteredRole
This role is attached to confirmed users through the Registered group and can receive execute-api permissions for the current account’s API Gateway resources.
Result
Permission becomes intentional
The architecture avoids treating every signed-in user as an application user with full runtime access. That matters for portals, member areas and client-facing tools.
This split also gives future templates a clean contract: a backend stack can trust that “registered” users are not merely visitors with a token, but users who passed the confirmation and group-assignment path.
Custom email is infrastructure, not cosmetics
Cognito’s built-in email experience is acceptable for prototypes, but production portals usually need branded HTML emails, localized copy, custom callback links and a clear sender identity.
The template treats email as deployable infrastructure. Starter templates live in S3, the Lambda interpolates known attributes and verification codes, and SES can be used when the sender identity is verified. When SES is not configured, the design can fall back to Cognito’s built-in transport rather than blocking the whole identity rollout.
S3 templates
Versioned HTML assets
Sign-up, resend-code, forgot-password, admin-create, attribute verification, authentication and account-takeover templates can be managed as files.
SES sender
Production sender identity
The stack can use a verified email or domain identity for branded delivery when the customer owns SES setup.
Runtime fallback
Safer adoption path
When full SES delivery is not ready, Cognito delivery can keep the user journey moving while the stack remains deployable.
Security model
No password proxy
Browser talks to Cognito
The WordPress server does not need to receive passwords or become an authentication broker.
No client secret in WP
Public-client setup
The app client is designed for browser flows and avoids secrets that cannot be protected in static or frontend environments.
Trigger-scoped IAM
Least privilege Lambdas
Each trigger needs only the actions it performs: template reads, SES send, user linking or group assignment.
reCAPTCHA server validation
Bot checks outside WordPress
The token is collected in the browser but validated by the Pre Sign-Up Lambda before Cognito accepts the sign-up path.
Groups to scopes
Authorization-ready tokens
Group context becomes machine-readable for API Gateway without forcing every backend to call Cognito again.
Outputs as contract
Composable stacks
Other templates should consume stack outputs rather than duplicating identity resources manually.
Operational failure modes
| Failure mode | User-visible symptom | Likely cause | Runbook direction |
|---|---|---|---|
| reCAPTCHA rejects sign-up | User cannot create an account | Wrong site key/secret, stale token, low score or action mismatch | Check clientMetadata token path, SSM secret, score threshold and Lambda logs. |
| Custom email not delivered | No confirmation/password email arrives | SES identity not verified, sandbox restriction, template read failure or FROM mismatch | Check SES identity, CloudWatch logs, S3 template key and fallback behavior. |
| Social login creates duplicate users | Same email appears under separate provider identities | External IdP linking disabled or failed | Review Pre Sign-Up logs and AdminLinkProviderForUser permissions. |
| API call denied after login | Frontend authenticates but protected API returns 401/403 | User not in Registered group, scopes missing, Identity Pool role not mapped or API authorizer misconfigured | Inspect token scopes, group membership, Post Confirmation logs and API Gateway authorizer settings. |
| Custom domain fails | Hosted UI or callback domain does not resolve | Certificate region/validation, Route53 zone mismatch or alias target issue | Validate ACM cert, DNS zone ownership and Cognito domain status. |
When this is a good fit
- You want Cognito login in WordPress without turning WordPress into the authentication backend.
- You need social or enterprise SSO, MFA, role-aware APIs or static-export-compatible login.
- You want sign-up hardening with reCAPTCHA and consistent user linking across external identity providers.
- You need branded, template-based Cognito emails that can be managed as deployment assets.
- You plan to compose protected APIs, AI backend or workflow stacks around the same identity backbone.
When not to use this
- The site only needs a simple WordPress login for wp-admin users and no frontend identity layer.
- The team does not want Cognito, AWS IAM, DNS/certificate ownership or CloudFormation-managed identity resources.
- All user management must remain in the WordPress users table because of existing plugin dependencies.
- The project is a short-lived prototype where console-created Cognito resources are acceptable and no repeatability is needed.
Related resources
Deep dive
WordPress on AWS Reference Architecture
pillar article that positions identity inside the wider WP Suite runtime split
Deep dive
Secure Static WordPress with Signed Cookies
how Cognito login can unlock protected static paths at CloudFront
FAQ
Why call this “day‑2” Cognito?
Because the hard parts appear after the first User Pool exists: email templates, IdP linking, sign-up protection, group assignment, API authorization, IAM credentials, DNS, certificates and repeatable deployment.
Does Gatey store Cognito secrets in WordPress?
No. The architecture is built around browser-side Cognito flows and public-client configuration, so WordPress does not need to store a Cognito client secret or user tokens.
Why add group names as token scopes?
Scopes make group context directly usable by API Gateway authorizers. Instead of every API reloading Cognito group membership, the access token can carry scoped authorization hints.
Why separate AuthenticatedRole and RegisteredRole?
A signed-in visitor and a confirmed application user are not always the same thing. The split lets the architecture grant API permissions only after the post-confirmation group path has completed.
Can this attach to an existing User Pool?
The architecture is designed to support both newly created and existing Cognito resources, with optional triggers and outputs depending on the deployment parameters.
Is this only useful for static WordPress?
No. Dynamic WordPress sites benefit too. Static export simply makes the browser-side and AWS-native boundary more visible.
Turn Cognito login into an identity backbone
Use this architecture when WordPress needs real identity primitives: SSO, MFA, groups, API authorization, protected static paths and repeatable customer-owned AWS deployment.
