Passwords are the weakest link in account security. They can be phished, leaked, reused, or forgotten. That’s why we’ve added support for WebAuthn passkeys - a phishing-resistant, passwordless authentication method that’s both more secure and more convenient.

What Are Passkeys?

Passkeys are cryptographic credentials stored on your device (phone, laptop, security key) that replace passwords for authentication. They use the WebAuthn standard (part of FIDO2) and provide several advantages:

  • Phishing-resistant - Passkeys are bound to the website’s domain, so they can’t be used on fake sites
  • No secrets to leak - Only the public key is stored on the server
  • Device-bound - The private key never leaves your device
  • Biometric-protected - Typically secured by Face ID, Touch ID, or Windows Hello
  • Cross-device sync - Modern platforms sync passkeys across devices via iCloud, Google, or Microsoft accounts

Our Implementation

We integrated WebAuthn passkey support into our existing django-allauth authentication stack, providing both web and mobile API endpoints.

Registration Flow

When a user registers a new passkey, the flow works like this:

  1. Client requests registration options from the server
  2. Server generates a challenge with user and relying party information
  3. Browser’s WebAuthn API prompts the user (biometric or security key)
  4. Client sends credential back to the server
  5. Server verifies and stores the public key
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# API endpoint to begin registration
class WebAuthnRegistrationOptionsView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request):
        from allauth.mfa.webauthn.internal import auth as webauthn_auth

        # Generate credential creation options
        creation_options = webauthn_auth.begin_registration(
            request.user,
            passwordless=False  # Use as 2FA, not passwordless
        )

        return Response({
            "success": True,
            "creation_options": creation_options,
        })

Authentication Flow

For two-factor authentication using a passkey:

  1. User completes primary authentication (username/password or social login)
  2. Server returns challenge for WebAuthn verification
  3. Browser prompts for passkey (biometric touch)
  4. Credential is verified against stored public key
  5. User is fully authenticated
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class WebAuthnVerifyView(APIView):
    def post(self, request):
        credential = request.data.get("credential")

        # Complete authentication and verify credential
        authenticator = webauthn_auth.complete_authentication(
            request.user,
            credential
        )

        # Record usage for security monitoring
        authenticator.last_used_at = timezone.now()
        authenticator.save()

        return Response({"success": True})

Passkey Management

Users can manage their registered passkeys:

  • List all passkeys with registration date and last usage
  • Rename passkeys for easier identification (e.g., “iPhone”, “MacBook”)
  • Delete passkeys (with protection against removing the last authenticator)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
class WebAuthnListView(APIView):
    def get(self, request):
        authenticators = Authenticator.objects.filter(
            user=request.user,
            type=Authenticator.Type.WEBAUTHN
        ).order_by("-created_at")

        return Response({
            "passkeys": [
                {
                    "id": auth.pk,
                    "name": auth.wrap().name,
                    "created_at": auth.created_at.isoformat(),
                    "last_used_at": auth.last_used_at.isoformat() if auth.last_used_at else None,
                }
                for auth in authenticators
            ]
        })

API Endpoints

Our REST API provides full WebAuthn support for the mobile app:

EndpointMethodDescription
/api/webauthn/register/options/GETGet registration challenge
/api/webauthn/register/complete/POSTComplete registration
/api/webauthn/authenticate/options/GETGet authentication challenge
/api/webauthn/verify/POSTVerify credential for 2FA
/api/webauthn/GETList registered passkeys
/api/webauthn/<id>/PATCHRename a passkey
/api/webauthn/<id>/DELETEDelete a passkey

Security Considerations

Recovery Codes

When a user registers their first authenticator (passkey or TOTP), we automatically generate recovery codes. These one-time-use codes can be used if all other authentication methods become unavailable:

1
2
if Authenticator.objects.filter(user=request.user).count() == 1:
    auto_generate_recovery_codes(request._request)

Preventing Lockout

Users cannot delete their last passkey if it would leave them without any two-factor authentication method:

1
2
3
4
if not adapter.can_delete_authenticator(authenticator):
    return Response({
        "message": "Cannot delete - you must have at least one authentication method"
    }, status=400)

Monitoring

We track passkey usage via Datadog metrics for security monitoring:

1
2
3
increment("webauthn.registration.success")
increment("webauthn.verify.success")
increment("webauthn.verify.failed")

Browser Support

WebAuthn is supported in all modern browsers:

  • Chrome 67+
  • Firefox 60+
  • Safari 13+
  • Edge 79+
  • Mobile Chrome, Safari, Firefox

For older browsers, users can still use TOTP (authenticator apps) or recovery codes.

The User Experience

Adding a passkey takes just seconds:

  1. Go to Settings → Security → Two-Factor Authentication
  2. Click Add Passkey
  3. Name your device (e.g., “iPhone 15”)
  4. Authenticate with Face ID / Touch ID / Windows Hello
  5. Done! Your passkey is registered

Future logins require just a biometric confirmation - no typing passwords or copying codes from authenticator apps.

Why We Chose Passkeys

MethodPhishing RiskUser ExperienceRecovery
PasswordsHighPoor (forgotten, weak)Email reset
SMS 2FAMedium (SIM swap)OkayPhone number
TOTP AppsLowManual code entryBackup keys
PasskeysVery LowExcellentRecovery codes

Passkeys represent the future of authentication. They’re already supported by Apple, Google, and Microsoft platforms, making them accessible to nearly all our users.


Ready to go passwordless? Enable passkeys in your security settings!