Custom Social Providers with Auth0

In addition to the standard social login providers (Google, Facebook, Apple), Account Kit allows you to integrate custom OAuth providers through Auth0. This gives you flexibility to add authentication methods like GitHub, Twitter, LinkedIn, and more.

You can implement custom social providers in two ways:

Pre-built UI Components

Account Kit provides pre-built UI components that handle the entire custom social provider authentication flow with minimal code.

Step 1: Add Authentication Components to Your Page

Before configuring your authentication, first add one of the pre-built components to your application:

Using Modal Authentication

To add authentication in a modal popup:

import React from "react";
import { 
const useAuthModal: () => { isOpen: boolean; openAuthModal: () => void; closeAuthModal: () => void; }

A hook that returns the open and close functions for the Auth Modal if uiConfig is enabled on the Account Provider

useAuthModal
} from "@account-kit/react";
export default function
function MyPage(): JSX.Element
MyPage
() {
const {
const openAuthModal: () => void
openAuthModal
} =
function useAuthModal(): { isOpen: boolean; openAuthModal: () => void; closeAuthModal: () => void; }

A hook that returns the open and close functions for the Auth Modal if uiConfig is enabled on the Account Provider

useAuthModal
();
return <
React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefined
onClick
={
const openAuthModal: () => void
openAuthModal
}>Sign in</
React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
>;
}

For more details on modal configuration, see the Modal Authentication documentation.

Or:

Using Embedded Authentication

To embed authentication directly in your page:

import React from "react";
import { 
const AuthCard: (props: AuthCardProps) => JSX.Element

React component containing an Auth view with configured auth methods and options based on the config passed to the AlchemyAccountProvider

AuthCard
} from "@account-kit/react";
export default function
function MyLoginPage(): JSX.Element
MyLoginPage
() {
return ( <
React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
React.HTMLAttributes<HTMLDivElement>.className?: string | undefined
className
="flex flex-row p-4 bg-white border border-gray-200 rounded-lg">
<
const AuthCard: (props: AuthCardProps) => JSX.Element

React component containing an Auth view with configured auth methods and options based on the config passed to the AlchemyAccountProvider

AuthCard
/>
</
React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
>
); }

For more details on embedded authentication, see the Embedded Authentication documentation.

Step 2: Setting Up Auth0

Before configuring the UI components, you need to set up Auth0:

  1. Create or log in to an account on auth0.com

  2. In the Auth0 dashboard, go to “Authentication → Social” in the sidebar

  3. Click “Create Social Connection” and choose your desired provider (e.g., GitHub)

    Auth0 provider list
  4. You can either use Auth0’s dev keys for testing or add your own credentials. If you want to add your own, click the link that says “How to obtain a Client ID” and follow the directions.

  5. Select the attributes and permissions you’ll be requesting. It’s recommended to at least request the user’s email address as it can be useful for merging accounts from different providers later. Note that your users will be prompted for consent to share whatever information you request.

    Configure Github auth provider settings Auth0
  6. Note the “Name” field (e.g., “github”) - you’ll need this later for the auth0Connection parameter

  7. Enable the connection for your Auth0 application

    Auth0 app selection page
  8. From your Auth0 dashboard, go to “Applications → Applications” in the sidebar

  9. Select your application and note the “Domain”, “Client ID”, and “Client Secret”

    Settings page in Auth0 with relevant fields
  10. Add these to your Account Kit dashboard in the embedded accounts auth config

    Copy fields from Auth0 to the Alchemy accounts config

    In addition to the “Client ID” and “Client Secret” fields, you must also fill in the “Auth0 Domain” field from the Auth0 dashboard.

Step 3: Configure Custom Social Providers in UI Components

After adding the components and setting up Auth0, configure the custom social providers in your application config:

