CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-aws-amplify--auth

Authentication category of AWS Amplify providing APIs and building blocks for creating authentication experiences with Amazon Cognito

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

webauthn-credentials.mddocs/

WebAuthn Credentials

Passwordless authentication using WebAuthn for biometric and security key authentication.

Associate WebAuthn Credential

Register a new WebAuthn credential (biometric or security key) for the current user.

function associateWebAuthnCredential(): Promise<void>;

Usage Example

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

try {
  await associateWebAuthnCredential();
  console.log("WebAuthn credential registered successfully");
} catch (error) {
  console.log("WebAuthn registration failed:", error.message);
}

List WebAuthn Credentials

Get all WebAuthn credentials associated with the current user.

function listWebAuthnCredentials(input?: ListWebAuthnCredentialsInput): Promise<ListWebAuthnCredentialsOutput>;

interface ListWebAuthnCredentialsInput {
  pageSize?: number;
  nextToken?: string;
}

interface ListWebAuthnCredentialsOutput {
  credentials: AuthWebAuthnCredential[];
  nextToken?: string;
}

interface AuthWebAuthnCredential {
  credentialId: string;
  friendlyCredentialName: string;
  relyingPartyId: string;
  authenticatorAttachment?: 'platform' | 'cross-platform';
  authenticatorTransports?: ('ble' | 'hybrid' | 'internal' | 'nfc' | 'usb')[];
  createdAt: Date;
}

Usage Example

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

const { credentials, nextToken } = await listWebAuthnCredentials({
  pageSize: 10
});

credentials.forEach(credential => {
  console.log(`Credential: ${credential.friendlyCredentialName}`);
  console.log(`ID: ${credential.credentialId}`);
  console.log(`Type: ${credential.authenticatorAttachment || 'unknown'}`);
  console.log(`Created: ${credential.createdAt.toLocaleDateString()}`);
  console.log(`Transports: ${credential.authenticatorTransports?.join(', ') || 'none'}`);
  console.log('---');
});

// Handle pagination
if (nextToken) {
  const nextPage = await listWebAuthnCredentials({
    pageSize: 10,
    nextToken
  });
}

Delete WebAuthn Credential

Remove a WebAuthn credential from the user's account.

function deleteWebAuthnCredential(input: DeleteWebAuthnCredentialInput): Promise<void>;

interface DeleteWebAuthnCredentialInput {
  credentialId: string;
}

Usage Example

import { deleteWebAuthnCredential, listWebAuthnCredentials } from "@aws-amplify/auth";

// Get user's credentials
const { credentials } = await listWebAuthnCredentials();

// Find credential to delete
const credentialToDelete = credentials.find(c => 
  c.friendlyCredentialName === 'Old Security Key'
);

if (credentialToDelete) {
  await deleteWebAuthnCredential({
    credentialId: credentialToDelete.credentialId
  });
  console.log("WebAuthn credential deleted successfully");
}

WebAuthn Registration Flow

Complete flow for registering a new WebAuthn credential:

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

class WebAuthnManager {
  async registerCredential(friendlyName?: string) {
    // Check browser support
    if (!this.isWebAuthnSupported()) {
      throw new Error('WebAuthn not supported in this browser');
    }
    
    try {
      // The associateWebAuthnCredential function handles the WebAuthn ceremony
      await associateWebAuthnCredential();
      
      console.log('WebAuthn credential registered successfully');
      return true;
    } catch (error: any) {
      this.handleWebAuthnError(error);
      return false;
    }
  }
  
  private isWebAuthnSupported(): boolean {
    return !!(navigator.credentials && window.PublicKeyCredential);
  }
  
  private handleWebAuthnError(error: any) {
    // Handle common WebAuthn errors
    if (error.name === 'NotSupportedError') {
      console.log('WebAuthn not supported on this device');
    } else if (error.name === 'InvalidStateError') {
      console.log('Credential already registered');
    } else if (error.name === 'NotAllowedError') {
      console.log('User cancelled WebAuthn registration');
    } else if (error.name === 'AbortError') {
      console.log('WebAuthn registration timed out');
    } else {
      console.log('WebAuthn registration failed:', error.message);
    }
  }
}

// Usage
const webAuthnManager = new WebAuthnManager();

const handleRegisterWebAuthn = async () => {
  const success = await webAuthnManager.registerCredential('My Security Key');
  
  if (success) {
    // Update UI to show new credential
    window.location.reload();
  }
};

WebAuthn Authentication Flow

WebAuthn credentials are used during the sign-in process:

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

// Sign in process with WebAuthn
const { isSignedIn, nextStep } = await signIn({
  username: "user@example.com",
  // No password needed for WebAuthn
});

if (!isSignedIn && nextStep.signInStep === 'CONFIRM_SIGN_IN_WITH_WEBAUTHN_CREDENTIAL') {
  // User needs to provide WebAuthn credential
  console.log("Please use your security key or biometric");
  
  try {
    // The confirmSignIn will trigger WebAuthn ceremony
    // No challengeResponse needed for WebAuthn
    const { isSignedIn: webAuthnComplete } = await confirmSignIn({
      challengeResponse: '' // Empty for WebAuthn
    });
    
    if (webAuthnComplete) {
      console.log("Signed in with WebAuthn successfully");
    }
  } catch (error) {
    console.log("WebAuthn authentication failed:", error);
  }
}

