Add authentication to your app using AuthReturn and AWS Cognito.
The auth component automatically loads its CSS and the Cognito SDK, so you only need one script tag:
<!-- Auth Component (auto-loads CSS and Cognito SDK) -->
<script src="https://authreturn.com/static/auth_component.js"></script>
<!-- Optional: Header Component for login/logout buttons -->
<script src="https://authreturn.com/static/auth_header_component.js"></script>
<link rel="stylesheet" href="https://authreturn.com/static/auth_header_component.css">
<!-- Modal for login/signup forms -->
<div id="auth-modal" class="auth-modal" style="display: none;">
<div class="auth-modal-backdrop"></div>
<div class="auth-modal-content">
<button class="auth-modal-close" onclick="closeModal()">×</button>
<div id="auth-container"></div>
</div>
</div>
const APP_ID = 1; // Your app ID from AuthReturn
function showAuthModal(mode) {
AuthComponent.create({
containerId: 'auth-container',
mode: mode, // 'login' or 'signup'
appId: APP_ID,
theme: 'dark', // 'light' or 'dark' (default: 'light')
onSuccess: (token, user) => {
// user contains: { email, userId }
localStorage.setItem('auth_token', token);
localStorage.setItem('user_email', user.email);
document.getElementById('auth-modal').style.display = 'none';
// Refresh your UI here
},
onError: (error) => {
console.error('Auth error:', error);
}
});
document.getElementById('auth-modal').style.display = 'flex';
}
function closeModal() {
document.getElementById('auth-modal').style.display = 'none';
}
| Option | Type | Default | Description |
|---|---|---|---|
containerId | string | required | ID of the container element |
mode | string | 'login' | 'login' or 'signup' |
appId | number | null | Your app ID (fetches Cognito config automatically) |
cognitoConfig | object | null | Direct Cognito config: { userPoolId, clientId, region } |
authReturnUrl | string | 'https://authreturn.com' | AuthReturn server URL |
theme | string | 'light' | 'light' or 'dark' |
onSuccess | function | null | Callback: (token, user) => {} |
onError | function | null | Callback: (errorMessage) => {} |
Either appId or cognitoConfig is required. If using appId, Cognito config is fetched from AuthReturn automatically.
The header component manages login/logout buttons and shows user state:
<div class="header-actions" id="auth-header-container">
<button class="auth-header-login-btn">Sign In</button>
<button class="auth-header-signup-btn">Sign Up</button>
<div class="auth-header-user-info" style="display: none;">
<span class="auth-header-user-email"></span>
<button class="auth-header-logout-btn">Sign Out</button>
</div>
</div>
<script>
const authHeader = AuthHeader.create({
containerId: 'auth-header-container',
authServiceUrl: 'https://authreturn.com',
appId: 1,
onLoginClick: (e) => { e.preventDefault(); showAuthModal('login'); },
onSignupClick: (e) => { e.preventDefault(); showAuthModal('signup'); },
onLogout: () => {
// Your app cleanup - AuthHeader already clears localStorage
}
});
// Returned object has methods:
// authHeader.getUser() - returns current user or null
// authHeader.checkAuth() - re-check auth state
// authHeader.updateUI() - manually refresh UI
</script>
| Option | Type | Default | Description |
|---|---|---|---|
containerId | string | required | ID of the container element |
authServiceUrl | string | 'https://authreturn.com' | AuthReturn server URL |
appId | number | null | Your app ID |
cognitoConfig | object | null | Direct Cognito config: { userPoolId, clientId } |
onLoginClick | function | null | Click handler for login button |
onSignupClick | function | null | Click handler for signup button |
onLogout | function | null | Callback after logout completes |
The AuthHeader component automatically stays in sync with auth state changes:
authreturn:authchange event - no manual sync neededstorage eventWhen your backend returns 401 (expired token), dispatch the event to keep AuthHeader in sync:
if (response.status === 401) {
localStorage.removeItem('auth_token');
localStorage.removeItem('user_email');
window.dispatchEvent(new CustomEvent('authreturn:authchange'));
// Show login UI...
}
The auth component uses these CSS classes for styling:
| Class | Description |
|---|---|
.auth-form | Main form container (max-width: 400px) |
.auth-theme-light | Light theme modifier |
.auth-theme-dark | Dark theme modifier |
.auth-form-group | Input field wrapper |
.auth-input-wrapper | Input with password toggle |
.auth-password-toggle | Show/hide password button |
.auth-submit-btn | Submit button |
.auth-error | Error message display |
.auth-success | Success message display |
.auth-toggle | Login/signup toggle link |
.auth-branding | Footer branding |
Your app must handle 401 errors and refresh tokens. The Cognito SDK handles this automatically if you use getSession().
async function getValidToken() {
const ACI = window.AmazonCognitoIdentity;
if (!ACI) return localStorage.getItem('auth_token');
// Get your app's Cognito config
const configRes = await fetch('https://authreturn.com/api/apps/YOUR_APP_ID/cognito');
const config = await configRes.json();
const userPool = new ACI.CognitoUserPool({
UserPoolId: config.userPoolId,
ClientId: config.clientId
});
const cognitoUser = userPool.getCurrentUser();
if (!cognitoUser) return null;
return new Promise((resolve) => {
cognitoUser.getSession((err, session) => {
if (err || !session?.isValid()) {
resolve(null);
return;
}
const newToken = session.getIdToken().getJwtToken();
localStorage.setItem('auth_token', newToken);
resolve(newToken);
});
});
}
async function apiCall(url, options = {}) {
const token = await getValidToken();
if (!token) {
showAuthModal('login');
throw new Error('Not authenticated');
}
const res = await fetch(url, {
...options,
headers: {
...options.headers,
'Authorization': `Bearer ${token}`
}
});
if (res.status === 401) {
// Token expired and couldn't refresh
localStorage.removeItem('auth_token');
localStorage.removeItem('user_email');
window.dispatchEvent(new CustomEvent('authreturn:authchange'));
showAuthModal('login');
throw new Error('Session expired');
}
return res;
}
Verify Cognito JWTs on your backend using the JWKS endpoint:
import jwt
import requests
from functools import lru_cache
@lru_cache(maxsize=1)
def get_cognito_keys(region, user_pool_id):
url = f'https://cognito-idp.{region}.amazonaws.com/{user_pool_id}/.well-known/jwks.json'
return requests.get(url).json()['keys']
def verify_cognito_token(token, region, user_pool_id, client_id):
keys = get_cognito_keys(region, user_pool_id)
# Get the key ID from the token header
header = jwt.get_unverified_header(token)
key = next((k for k in keys if k['kid'] == header['kid']), None)
if not key:
raise ValueError('Key not found')
# Construct the public key and verify
public_key = jwt.algorithms.RSAAlgorithm.from_jwk(key)
payload = jwt.decode(
token,
public_key,
algorithms=['RS256'],
audience=client_id,
issuer=f'https://cognito-idp.{region}.amazonaws.com/{user_pool_id}'
)
return payload # Contains 'email', 'sub', etc.
The auth component includes a "Forgot password?" link that triggers Cognito's password reset flow. Users receive an email with a verification code.
By default, password reset emails come from AWS. Contact us to configure emails from your own domain via SES.
Your Cognito user pool client needs SRP auth enabled. Contact AuthReturn support.
Ensure you're using the correct userPoolId and clientId from /api/apps/{id}/cognito.
The auth component makes requests directly to AWS Cognito, which handles CORS. If you see CORS errors, check your network - the Cognito SDK may not be loaded.
See Architecture Overview for how AuthReturn works under the hood.