Social Login with Multi-Factor Authentication

This guide shows you how to implement social login authentication when a user has multi-factor authentication (MFA) enabled.

Overview

When a user has MFA enabled using an authenticator app (TOTP), the authentication process for social login works as follows:

  1. The user initiates authentication with a social provider (Google, Facebook, etc.)
  2. After successful authentication with the provider
  3. The user is prompted to enter the 6-digit TOTP code from their authenticator app (Google Authenticator, Authy, etc.) on the OAuth callback page

The key advantage of social login with MFA is that the authenticator app verification happens automatically in the OAuth callback flow—your application code doesn’t require special handling beyond enabling MFA.

Implementation

Step 1: Set Up Social Login

First, implement social login as described in the Social Login Authentication guide. You can use either the pre-built UI components or custom UI approach.

Step 2: Implement MFA Setup for Users

Before users can authenticate with social login + MFA, they need to set up an authenticator app for their account. Implement the MFA setup flow as described in the Setting Up Multi-Factor Authentication guide.

Key implementation points:

  • Users need to be authenticated before they can set up MFA
  • Provide a UI for users to add an authenticator app to their account
  • Guide users through the QR code scanning process
  • Verify the setup with a 6-digit code from their authenticator app

Once users have set up their authenticator app, they’ll be prompted for the 6-digit code during subsequent social login attempts.

Step 3: No Additional Code Required

When a user with MFA enabled attempts to authenticate using social login:

  1. They’ll be redirected to the social provider’s login page
  2. After successfully authenticating with the provider
  3. The OAuth callback page will automatically prompt them for their authenticator app code
  4. Once verified, they’ll be redirected back to your application as a fully authenticated user
import { 
function useAuthenticate(mutationArgs?: UseAuthenticateMutationArgs): UseAuthenticateResult

Hook that provides functions and state for authenticating a user using a signer. It includes methods for both synchronous and asynchronous mutations. Useful if building your own UI components and want to control the authentication flow. For authenticate vs authenticateAsync, use authenticate when you want the hook the handle state changes for you, authenticateAsync when you need to wait for the result to finish processing.

This can be complex for magic link or OTP flows: OPT calls authenticate twice, but this should be handled by the signer.

useAuthenticate
} from "@account-kit/react";
// Your social login implementation doesn't change const {
const authenticate: UseMutateFunction<User, Error, AuthParams, unknown>
authenticate
} =
function useAuthenticate(mutationArgs?: UseAuthenticateMutationArgs): UseAuthenticateResult

Hook that provides functions and state for authenticating a user using a signer. It includes methods for both synchronous and asynchronous mutations. Useful if building your own UI components and want to control the authentication flow. For authenticate vs authenticateAsync, use authenticate when you want the hook the handle state changes for you, authenticateAsync when you need to wait for the result to finish processing.

This can be complex for magic link or OTP flows: OPT calls authenticate twice, but this should be handled by the signer.

useAuthenticate
();
// For popup flow const
const handleGoogleLogin: () => void
handleGoogleLogin
= () => {
const authenticate: (variables: AuthParams, options?: MutateOptions<User, Error, AuthParams, unknown> | undefined) => void
authenticate
(
{
type: "oauth"
type
: "oauth",
authProviderId: "google"
authProviderId
: "google",
mode: "popup"
mode
: "popup",
}, {
MutateOptions<User, Error, AuthParams, unknown>.onSuccess?: ((data: User, variables: AuthParams, context: unknown) => void) | undefined
onSuccess
: () => {
// Authentication successful! // If MFA was required, it was handled automatically in the oauth flow },
MutateOptions<User, Error, AuthParams, unknown>.onError?: ((error: Error, variables: AuthParams, context: unknown) => void) | undefined
onError
: (
error: Error
error
) => {
// Handle error }, }, ); };

Step 4: Testing the Flow

To test the complete authentication flow:

  1. Set up multi-factor authentication for a user using an authenticator app (see Setting Up Multi-Factor Authentication)
  2. Log out the user
  3. Attempt to log in using social login
  4. After authenticating with the social provider, the user should be prompted for their authenticator app code
  5. After entering the correct code, the user should be fully authenticated