How to fix insufficient_aal: Higher Authenticator Assurance Level required in Supabase

SupabaseINTERMEDIATEMEDIUMUpdated Dec 22, 2025ยท 277 views

Supabase returns this error when a user attempts to access a protected resource without meeting the required Multi-Factor Authentication (MFA) level. Authenticator Assurance Level (AAL) is a security concept that defines how strongly a user has authenticated, and certain operations require higher assurance levels than basic password authentication.

Quick fix

Fixes insufficient_aal

import { createClient } from '@supabase/supabase-js'

const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY)

// Get current session
const { data: { session } } = await supabase.auth.getSession()

// Check MFA factors for the user
const { data: factors } = await supabase.auth.mfa.getAuthenticatorAssuranceLevel()
console.log('Current AAL:', factors.currentLevel)
console.log('Next required AAL:', factors.nextLevel)
  1. Check if the user has MFA enabled
  2. Prompt the user to complete MFA setup or verification
  3. Configure your application to handle AAL requirements
  4. Review and adjust your Supabase RLS policies
supabase โ€” error
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY)
// Get current session
const { data: { session } } = await supabase.auth.getSession()
// Check MFA factors for the user
const { data: factors } = await supabase.auth.mfa.getAuthenticatorAssuranceLevel()
console.log('Current AAL:', factors.currentLevel)
console.log('Next required AAL:', factors.nextLevel)
insufficient_aal: Higher Authenticator Assurance Level required

What this error means

Supabase implements Authenticator Assurance Levels (AAL) as part of its Multi-Factor Authentication (MFA) system. AAL1 represents single-factor authentication (password only), while AAL2 requires multi-factor authentication (password + second factor like TOTP or SMS). When you configure certain operations or endpoints to require AAL2, Supabase will reject requests from users who have only authenticated with AAL1. This error occurs when a user tries to perform a sensitive operation (like changing account settings, accessing financial data, or modifying security configurations) without having completed MFA setup or verification.

Typical symptoms

  • API requests to protected endpoints return HTTP 401 with error code 'insufficient_aal'
  • Users cannot access certain features despite being logged in with valid credentials
  • Mobile or web apps show authentication errors when trying to perform sensitive operations
  • Supabase Auth methods like `supabase.auth.getSession()` return valid sessions but certain API calls still fail
  • Error appears in logs when users attempt actions that require higher security assurance

Common causes

  1. The user has not enabled Multi-Factor Authentication (MFA) on their account
  2. The user authenticated with only a password (AAL1) but the operation requires MFA (AAL2)
  3. The Supabase project has policies or Row Level Security (RLS) that require AAL2 for certain operations
  4. The user's MFA session has expired and needs re-authentication
  5. Custom business logic or edge functions are checking AAL levels and rejecting AAL1 sessions
  6. The application is calling endpoints that are configured to require higher assurance levels

How to fix "insufficient_aal: Higher Authenticator Assurance Level required"

1Check if the user has MFA enabled

First, verify whether the user has MFA configured. You can check this through the Supabase dashboard or by querying the user's MFA factors:

typescript
import { createClient } from '@supabase/supabase-js'

const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY)

// Get current session
const { data: { session } } = await supabase.auth.getSession()

// Check MFA factors for the user
const { data: factors } = await supabase.auth.mfa.getAuthenticatorAssuranceLevel()
console.log('Current AAL:', factors.currentLevel)
console.log('Next required AAL:', factors.nextLevel)

If the user doesn't have MFA enabled, they'll need to set it up before accessing AAL2-protected resources.

2Prompt the user to complete MFA setup or verification

If the user needs to enable MFA, guide them through the setup process:

typescript
// Start MFA enrollment
const { data, error } = await supabase.auth.mfa.enroll({
  factorType: 'totp', // or 'sms'
})

// For TOTP, show QR code to user
if (data?.totp?.qr_code) {
  // Display QR code for user to scan with authenticator app
  console.log('QR Code URL:', data.totp.qr_code)
}

// After user scans QR code and enters code:
const { error: verifyError } = await supabase.auth.mfa.verify({
  factorId: data.id,
  code: userEnteredCode,
})

