Edit on GitHub

Two-Factor Authentication

Two-Factor Authentication (2FA) adds an extra layer of security to your authentication system by requiring users to provide two different authentication factors to verify their identity.

Overview

Svelte Guardian supports Time-Based One-Time Password (TOTP) two-factor authentication, commonly used with authenticator apps like Google Authenticator, Authy, and Microsoft Authenticator.

Configuration

Configure 2FA in your Svelte Guardian setup:

const { handle, signIn, signOut } = await guardianAuth({ // Other configuration... security: { twoFactorAuth: { method: 'totp', // Currently only TOTP is supported allowBackupCodes: true, // Enable backup codes for account recovery backupCodeCount: 8, // Number of backup codes to generate issuer: 'Your App Name', // The name shown in authenticator apps digits: 6, // Number of digits in the OTP (default: 6) period: 30, // How long each code is valid in seconds (default: 30) algorithm: 'sha1' // Algorithm used (default: sha1) } } });

Implementation

Enabling 2FA for Users

2FA setup typically involves these steps:

  1. Generate a secret for the user
  2. Display the QR code to the user
  3. Verify the user can generate valid codes
  4. Save the verified secret to the user’s account
  5. Generate backup codes (optional)

Server-Side Implementation

Svelte Guardian provides endpoints for handling 2FA setup and verification:

  • POST:/auth/2fa/generate: Generates a new TOTP secret
  • POST:/auth/2fa/verify: Verifies a TOTP code and enables 2FA
  • POST:/auth/2fa/disable: Disables 2FA for a user
  • POST:/auth/2fa/backup-codes: Generates backup codes

Here’s a server-side example showing how to handle these requests:

// src/routes/api/2fa/setup/+server.ts import { json } from '@sveltejs/kit'; import { twoFactorAuth } from 'svelte-guardian/server'; export async function POST({ locals }) { // Get the authenticated user const session = await locals.auth(); if (!session?.user) { return json({ error: 'Unauthorized' }, { status: 401 }); } // Generate a new TOTP secret const { secret, qrCodeUrl } = await twoFactorAuth.generateSecret({ userId: session.user.id, email: session.user.email }); // Return the secret and QR code URL (the secret will be saved only after verification) return json({ success: true, secret, qrCodeUrl }); }

Client-Side Implementation

Create a 2FA setup flow in your application:

{#if step === 'intro'}

Set Up Two-Factor Authentication

Two-factor authentication adds an extra layer of security to your account.

{:else if step === 'scan'}

Scan QR Code

Scan this QR code with your authenticator app:

QR Code for Two-Factor Authentication

If you can't scan the QR code, enter this code manually:

{secret}

Now enter the verification code from your authenticator app:

{#if error}
{error}
{/if}
{:else if step === 'backup-codes'}

Backup Codes

Important: Save these backup codes in a secure place. They can be used to regain access to your account if you lose your authenticator device.

{#each backupCodes as code}
{code}
{/each}
Done
{/if}

Login with 2FA

When 2FA is enabled for a user, they’ll need to complete a two-step login process:

  1. Enter their credentials (email/password or OAuth)
  2. Enter the time-based code from their authenticator app

Handling 2FA During Login

Svelte Guardian automatically handles the 2FA challenge during login. When a user with 2FA enabled attempts to log in, the signIn function will return a result indicating that a 2FA code is required:

const result = await signIn('credentials', { email, password, redirect: false }); if (result.twoFactorRequired) { // Redirect to 2FA verification page goto('/verify-2fa'); } else if (result.success) { // Redirect to dashboard goto('/dashboard'); } else { // Handle error error = result.error; }

2FA Verification Page

Create a page for 2FA code verification:

Two-Factor Authentication

Enter the verification code from your authenticator app.

{#if error}
{error}
{/if}

Using Backup Codes

If a user loses access to their authenticator device, they can use one of their backup codes to log in:

Use Backup Code

Enter one of your backup codes to sign in.

{#if error}
{error}
{/if}

Note: Each backup code can only be used once. After using a backup code, you should disable 2FA and set it up again if you still don't have access to your authenticator app.

Disabling 2FA

Allow users to disable 2FA if needed:

// src/routes/api/2fa/disable/+server.ts import { json } from '@sveltejs/kit'; import { twoFactorAuth } from 'svelte-guardian/server'; export async function POST({ locals, request }) { const session = await locals.auth(); if (!session?.user) { return json({ error: 'Unauthorized' }, { status: 401 }); } const data = await request.json(); const { password } = data; // Require password re-entry for security try { const result = await twoFactorAuth.disable({ userId: session.user.id, password }); if (result.success) { return json({ success: true }); } else { return json({ error: result.error }, { status: 400 }); } } catch (error) { return json({ error: 'Failed to disable 2FA' }, { status: 500 }); } }

Security Considerations

  1. Account Recovery: Always provide backup codes or an alternative recovery method.

  2. Password Confirmation: Require password confirmation when enabling or disabling 2FA.

  3. Session Management: Invalidate other sessions when 2FA is enabled or disabled.

  4. Remember Devices (optional): Consider allowing users to trust devices to reduce 2FA prompts.

  5. Rate Limiting: Apply strict rate limits to 2FA verification endpoints.

Best Practices

  1. Clear Instructions: Provide clear guidance for users setting up 2FA.

  2. QR Code and Manual Entry: Support both QR code scanning and manual secret entry.

  3. Testing: Verify code generation before finalizing setup.

  4. Backup Codes: Generate and require users to save backup codes.

  5. Application Name: Set a clear issuer name for the authenticator app.

Share this page