Overview

Embedded Wallets provide secure, user-friendly authentication methods that eliminate the complexity of traditional crypto wallets. Users can access their wallets through familiar authentication patterns like email one-time passwords (OTP), without ever dealing with seed phrases or browser extensions.
Looking for the technical implementation? Check out our Quickstart Guide for step-by-step integration instructions.

Authentication methods

Email OTP

Email OTP is the primary authentication method for Embedded Wallets, providing a secure and familiar experience for users.

SMS OTP

SMS-based one-time passwords are available as an additional authentication method, providing users with more flexibility in how they access their wallets. This is currently available only for United States based phone numbers.
SMS security considerations:
  • SMS authentication is inherently vulnerable to SIM swapping attacks, where attackers can take over a user’s phone number.
  • You should weigh the convenience of logging in with SMS with the potential for a user’s wallet to be taken control of.

Implementation approaches

There are three ways to implement authentication in your application:
  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 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 AppConfig, CDPReactProvider } from "@coinbase/cdp-react";
import { AuthButton } from "@coinbase/cdp-react/components/AuthButton";
import { type Config } from "@coinbase/cdp-core";

const config: Config = {
  projectId: "your-project-id"
};

const appConfig: AppConfig = {
  name: "React Library Demo",
  logoUrl: "https://picsum.photos/64",
  // Enabled authentication methods
  authMethods: ["email", "sms"],
};

function App() {
  return (
    <CDPReactProvider app={appConfig} 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"
};

// 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

Developer-delegated authentication

This feature is in development and coming soon. This will enable applications with existing authentication systems to integrate Embedded Wallets seamlessly with their current user authentication flow.

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;
}

Best practices

Security recommendations

  1. Domain allowlisting: Always configure your allowed domains in CDP Portal
  2. HTTPS only: Never use embedded wallets on non-HTTPS sites in production
  3. Rate limiting: Implement rate limiting on your authentication endpoints
  4. Session management: Use appropriate session timeouts for your use case

State management

  1. Always check authentication state: Before starting any authentication flow, verify if the user is already signed in:
    const user = await getCurrentUser();
    if (user) {
      // User is already authenticated
      return;
    }
    
  2. Implement sign out: Provide a clear way for users to sign out:
    import { signOut } from '@coinbase/cdp-core';
    
    // Sign out the current user
    await signOut();
    
    For React applications, use the AuthButton component which handles sign out automatically, or the useSignOut hook:
    import { useSignOut } from '@coinbase/cdp-hooks';
    
    function SignOutButton() {
      const { signOut } = useSignOut();
      return <button onClick={signOut}>Sign Out</button>;
    }
    
  3. Avoid redundant verification: Don’t call verifyEmailOTP or verifySmsOTP when a user is already authenticated. This will result in an error and may leave your application in an inconsistent state.

User experience tips

  1. Clear messaging: Explain why users need to verify their email
  2. Error handling: Provide helpful error messages for common issues
  3. Loading states: Show progress during authentication steps
  4. Success feedback: Confirm when authentication is complete

Choosing the right approach