// For SMS MFA:
const { data: smsData } = await supabase.auth.mfa.enroll({
  factorType: 'sms',
  phone: userPhoneNumber,
})
// User will receive SMS with code to verify

Once MFA is set up, the user will need to authenticate with both password and MFA to achieve AAL2.

3Configure your application to handle AAL requirements

Update your application logic to check AAL levels and prompt for MFA when needed:

typescript
// Before performing sensitive operations, check AAL
const { data: aalData } = await supabase.auth.mfa.getAuthenticatorAssuranceLevel()

if (aalData.currentLevel !== aalData.nextLevel) {
  // User needs to elevate authentication level
  // Redirect to MFA verification page
  window.location.href = '/auth/mfa-verify'
} else {
  // User has required AAL, proceed with operation
  await performSensitiveOperation()
}

// After MFA verification, retry the original request
const { error } = await performSensitiveOperation()
if (!error) {
  // Operation succeeded with elevated AAL
}

Consider implementing middleware or hooks that automatically check AAL requirements for protected routes.

4Review and adjust your Supabase RLS policies

If you're using Row Level Security (RLS), check if your policies require AAL2:

sql
-- Example RLS policy that requires AAL2
CREATE POLICY "Require MFA for sensitive data" ON sensitive_table
FOR ALL USING (
  auth.jwt() ->> 'aal' = 'aal2'
  AND -- other conditions
);

-- Check current policies
SELECT * FROM pg_policies
WHERE tablename = 'your_table';

You may need to adjust policies to:
1. Allow AAL1 for non-sensitive operations
2. Require AAL2 only for specific sensitive operations
3. Provide clear error messages when AAL requirements aren't met

Also check if you have any Edge Functions or custom business logic that checks AAL levels.

5Implement proper error handling and user guidance

Provide clear feedback to users when they encounter AAL errors:

typescript
try {
  await performSensitiveOperation()
} catch (error) {
  if (error.message.includes('insufficient_aal')) {
    // Show user-friendly message
    showNotification({
      title: 'Additional Verification Required',
      message: 'This action requires multi-factor authentication. Please set up or verify MFA.',
      action: {
        label: 'Set up MFA',
        onClick: () => navigateToMfaSetup()
      }
    })

    // Optionally, store the intended action to retry after MFA
    localStorage.setItem('pending_action', JSON.stringify({
      action: 'sensitive_operation',
      params: operationParams
    }))
  }
}

Make sure your UI clearly indicates which operations require MFA and provide easy paths for users to set it up.

Advanced notes

Supabase's AAL implementation follows the IETF's Authentication Context Class Reference (ACR) and Authenticator Assurance Level (AAL) concepts from standards like OpenID Connect. AAL1 corresponds to single-factor authentication, while AAL2 requires multi-factor authentication. When designing your security model, consider implementing step-up authentication: allow users to access most features with AAL1, but require AAL2 for sensitive operations. This balances security with user experience. Remember that MFA sessions have their own expiration times, and users may need to re-authenticate with MFA periodically for continued access to AAL2-protected resources.

Frequently asked questions

What does "insufficient_aal" mean?
Supabase implements Authenticator Assurance Levels (AAL) as part of its Multi-Factor Authentication (MFA) system. AAL1 represents single-factor authentication (password only), while AAL2 requires multi-factor authentication (password + second factor like TOTP or SMS). When you configure certain operations or endpoints to require AAL2, Supabase will reject requests from users who have only authenticated with AAL1.
How do I fix "insufficient_aal"?
Supabase returns this error when a user attempts to access a protected resource without meeting the required Multi-Factor Authentication (MFA) level. Authenticator Assurance Level (AAL) is a security concept that defines how strongly a user has authenticated, and certain operations require higher assurance levels than basic password authentication.
What causes "insufficient_aal"?
The user has not enabled Multi-Factor Authentication (MFA) on their account The user authenticated with only a password (AAL1) but the operation requires MFA (AAL2) The Supabase project has policies or Row Level Security (RLS) that require AAL2 for certain operations
Is the "insufficient_aal" error serious?
It is a medium-severity error โ€” more of a blocker than a risk. The steps above resolve it.

Official resources & further reading