import { 
type AlchemyAccountsUIConfig = { auth?: { addPasskeyOnSignup?: boolean; header?: React.ReactNode; hideError?: boolean; onAuthSuccess?: () => void; sections: AuthType[][]; hideSignInText?: boolean; }; illustrationStyle?: "outline" | "linear" | "filled" | "flat" | undefined; modalBaseClassName?: string; supportUrl?: string | undefined; }
AlchemyAccountsUIConfig
,
const createConfig: (props: CreateConfigProps, ui?: AlchemyAccountsUIConfig) => AlchemyAccountsConfigWithUI

Wraps the createConfig that is exported from @aa-sdk/core to allow passing an additional argument, the configuration object for the Auth Components UI (the modal and AuthCard).

createConfig
} from "@account-kit/react";
import {
const sepolia: Chain
sepolia
,
function alchemy(config: AlchemyTransportConfig): AlchemyTransport

Creates an Alchemy transport with the specified configuration options. When sending all traffic to Alchemy, you must pass in one of rpcUrl, apiKey, or jwt. If you want to send Bundler and Paymaster traffic to Alchemy and Node traffic to a different RPC, you must pass in alchemyConnection and nodeRpcUrl.

alchemy
} from "@account-kit/infra";
const
const uiConfig: AlchemyAccountsUIConfig
uiConfig
:
type AlchemyAccountsUIConfig = { auth?: { addPasskeyOnSignup?: boolean; header?: React.ReactNode; hideError?: boolean; onAuthSuccess?: () => void; sections: AuthType[][]; hideSignInText?: boolean; }; illustrationStyle?: "outline" | "linear" | "filled" | "flat" | undefined; modalBaseClassName?: string; supportUrl?: string | undefined; }
AlchemyAccountsUIConfig
= {
auth?: { addPasskeyOnSignup?: boolean; header?: React.ReactNode; hideError?: boolean; onAuthSuccess?: () => void; sections: AuthType[][]; hideSignInText?: boolean; } | undefined
auth
: {
sections: AuthType[][]

Each section can contain multiple auth types which will be grouped together and separated by an OR divider

sections
: [
[ // Standard social providers {
type: "social"
type
: "social",
authProviderId: KnownAuthProvider
authProviderId
: "google",
mode: "popup"
mode
: "popup" },
// Custom social providers via Auth0 {
type: "social"
type
: "social",
authProviderId: "auth0"
authProviderId
: "auth0",
// Specify the Auth0 connection to use directly
auth0Connection?: string | undefined
auth0Connection
: "github",
displayName: string
displayName
: "GitHub",
// Custom logo URL for the provider
logoUrl: string
logoUrl
:
"https://github.githubassets.com/assets/GitHub-Mark-ea2971cee799.png", // Optional dark mode logo
logoUrlDark?: string | undefined
logoUrlDark
:
"https://github.githubassets.com/assets/GitHub-Mark-Light-ea2971cee799.png",
mode: "popup"
mode
: "popup",
}, {
type: "social"
type
: "social",
authProviderId: "auth0"
authProviderId
: "auth0",
auth0Connection?: string | undefined
auth0Connection
: "twitter",
displayName: string
displayName
: "Twitter",
logoUrl: string
logoUrl
: "https://path-to-twitter-logo.png",
mode: "popup"
mode
: "popup",
}, ], ], }, }; export const
const config: AlchemyAccountsConfigWithUI
config
=
function createConfig(props: CreateConfigProps, ui?: AlchemyAccountsUIConfig): AlchemyAccountsConfigWithUI

Wraps the createConfig that is exported from @aa-sdk/core to allow passing an additional argument, the configuration object for the Auth Components UI (the modal and AuthCard).

createConfig
(
{
transport: AlchemyTransport
transport
:
function alchemy(config: AlchemyTransportConfig): AlchemyTransport

Creates an Alchemy transport with the specified configuration options. When sending all traffic to Alchemy, you must pass in one of rpcUrl, apiKey, or jwt. If you want to send Bundler and Paymaster traffic to Alchemy and Node traffic to a different RPC, you must pass in alchemyConnection and nodeRpcUrl.

alchemy
({
apiKey: string
apiKey
: "your-api-key" }),
chain: Chain
chain
:
const sepolia: Chain
sepolia
,
// Required for popup flow
enablePopupOauth: true
enablePopupOauth
: true,
},
const uiConfig: AlchemyAccountsUIConfig
uiConfig
,
);

Auth0 custom providers accept the following configuration:

