Skip to main content
EVMSolana

Overview

There are three ways to implement authentication in your application, each offering different levels of customization and control:
  1. AuthButton component from @coinbase/cdp-react: Pre-built UI component (fastest integration)
  2. React hooks from @coinbase/cdp-hooks: For custom React UIs with state management
  3. Direct methods from @coinbase/cdp-core: For vanilla JavaScript/TypeScript or non-React frameworks
Important authentication considerations:
  • Always check if a user is already signed in before starting a new authentication flow. Attempting to call verifyEmailOTP or ‘verifySMSOTP` while a user is already authenticated will result in an error and may leave the application in an inconsistent state.
  • To sign out users, use the signOut() method from @coinbase/cdp-core, the useSignOut() hook from @coinbase/cdp-hooks, or the AuthButton component which handles sign out automatically.

AuthButton component (simplest)

For the fastest integration, @coinbase/cdp-react provides a pre-built AuthButton component that handles the entire authentication flow with a single line of code.
For more CDP React components and styling options, see the React Components documentation.By default, email authentication is the only method enabled. For enabling additional methods, refer to the AppConfig documentation
import { type Config, CDPReactProvider } from "@coinbase/cdp-react";
import { AuthButton } from "@coinbase/cdp-react/components/AuthButton";

const config: Config = {
  projectId: "your-project-id",
  ethereum: {
    createOnLogin: "eoa",
  },
  appName: "React Library Demo",
  appLogoUrl: "https://picsum.photos/64",
  // Enabled authentication methods
  authMethods: ["email", "sms", "oauth:google"],
};

function App() {
  return (
    <CDPReactProvider config={config}>
      <YourApp />
    </CDPReactProvider>
  );
}
The AuthButton component automatically:
  • Shows “Sign In” when the user is not authenticated
  • Shows “Sign Out” when the user is authenticated
  • Handles the entire Email or SMS OTP flow internally
  • Manages loading and error states
  • Follows your theme configuration
This is the recommended approach for most applications that want a quick, production-ready authentication experience.

React hooks

For React applications, @coinbase/cdp-hooks provides convenient hooks that handle state management and re-renders automatically.
import { CDPHooksProvider } from "@coinbase/cdp-hooks";
import { type Config } from "@coinbase/cdp-core";

const config: Config = {
  projectId: "your-project-id",
  ethereum: {
    createOnLogin: "eoa",
  },
};

// Wrap your app with the provider
function App() {
  return (
    <CDPHooksProvider config={config}>
      <YourApp />
    </CDPHooksProvider>
  );
}
The React hooks automatically handle loading states, error states, and re-renders when authentication state changes. They’re the recommended approach for React applications.

Direct methods

The @coinbase/cdp-core package provides the low-level authentication primitives for maximum control over the user experience. This approach is ideal for non-React applications or when you need fine-grained control.
import { initialize, signInWithEmail, verifyEmailOTP } from '@coinbase/cdp-core';

// Step 1: Initialize the CDP SDK
await initialize({
  projectId: 'your-project-id'
});

// Step 2: Initiate email authentication
const { flowId, message } = await signInWithEmail({
  email: 'user@example.com'
});
console.log(message); // "OTP sent to user@example.com"

// Step 3: Verify the OTP code
const { user, isNewUser } = await verifyEmailOTP({
  flowId,
  otp: '123456'
});

// User is now authenticated and has access to their wallet
console.log('User ID:', user.userId);
console.log('EVM Addresses:', user.evmAccounts);
console.log('Is new user:', isNewUser);
Always handle authentication errors gracefully. Common errors include:
  • Invalid or expired OTP codes
  • Rate limiting for too many attempts
  • Network connectivity issues
  • Invalid project configuration

Server-side validation

Some developers take additional action (fetching additional data, starting asynchronous processes) based on a user having an active session. For security reasons, it is important that you check authentication status by validating the access token Coinbase grants a user when they log in.
import { useGetAccessToken, useIsSignedIn } from "@coinbase/cdp-hooks";
import { useEffect, useState } from "react";

export default function useServerSideAuth() {
  const { isSignedIn } = useIsSignedIn();
  const { getAccessToken } = useGetAccessToken();
  const [isServerSideAuthenticated, setIsServerSideAuthenticated] = useState<boolean>(false);

  // When the user signs in, we need to check if the user is authenticated on the server side.
  useEffect(() => {
    async function checkAuth() {
      if (!isSignedIn) {
        return;
      }
      // Retrieve the access token
      const accessToken = await getAccessToken();

      // Send the access token to your server to check if the user is authenticated.
      const response = await fetch("/api/check-auth", {
        method: "POST",
        body: JSON.stringify({
          accessToken,
        }),
      });
      const { isAuthenticated, endUser, error } = await response.json();
      if (isAuthenticated) {
        setIsServerSideAuthenticated(true);
        console.log("endUser", endUser);
      } else {
        setIsServerSideAuthenticated(false);
        console.log("error", error);
      }
    }
    void checkAuth();
  }, [isSignedIn, getAccessToken]);

  return isServerSideAuthenticated;
}
I