Credential Management UI

Building a credential management interface:

import { 
  listWebAuthnCredentials, 
  deleteWebAuthnCredential, 
  associateWebAuthnCredential 
} from "@aws-amplify/auth";

class CredentialManager {
  async buildCredentialList() {
    const { credentials } = await listWebAuthnCredentials();
    
    return credentials.map(credential => ({
      id: credential.credentialId,
      name: credential.friendlyCredentialName,
      type: this.getCredentialTypeDescription(credential),
      created: credential.createdAt.toLocaleDateString(),
      
      async delete() {
        const confirm = window.confirm(`Remove ${this.name}?`);
        if (confirm) {
          await deleteWebAuthnCredential({ credentialId: credential.credentialId });
          console.log(`Credential ${this.name} removed`);
        }
      }
    }));
  }
  
  private getCredentialTypeDescription(credential: AuthWebAuthnCredential): string {
    const attachment = credential.authenticatorAttachment;
    const transports = credential.authenticatorTransports || [];
    
    if (attachment === 'platform') {
      return 'Built-in (Face ID, Touch ID, Windows Hello)';
    } else if (attachment === 'cross-platform') {
      if (transports.includes('usb')) {
        return 'USB Security Key';
      } else if (transports.includes('nfc')) {
        return 'NFC Security Key';
      } else if (transports.includes('ble')) {
        return 'Bluetooth Security Key';
      } else {
        return 'External Security Key';
      }
    }
    
    return 'Security Key';
  }
  
  async addNewCredential() {
    try {
      await associateWebAuthnCredential();
      console.log('New credential added successfully');
      return true;
    } catch (error) {
      console.log('Failed to add credential:', error);
      return false;
    }
  }
}

// Usage in React component or similar
const credentialManager = new CredentialManager();

const credentials = await credentialManager.buildCredentialList();
credentials.forEach(credential => {
  console.log(`${credential.name} (${credential.type}) - Added: ${credential.created}`);
});

Browser Compatibility

WebAuthn support varies across browsers and platforms:

class WebAuthnSupport {
  static check(): {
    isSupported: boolean;
    canCreateCredentials: boolean;
    canUseCredentials: boolean;
    platformSupport: string[];
  } {
    const isSupported = !!(
      window.PublicKeyCredential && 
      navigator.credentials && 
      navigator.credentials.create
    );
    
    const platformSupport: string[] = [];
    
    // Check platform-specific support
    if (typeof window !== 'undefined') {
      if ('ontouchstart' in window) {
        platformSupport.push('touch');
      }
      
      if (navigator.userAgent.includes('iPhone') || navigator.userAgent.includes('iPad')) {
        platformSupport.push('faceid', 'touchid');
      } else if (navigator.userAgent.includes('Mac')) {
        platformSupport.push('touchid');
      } else if (navigator.userAgent.includes('Windows')) {
        platformSupport.push('windows-hello');
      }
    }
    
    return {
      isSupported,
      canCreateCredentials: isSupported,
      canUseCredentials: isSupported,
      platformSupport
    };
  }
  
  static getUnavailabilityReason(): string | null {
    if (!window.PublicKeyCredential) {
      return 'WebAuthn not supported in this browser';
    }
    
    if (!navigator.credentials) {
      return 'Credentials API not available';
    }
    
    if (window.location.protocol !== 'https:' && window.location.hostname !== 'localhost') {
      return 'WebAuthn requires HTTPS';
    }
    
    return null;
  }
}

// Usage
const support = WebAuthnSupport.check();
if (!support.isSupported) {
  const reason = WebAuthnSupport.getUnavailabilityReason();
  console.log('WebAuthn unavailable:', reason);
} else {
  console.log('WebAuthn supported with:', support.platformSupport);
}

Security Best Practices

User Education

  • Explain what WebAuthn credentials are and their benefits
  • Guide users through the registration process
  • Provide clear instructions for different credential types
  • Explain backup and recovery options

Implementation Guidelines

  • Always check browser support before attempting WebAuthn operations
  • Provide fallback authentication methods
  • Handle user cancellation gracefully
  • Store minimal credential metadata
  • Implement proper error handling and user feedback

Error Handling

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

try {
  await associateWebAuthnCredential();
} catch (error) {
  if (error instanceof AuthError) {
    switch (error.name) {
      case 'NotAuthorizedException':
        console.log('User not signed in');
        break;
      case 'InvalidParameterException':
        console.log('Invalid WebAuthn parameters');
        break;
      case 'ResourceNotFoundException':
        console.log('WebAuthn not configured for this user pool');
        break;
      default:
        console.log('WebAuthn operation failed:', error.message);
    }
  } else {
    // Handle WebAuthn-specific errors
    switch (error.name) {
      case 'NotSupportedError':
        console.log('WebAuthn not supported');
        break;
      case 'InvalidStateError':
        console.log('Credential already exists');
        break;
      case 'NotAllowedError':
        console.log('User cancelled or timeout');
        break;
      case 'AbortError':
        console.log('Operation aborted');
        break;
      default:
        console.log('WebAuthn error:', error.message);
    }
  }
}

docs

authentication-lifecycle.md

device-management.md

index.md

multi-factor-authentication.md

oauth-social-authentication.md

password-management.md

server-side-apis.md

session-management.md

user-management.md

webauthn-credentials.md

tile.json