Cognito User Pool vs Google OAuth: Which Login Strategy Should You Use?
You're building a new app and hit the first real architectural decision: do you spin up a Cognito User Pool with email/password, or just drop in 'Sign in with Google' and call it done? The answer matters more than it looks — one gives you full control over identity, the other offloads credential management entirely, and Cognito can actually do both at the same time if you wire it correctly.
TL;DR: Cognito User Pool vs Google OAuth
| Dimension | Cognito User Pool (native) | Google OAuth (federated) | Cognito + Google (federated via Cognito) |
|---|---|---|---|
| Credential storage | Cognito manages passwords | Google manages passwords | Google manages passwords; Cognito holds the federated identity |
| Token issuer | Cognito (Cognito JWTs) | Google (Google ID tokens) | Cognito (Cognito JWTs, after federation) |
| Your backend sees | Cognito JWT | Google ID token | Cognito JWT (consistent regardless of login method) |
| MFA support | Yes (TOTP, SMS) | Delegated to Google | Cognito MFA applies to native users; Google MFA applies to federated users |
| User attribute control | Full | Read-only from Google | Attribute mapping from Google to Cognito attributes |
| Operational overhead | Medium (user management, password reset flows) | Low | Medium-High (federation config, attribute mapping, account linking) |
How Cognito User Pool Authentication Works
Before choosing, you need a clear mental model of what Cognito actually does in each configuration — because 'Cognito supports Google login' is technically true but hides a lot of plumbing.
- Native flow: The user submits credentials directly to Cognito. Cognito validates against its own user store and issues a JWT set (ID token, access token, refresh token).
- Federated flow: The user authenticates with Google. Google issues an authorization code. Cognito exchanges that code for a Google ID token, maps Google attributes to Cognito user attributes, creates (or looks up) a user in the User Pool, and then issues its own Cognito JWTs.
- Your backend always sees a Cognito JWT — this is the key architectural benefit of running Google through Cognito rather than directly. Your API doesn't need to know or care whether the user authenticated natively or via Google.
Decision Guide: Which Approach Fits Your Use Case?
If you're not sure which path to take, run through this decision flow before touching any configuration.
email/password login?"}; B -- Yes --> C{"Do you also need
social login (Google etc.)?"}; B -- No --> D{"Are all users in
a single Google org?"}; D -- Yes --> E["Direct Google OAuth
(simplest path)"]; D -- No --> F["Cognito + Google federation"]; C -- Yes --> F; C -- No --> G["Cognito User Pool
(native only)"]; F --> H{"Need Lambda triggers,
custom attributes,
or multi-provider?"}; H -- Yes --> I["Cognito User Pool
+ Federation + Lambda triggers"]; H -- No --> J["Cognito User Pool
+ Google federation"]; G --> K["Build Pre Sign-up trigger
for account linking"]; I --> K; J --> K;
- If your users are all within a Google Workspace org, direct Google OAuth is often sufficient and simpler to operate.
- If you need email/password as a fallback, or you want to support multiple identity providers (Google, Facebook, SAML), Cognito as a federation layer is the right call.
- If you need fine-grained user attributes, custom auth flows, or Lambda triggers on sign-in events, Cognito User Pool is required regardless of the login method.
Setting Up Cognito User Pool with Google as a Federated Identity Provider
This is the most common production pattern: Cognito handles all token issuance, and Google is just one of the login options. Here's how to wire it up from scratch using the AWS CLI.
Step 1: Create the User Pool
Create a User Pool with email as the username attribute. This also supports native email/password login alongside Google federation.
aws cognito-idp create-user-pool \
--pool-name my-app-users \
--policies '{"PasswordPolicy":{"MinimumLength":8,"RequireUppercase":true,"RequireLowercase":true,"RequireNumbers":true,"RequireSymbols":false}}' \
--auto-verified-attributes email \
--username-attributes email \
--region us-east-1
Note the UserPoolId in the response — you'll need it in every subsequent step.
Step 2: Create a User Pool Domain
Cognito's hosted UI (which handles the OAuth redirect flow with Google) requires a domain. This is non-negotiable for federation.
aws cognito-idp create-user-pool-domain \
--domain my-app-auth \
--user-pool-id us-east-1_XXXXXXXXX \
--region us-east-1
Step 3: Register Google as an Identity Provider
Before running this, you must create an OAuth 2.0 client in the Google Cloud Console and obtain a client ID and secret. The authorized redirect URI must be set to https://my-app-auth.auth.us-east-1.amazoncognito.com/oauth2/idpresponse.
aws cognito-idp create-identity-provider \
--user-pool-id us-east-1_XXXXXXXXX \
--provider-name Google \
--provider-type Google \
--provider-details client_id=YOUR_GOOGLE_CLIENT_ID,client_secret=YOUR_GOOGLE_CLIENT_SECRET,authorize_scopes="profile email openid" \
--attribute-mapping email=email,name=name,picture=picture \
--region us-east-1
The --attribute-mapping maps Google's token claims to Cognito user attributes. email=email means Google's email claim maps to Cognito's email attribute.
Step 4: Create the App Client
The app client defines which OAuth flows and identity providers are allowed. Wire in both COGNITO (native) and Google so users can choose either path.
aws cognito-idp create-user-pool-client \
--user-pool-id us-east-1_XXXXXXXXX \
--client-name my-app-client \
--generate-secret \
--allowed-o-auth-flows code \
--allowed-o-auth-scopes openid email profile \
--allowed-o-auth-flows-user-pool-client \
--callback-urls "https://yourapp.com/callback" \
--logout-urls "https://yourapp.com/logout" \
--supported-identity-providers COGNITO Google \
--region us-east-1
Step 5: Update the App Client (if already exists)
If you're adding Google to an existing app client, use update-user-pool-client instead. Every parameter must be re-specified — partial updates overwrite omitted fields with defaults.
aws cognito-idp update-user-pool-client \
--user-pool-id us-east-1_XXXXXXXXX \
--client-id YOUR_APP_CLIENT_ID \
--allowed-o-auth-flows code \
--allowed-o-auth-scopes openid email profile \
--allowed-o-auth-flows-user-pool-client \
--callback-urls "https://yourapp.com/callback" \
--logout-urls "https://yourapp.com/logout" \
--supported-identity-providers COGNITO Google \
--region us-east-1
IAM Permissions for Cognito Administration
If you're running these CLI commands from a CI/CD pipeline or an admin Lambda, the executing role needs at minimum these permissions. Read/List actions on Cognito User Pools require "Resource": "*" — resource-level restriction is not supported for those actions.
🔽 Click to expand — IAM policy for Cognito admin operations
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "CognitoUserPoolAdmin",
"Effect": "Allow",
"Action": [
"cognito-idp:CreateUserPool",
"cognito-idp:CreateUserPoolClient",
"cognito-idp:UpdateUserPoolClient",
"cognito-idp:CreateUserPoolDomain",
"cognito-idp:CreateIdentityProvider",
"cognito-idp:DescribeUserPool",
"cognito-idp:ListUserPools",
"cognito-idp:AdminLinkProviderForUser"
],
"Resource": "*"
}
]
}
Account Linking: When the Same Person Has Both Login Methods
Here's a scenario that bites almost every team eventually: a user signs up with email/password, then later clicks 'Sign in with Google' using the same email address. Without account linking, Cognito creates a second, separate user record. Your database now has two user IDs for the same human.
Think of it like two loyalty cards for the same customer at the same store. The store's system doesn't know they're the same person until someone explicitly links the records.
Cognito's AdminLinkProviderForUser API merges a federated identity into an existing native user. You'd typically call this from a Pre Sign-up Lambda trigger that detects email collisions.
aws cognito-idp admin-link-provider-for-user \
--user-pool-id us-east-1_XXXXXXXXX \
--destination-user ProviderName=Cognito,ProviderAttributeValue=EXISTING_USERNAME \
--source-user ProviderName=Google,ProviderAttributeName=Cognito_Subject,ProviderAttributeValue=GOOGLE_USER_SUB \
--region us-east-1
After linking, the user can sign in via either method and will resolve to the same Cognito user record and the same sub claim in the JWT.
The Misdiagnosis That Wastes an Afternoon
A team sets up Google federation, tests the login flow, gets a Cognito JWT back, and everything looks fine. Two weeks later, a user reports they can't log in. The Cognito console shows the user exists. The Google OAuth consent screen works. The error in CloudWatch is invalid_grant on the token exchange.
The instinct is to blame the Google OAuth credentials — rotate the client secret, re-test, same error. The actual cause: the Google OAuth client in Google Cloud Console had the Cognito idpresponse redirect URI removed by someone cleaning up 'unused' URIs. Google rejected the redirect, Cognito got a malformed response, and surfaced it as a generic token exchange failure.
The fix is to verify the authorized redirect URIs in Google Cloud Console match exactly what Cognito sends: https://<your-domain>.auth.<region>.amazoncognito.com/oauth2/idpresponse. Check this first before touching Cognito configuration — the failure originates upstream.
Behavioral Depth: Attribute Mapping and the Silent Overwrite Problem
When a federated user signs in, Cognito re-applies attribute mapping on every login — not just at account creation. If your attribute mapping includes name=name and the user has manually updated their display name in your app, that update gets silently overwritten by whatever Google returns on the next sign-in.
This interaction between attribute mapping configuration and mutable user attributes isn't prominently called out in the mapping docs, but it's the expected behavior. If you need user-editable attributes that survive re-federation, either exclude those attributes from the mapping, or use a custom attribute (prefixed custom:) that isn't mapped from the provider.
Verifying the Full Federation Flow
After configuration, validate each layer independently before doing an end-to-end test. Debugging a broken end-to-end flow without isolating layers is slow.
Check the identity provider is registered:
aws cognito-idp describe-identity-provider \
--user-pool-id us-east-1_XXXXXXXXX \
--provider-name Google \
--region us-east-1
Check the app client has Google in supported providers:
aws cognito-idp describe-user-pool-client \
--user-pool-id us-east-1_XXXXXXXXX \
--client-id YOUR_APP_CLIENT_ID \
--region us-east-1
Look for SupportedIdentityProviders in the response. If Google is absent, the hosted UI won't show the Google login button regardless of other configuration.
Check the domain is active:
aws cognito-idp describe-user-pool-domain \
--domain my-app-auth \
--region us-east-1
The Status field should be ACTIVE. A domain in CREATING state will return errors on redirect.
Wrap-Up: Cognito User Pool vs Google OAuth — Choosing the Right Login Strategy
If your app only needs Google login and you're comfortable with Google ID tokens reaching your backend, direct Google OAuth is simpler. The moment you need email/password fallback, multiple providers, Lambda triggers on auth events, or a consistent token format regardless of login method — Cognito as a federation layer earns its complexity.
The account linking problem is the most common operational surprise. Build the Pre Sign-up Lambda trigger for email collision detection before you go to production, not after your first support ticket about duplicate accounts.
For next steps, review the Cognito User Pool federation documentation and the Pre Sign-up Lambda trigger reference for account linking implementation details.
Glossary
| Term | Definition |
|---|---|
| User Pool | A Cognito directory that stores user accounts and handles authentication. Issues its own JWTs. |
| Federated Identity Provider | An external authentication system (like Google) that Cognito trusts to authenticate users on its behalf. |
| ID Token | A JWT containing user identity claims (email, name, sub). Issued by Cognito after successful authentication, regardless of login method. |
| Account Linking | The process of associating a federated identity (Google login) with an existing native Cognito user record so both resolve to the same user. |
| Attribute Mapping | Configuration that maps claims from a federated provider's token to Cognito user attributes. Applied on every sign-in, not just at registration. |
Comments
Post a Comment