I hated the default Amazon Cognito login screen. It looked like a dashboard from 2015.
Today, I decided to build a custom Split-Screen Auth UI for my React app and update my AWS Lambda backend to actually read the JSON Web Tokens (JWT) sent by the frontend. Here is exactly how I did it.
- The Frontend: Split-Screen Auth with Amplify
Instead of using the standard wrapper which forces a centered card, I used Authenticator.Provider. This gives you access to the useAuthenticator hook so you can render the login form exactly where you want it.
I built a two-column layout using Tailwind CSS. Left side: Branding. Right side: Login.
import { Authenticator, useAuthenticator } from '@aws-amplify/ui-react';
const CustomAuthWrapper = () => {
const { authStatus } = useAuthenticator((context) => [context.authStatus]);
if (authStatus === 'authenticated') return ;
return (
{/* Left Column: Branding */}
FinAI Agent
{/* Right Column: Auth Form */}
<div className="w-1/2 flex items-center justify-center">
<Authenticator />
</div>
</div>
);
};
- Injecting the Bearer Token
To make the backend aware of who is calling it, we need to send the Cognito JWT. I updated my fetch calls in React to grab the session token dynamically:
JavaScript
import { fetchAuthSession } from 'aws-amplify/auth';
const { tokens } = await fetchAuthSession();
const jwtToken = tokens?.idToken?.toString();
const response = await fetch(API_URL, {
headers: { 'Authorization': Bearer ${jwtToken} }
});
- The Backend: Parsing JWT in Python (AWS Lambda)
API Gateway handles the heavy lifting of verifying the signature, but I still needed my Lambda function to know who the user was to query DynamoDB correctly. I wrote a small base64 decoding snippet to extract the sub (user ID) and email.
Python
import base64
import json
def lambda_handler(event, context):
headers = event.get('headers', {})
auth_header = headers.get('authorization', headers.get('Authorization', ''))
if auth_header.startswith('Bearer '):
token = auth_header.split(' ')[1]
payload_b64 = token.split('.')[1]
payload_b64 += '=' * (-len(payload_b64) % 4) # Add padding
payload = json.loads(base64.b64decode(payload_b64).decode('utf-8'))
user_id = payload.get('sub')
user_email = payload.get('email')
print(f"Authenticated: {user_email} (ID: {user_id})")
My Serverless app is now officially Multi-Tenant! Every user gets their own DynamoDB sandbox, and the AI greets them by their real name. 🚀

Top comments (0)