Authentication category of AWS Amplify providing APIs and building blocks for creating authentication experiences with Amazon Cognito
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Authentication session and token management including automatic refresh, secure storage, and AWS credentials integration.
Get the current authentication session including tokens and AWS credentials.
function fetchAuthSession(options?: FetchAuthSessionOptions): Promise<AuthSession>;
interface FetchAuthSessionOptions {
forceRefresh?: boolean;
}
interface AuthSession {
tokens?: AuthTokens;
credentials?: AWSCredentials;
identityId?: string;
userSub?: string;
}
interface AuthTokens {
accessToken: JWT;
idToken?: JWT;
refreshToken?: string;
}
interface AWSCredentials {
accessKeyId: string;
secretAccessKey: string;
sessionToken?: string;
expiration?: Date;
}import { fetchAuthSession } from "@aws-amplify/auth";
// Get current session
const session = await fetchAuthSession();
if (session.tokens) {
console.log("User is signed in");
console.log("Access token expires:", session.tokens.accessToken.payload.exp);
console.log("User ID:", session.userSub);
// Access AWS credentials for API calls
if (session.credentials) {
console.log("AWS credentials available");
console.log("Identity ID:", session.identityId);
}
} else {
console.log("User not signed in");
}
// Force token refresh
const refreshedSession = await fetchAuthSession({
forceRefresh: true
});Decode and inspect JWT tokens without verification.
function decodeJWT(token: string): JWT;
interface JWT {
header: JWTHeader;
payload: JWTPayload;
signature: string;
toString(): string;
}
interface JWTHeader {
alg: string;
typ: string;
kid?: string;
}
interface JWTPayload {
sub: string;
aud: string;
iss: string;
exp: number;
iat: number;
token_use: 'access' | 'id';
scope?: string;
[key: string]: any;
}import { fetchAuthSession, decodeJWT } from "@aws-amplify/auth";
const session = await fetchAuthSession();
if (session.tokens?.accessToken) {
const decoded = decodeJWT(session.tokens.accessToken.toString());
console.log("Token issuer:", decoded.payload.iss);
console.log("Token audience:", decoded.payload.aud);
console.log("Token expires:", new Date(decoded.payload.exp * 1000));
console.log("Token scopes:", decoded.payload.scope);
// Check if token is expired
const isExpired = decoded.payload.exp * 1000 < Date.now();
console.log("Token expired:", isExpired);
}Managing authentication state across your application:
import { fetchAuthSession, getCurrentUser } from "@aws-amplify/auth";
class SessionManager {
private session: AuthSession | null = null;
private sessionListeners: ((session: AuthSession | null) => void)[] = [];
// Initialize session on app start
async initialize() {
try {
await this.refreshSession();
} catch (error) {
console.log("No active session");
this.session = null;
this.notifyListeners();
}
}
// Get current session
async getSession(): Promise<AuthSession | null> {
if (!this.session) {
try {
await this.refreshSession();
} catch (error) {
return null;
}
}
// Check if tokens are about to expire (within 5 minutes)
if (this.session?.tokens?.accessToken) {
const expiryTime = this.session.tokens.accessToken.payload.exp * 1000;
const fiveMinutesFromNow = Date.now() + (5 * 60 * 1000);
if (expiryTime < fiveMinutesFromNow) {
try {
await this.refreshSession(true);
} catch (error) {
console.log("Token refresh failed:", error);
this.session = null;
}
}
}
return this.session;
}
// Refresh session
async refreshSession(forceRefresh = false) {
this.session = await fetchAuthSession({ forceRefresh });
this.notifyListeners();
return this.session;
}
// Check if user is authenticated
async isAuthenticated(): Promise<boolean> {
const session = await this.getSession();
return !!(session?.tokens?.accessToken);
}
// Get AWS credentials
async getCredentials(): Promise<AWSCredentials | null> {
const session = await this.getSession();
return session?.credentials || null;
}
// Listen to session changes
onSessionChange(listener: (session: AuthSession | null) => void) {
this.sessionListeners.push(listener);
// Return unsubscribe function
return () => {
const index = this.sessionListeners.indexOf(listener);
if (index > -1) {
this.sessionListeners.splice(index, 1);
}
};
}
private notifyListeners() {
this.sessionListeners.forEach(listener => {
listener(this.session);
});
}
// Clear session on sign out
clearSession() {
this.session = null;
this.notifyListeners();
}
}
// Global session manager instance
export const sessionManager = new SessionManager();
// Usage in React component
import { useEffect, useState } from 'react';
function useAuthSession() {
const [session, setSession] = useState<AuthSession | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Initialize session
sessionManager.getSession().then(session => {
setSession(session);
setLoading(false);
});
// Listen to session changes
const unsubscribe = sessionManager.onSessionChange(session => {
setSession(session);
setLoading(false);
});
return unsubscribe;
}, []);
return { session, loading, isAuthenticated: !!session?.tokens };
}Understanding different token types and their purposes:
import { fetchAuthSession, decodeJWT } from "@aws-amplify/auth";
class TokenManager {
async analyzeTokens() {
const session = await fetchAuthSession();
if (!session.tokens) {
console.log("No tokens available");
return;
}
// Access Token - for API authorization
if (session.tokens.accessToken) {
const accessToken = decodeJWT(session.tokens.accessToken.toString());
console.log("=== ACCESS TOKEN ===");
console.log("Purpose: API authorization");
console.log("Expires:", new Date(accessToken.payload.exp * 1000));
console.log("Scopes:", accessToken.payload.scope);
console.log("Client ID:", accessToken.payload.client_id);
}
// ID Token - user identity information
if (session.tokens.idToken) {
const idToken = decodeJWT(session.tokens.idToken.toString());
console.log("=== ID TOKEN ===");
console.log("Purpose: User identity");
console.log("Email:", idToken.payload.email);
console.log("Email verified:", idToken.payload.email_verified);
console.log("Name:", idToken.payload.name);
console.log("Groups:", idToken.payload['cognito:groups']);
}
// Refresh Token - for obtaining new tokens
if (session.tokens.refreshToken) {
console.log("=== REFRESH TOKEN ===");
console.log("Purpose: Token refresh");
console.log("Available: Yes");
}
}
// Extract user info from ID token
getUserInfo(): any {
const session = this.getCurrentSession();
if (session?.tokens?.idToken) {
const idToken = decodeJWT(session.tokens.idToken.toString());
return {
sub: idToken.payload.sub,
email: idToken.payload.email,
emailVerified: idToken.payload.email_verified,
name: idToken.payload.name,
givenName: idToken.payload.given_name,
familyName: idToken.payload.family_name,
groups: idToken.payload['cognito:groups'] || [],
customAttributes: this.extractCustomAttributes(idToken.payload)
};
}
return null;
}
private extractCustomAttributes(payload: any): Record<string, any> {
const customAttrs: Record<string, any> = {};
Object.keys(payload).forEach(key => {
if (key.startsWith('custom:')) {
customAttrs[key.replace('custom:', '')] = payload[key];
}
});
return customAttrs;
}
}Using session credentials with AWS SDK:
import { fetchAuthSession } from "@aws-amplify/auth";
import { S3Client, ListObjectsV2Command } from "@aws-sdk/client-s3";
class AWSIntegration {
async createS3Client(): Promise<S3Client> {
const session = await fetchAuthSession();
if (!session.credentials) {
throw new Error("No AWS credentials available");
}
return new S3Client({
region: 'us-east-1',
credentials: {
accessKeyId: session.credentials.accessKeyId,
secretAccessKey: session.credentials.secretAccessKey,
sessionToken: session.credentials.sessionToken
}
});
}
async listS3Objects(bucket: string) {
const s3Client = await this.createS3Client();
const command = new ListObjectsV2Command({
Bucket: bucket,
MaxKeys: 10
});
try {
const response = await s3Client.send(command);
return response.Contents || [];
} catch (error) {
console.log("S3 operation failed:", error);
throw error;
}
}
// Generic AWS API call helper
async withAWSCredentials<T>(
operation: (credentials: AWSCredentials) => Promise<T>
): Promise<T> {
const session = await fetchAuthSession();
if (!session.credentials) {
throw new Error("No AWS credentials available");
}
return operation(session.credentials);
}
}
// Usage
const awsIntegration = new AWSIntegration();
// List S3 objects
const objects = await awsIntegration.listS3Objects('my-bucket');
// Use with any AWS SDK client
await awsIntegration.withAWSCredentials(async (credentials) => {
const dynamoClient = new DynamoDBClient({
region: 'us-east-1',
credentials: {
accessKeyId: credentials.accessKeyId,
secretAccessKey: credentials.secretAccessKey,
sessionToken: credentials.sessionToken
}
});
// Perform DynamoDB operations
});Handle session-related errors gracefully:
import { fetchAuthSession, AuthError } from "@aws-amplify/auth";
try {
const session = await fetchAuthSession();
} catch (error) {
if (error instanceof AuthError) {
switch (error.name) {
case 'NotAuthorizedException':
console.log('User not signed in');
// Redirect to sign-in
break;
case 'UserNotConfirmedException':
console.log('User email not confirmed');
// Redirect to confirmation
break;
case 'PasswordResetRequiredException':
console.log('Password reset required');
// Redirect to password reset
break;
case 'UserNotFoundException':
console.log('User not found');
// Clear stored credentials
break;
case 'NetworkError':
console.log('Network error occurred');
// Show offline message
break;
default:
console.log('Session error:', error.message);
}
}
}
// Handle token refresh failures
try {
await fetchAuthSession({ forceRefresh: true });
} catch (error) {
if (error instanceof AuthError && error.name === 'NotAuthorizedException') {
// Refresh token expired, user needs to sign in again
console.log('Session expired, please sign in again');
// Clear local session and redirect to sign-in
}
}