or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

authentication-lifecycle.mddevice-management.mdindex.mdmulti-factor-authentication.mdoauth-social-authentication.mdpassword-management.mdserver-side-apis.mdsession-management.mduser-management.mdwebauthn-credentials.md
tile.json

device-management.mddocs/

Device Management

Trusted device tracking and management for enhanced security and user experience.

Fetch Devices

Get a list of all devices associated with the current user.

function fetchDevices(): Promise<FetchDevicesOutput>;

interface FetchDevicesOutput {
  devices: AWSAuthDevice[];
}

interface AWSAuthDevice {
  id: string;
  name?: string;
  attributes?: Record<string, string>;
  createDate?: Date;
  lastModifiedDate?: Date;
  lastAccessedDate?: Date;
}

Usage Example

import { fetchDevices } from "@aws-amplify/auth";

const { devices } = await fetchDevices();

devices.forEach(device => {
  console.log(`Device ID: ${device.id}`);
  console.log(`Device Name: ${device.name || 'Unknown'}`);
  console.log(`Last Accessed: ${device.lastAccessedDate}`);
  console.log(`Attributes:`, device.attributes);
  console.log('---');
});

Remember Device

Mark the current device as trusted/remembered to skip MFA on future sign-ins.

function rememberDevice(): Promise<void>;

Usage Example

import { rememberDevice } from "@aws-amplify/auth";

// After successful sign-in with MFA, allow user to remember device
const shouldRememberDevice = confirm("Remember this device for future sign-ins?");

if (shouldRememberDevice) {
  await rememberDevice();
  console.log("Device marked as trusted");
}

Forget Device

Remove a device from the trusted devices list, requiring MFA on next sign-in.

function forgetDevice(input?: ForgetDeviceInput): Promise<void>;

interface ForgetDeviceInput {
  device?: {
    id: string;
  };
}

Usage Example

import { forgetDevice, fetchDevices } from "@aws-amplify/auth";

// Forget current device
await forgetDevice();
console.log("Current device forgotten");

// Forget a specific device
const { devices } = await fetchDevices();
const deviceToForget = devices.find(d => d.name === 'Old Phone');

if (deviceToForget) {
  await forgetDevice({
    device: { id: deviceToForget.id }
  });
  console.log(`Device ${deviceToForget.name} forgotten`);
}

Device Management Flow

Initial Device Setup

When a user signs in from a new device with MFA enabled:

import { signIn, confirmSignIn, rememberDevice } from "@aws-amplify/auth";

// Sign in process
const { isSignedIn, nextStep } = await signIn({
  username: "user@example.com",
  password: "MyPassword123!"
});

if (!isSignedIn && nextStep.signInStep === 'CONFIRM_SIGN_IN_WITH_SMS_CODE') {
  // User enters MFA code
  const mfaCode = "123456"; // from user input
  
  const { isSignedIn: mfaComplete } = await confirmSignIn({
    challengeResponse: mfaCode
  });
  
  if (mfaComplete) {
    // Offer to remember device
    const shouldRemember = confirm("Remember this device to skip MFA next time?");
    
    if (shouldRemember) {
      await rememberDevice();
    }
  }
}

Device Management UI

Building a device management interface:

import { fetchDevices, forgetDevice } from "@aws-amplify/auth";

async function buildDeviceManager() {
  const { devices } = await fetchDevices();
  
  const deviceList = devices.map(device => ({
    id: device.id,
    name: device.name || `Device ${device.id.slice(-4)}`,
    lastAccessed: device.lastAccessedDate?.toLocaleDateString(),
    isCurrentDevice: device.id === getCurrentDeviceId(), // Your implementation
    
    async forget() {
      await forgetDevice({ device: { id: device.id } });
      console.log(`Device ${this.name} removed from trusted devices`);
    }
  }));
  
  return deviceList;
}

// Usage in React component or similar
const devices = await buildDeviceManager();
devices.forEach(device => {
  console.log(`${device.name} - Last accessed: ${device.lastAccessed}`);
});

Device Attributes

Device attributes provide additional information about each device:

// Common device attributes
interface DeviceAttributes {
  'device_name'?: string;           // User-friendly device name
  'device_key'?: string;            // Unique device identifier
  'device_group_key'?: string;      // Device group identifier
  'device_remembered_status'?: string; // 'remembered' | 'not_remembered'
  [key: string]: string | undefined;  // Custom attributes
}

// Example of working with device attributes
const { devices } = await fetchDevices();
const currentDevice = devices[0];

if (currentDevice.attributes) {
  const deviceName = currentDevice.attributes['device_name'] || 'Unknown Device';
  const isRemembered = currentDevice.attributes['device_remembered_status'] === 'remembered';
  
  console.log(`Device: ${deviceName}, Remembered: ${isRemembered}`);
}

Security Best Practices

Device Trust Policy

// Example device trust management
class DeviceTrustManager {
  private readonly MAX_TRUSTED_DEVICES = 5;
  private readonly TRUST_DURATION_DAYS = 30;
  
  async manageTrustedDevices() {
    const { devices } = await fetchDevices();
    
    // Remove old trusted devices
    const oldDevices = devices.filter(device => {
      const lastAccessed = device.lastAccessedDate;
      if (!lastAccessed) return true;
      
      const daysSinceAccess = (Date.now() - lastAccessed.getTime()) / (1000 * 60 * 60 * 24);
      return daysSinceAccess > this.TRUST_DURATION_DAYS;
    });
    
    // Forget old devices
    for (const device of oldDevices) {
      await forgetDevice({ device: { id: device.id } });
      console.log(`Removed stale device: ${device.name}`);
    }
    
    // Check device limit
    const remainingDevices = devices.length - oldDevices.length;
    if (remainingDevices >= this.MAX_TRUSTED_DEVICES) {
      console.warn('Maximum trusted devices reached');
      return false;
    }
    
    return true;
  }
  
  async rememberCurrentDevice() {
    const canTrust = await this.manageTrustedDevices();
    
    if (canTrust) {
      await rememberDevice();
      return true;
    }
    
    return false;
  }
}

Error Handling

import { fetchDevices, forgetDevice, AuthError } from "@aws-amplify/auth";

try {
  const { devices } = await fetchDevices();
} catch (error) {
  if (error instanceof AuthError) {
    switch (error.name) {
      case 'NotAuthorizedException':
        console.log('User not signed in');
        break;
      case 'ResourceNotFoundException':
        console.log('User pool not found');
        break;
      case 'InvalidParameterException':
        console.log('Invalid request parameters');
        break;
      default:
        console.log('Failed to fetch devices:', error.message);
    }
  }
}

try {
  await forgetDevice({ device: { id: 'invalid-device-id' } });
} catch (error) {
  if (error instanceof AuthError) {
    switch (error.name) {
      case 'ResourceNotFoundException':
        console.log('Device not found');
        break;
      case 'InvalidParameterException':
        console.log('Invalid device ID');
        break;
      default:
        console.log('Failed to forget device:', error.message);
    }
  }
}

Integration with MFA

Device remembering works in conjunction with MFA:

  • New Device: Requires MFA + option to remember
  • Remembered Device: Skips MFA challenge
  • Forgotten Device: Requires MFA again
  • Device Limit: Configurable per User Pool

This provides a balance between security and user experience, allowing trusted devices to bypass MFA while maintaining security for unknown devices.