Integrating Social SSO into your React app is supposed to make life easier. But if you aren't careful with how your backend handles identities, you will create a massive headache for yourself.
Today, I realized my AWS Cognito integration was silently creating duplicate accounts.
The Problem: I had users who originally signed up with an email and password. When I added "Sign in with Google", Cognito treated those returning users as completely new entities because they had a new internal sub ID. My DynamoDB tables were fragmenting. Worse, my AI Engine (Amazon Bedrock) was being invoked from scratch for these returning users, driving up my cloud bill unnecessarily and destroying loading times.
The Architectural Fix (Lambda Interceptor)
I couldn't let Cognito dictate my database schema. I modified my main Lambda router to intercept the incoming JWTs from the React frontend before they hit the database.
Instead of blindly querying DynamoDB using the sub provided by the auth header, I decoded the token to extract the raw email.
python
# Inside the Lambda Authorizer / Router
token = auth_header.split(' ')[1]
payload = json.loads(base64.b64decode(payload_b64).decode('utf-8'))
user_email_jwt = payload.get('email', '')
real_name = payload.get('name') or payload.get('given_name')
# Force Unification: Use email as the absolute DB key
if user_email_jwt:
user_id = user_email_jwt.lower()
user_name = real_name if real_name else user_email_jwt.split('@')[0].capitalize()
else:
user_id = payload.get('sub', DEFAULT_USER_ID)
By enforcing the email as the Primary Key in DynamoDB, the data source of truth shifted back to my backend. Immediate load times were restored.
The Frontend Polish
While I was at it, I fixed two nagging local development bugs:
Vite Port Binding: Vite kept binding to random ports when I used the --host flag, breaking my strict Cognito Allowed Callback URLs. I updated my App.tsx to dynamically read window.location.origin so I don't have to hardcode ports anymore.
Session Contamination: To ensure the dual-identity bug didn't leave cached artifacts behind, I overrode the AWS Amplify signOut function to completely wipe localStorage before redirecting the user.
Own your user data logic. Code for engagement, but architect for sanity!

Top comments (0)