type 
type SocialAuthType = { type: "social"; authProviderId: "auth0"; auth0Connection?: string; displayName?: string; logoUrl: string; logoUrlDark?: string; mode: "popup" | "redirect"; scope?: string; claims?: string; }
SocialAuthType
= {
type: "social"
type
: "social";
// For Auth0 custom providers
authProviderId: "auth0"
authProviderId
: "auth0";
// Auth0-specific connection string (e.g., "github", "twitter")
auth0Connection?: string | undefined
auth0Connection
?: string;
// Display name for the provider button
displayName?: string | undefined
displayName
?: string;
// URL for the provider's logo
logoUrl: string
logoUrl
: string;
// URL for the provider's logo in dark mode (optional, `logoUrl` is used for both light & dark mode if not provided)
logoUrlDark?: string | undefined
logoUrlDark
?: string;
// Authentication mode (popup or redirect)
mode: "popup" | "redirect"
mode
: "popup" | "redirect";
// Optional: Specifies the requested OAuth scope
scope?: string | undefined
scope
?: string;
// Optional: Specifies additional claims to be included in the authentication token
claims?: string | undefined
claims
?: string;
};

You can find the full type definition in the Account Kit source code.

For more details on UI component customization, see the UI Components documentation.

Custom UI

If you need complete control over the user experience, you can implement your own custom UI for custom social providers using Account Kit hooks.

Step 1: Set Up Auth0

Before implementing in your React app, you need to configure Auth0 as described in the Setting Up Auth0 section above.

Step 2: Implement Authentication in Your React App

Use the useAuthenticate hook to implement Auth0 authentication:

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";
// Inside your component 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
();
// Option 1: Generic Auth0 login (shows Auth0 provider selection screen) const
const handleAuth0Login: () => void
handleAuth0Login
= () => {
const authenticate: (variables: AuthParams, options?: MutateOptions<User, Error, AuthParams, unknown> | undefined) => void
authenticate
(
{
type: "oauth"
type
: "oauth",
authProviderId: "auth0"
authProviderId
: "auth0",
mode: "popup"
mode
: "popup", // or "redirect"
}, {
MutateOptions<User, Error, AuthParams, unknown>.onSuccess?: ((data: User, variables: AuthParams, context: unknown) => void) | undefined
onSuccess
: () => {
// Authentication successful! },
MutateOptions<User, Error, AuthParams, unknown>.onError?: ((error: Error, variables: AuthParams, context: unknown) => void) | undefined
onError
: (
error: Error
error
) => {
// Handle error }, }, ); }; // Option 2: Direct provider login (bypasses Auth0 selection screen) const
const handleGitHubLogin: () => void
handleGitHubLogin
= () => {
const authenticate: (variables: AuthParams, options?: MutateOptions<User, Error, AuthParams, unknown> | undefined) => void
authenticate
(
{
type: "oauth"
type
: "oauth",
authProviderId: "auth0"
authProviderId
: "auth0",
auth0Connection?: string | undefined
auth0Connection
: "github", // Use the connection name from Auth0
mode: "popup"
mode
: "popup", // or "redirect"
}, {
MutateOptions<User, Error, AuthParams, unknown>.onSuccess?: ((data: User, variables: AuthParams, context: unknown) => void) | undefined
onSuccess
: () => {
// Authentication successful! },
MutateOptions<User, Error, AuthParams, unknown>.onError?: ((error: Error, variables: AuthParams, context: unknown) => void) | undefined
onError
: (
error: Error
error
) => {
// Handle error }, }, ); };

Option 1 will take users to an Auth0 login page where they can choose the authentication method they want. Option 2 sends users directly to the specific provider’s login (like GitHub) without showing the Auth0 selection screen, which usually provides a better user experience.

The value passed to auth0Connection should match the string that appeared in the “Name” field of your auth provider connection in the Auth0 dashboard.

Step 3: Track Authentication Status

Use the useSignerStatus hook to determine if the user is authenticated:

import { 
const useSignerStatus: (override?: AlchemyAccountContextProps) => UseSignerStatusResult

Hook to get the signer status, optionally using an override configuration, useful if you’re building your own login.

useSignerStatus
} from "@account-kit/react";
// Inside your component const {
const isConnected: boolean
isConnected
} =
function useSignerStatus(override?: AlchemyAccountContextProps): UseSignerStatusResult

Hook to get the signer status, optionally using an override configuration, useful if you’re building your own login.

useSignerStatus
();
// You can use isConnected to conditionally render UI