Skip to main content

Email Authentication

MoonKey enables users to login to your application with email-based one-time passcodes (OTP). With email authentication, your application can verify ownership of a user’s email address without requiring passwords.
Enable email authentication in the MoonKey Dashboard under Login Methods before implementing this feature.
For the complete and latest API reference, including all configuration options, check the @moon-key/react-auth npm package.

Using the React SDK

To authenticate your users, use the useMoonKey hook and call the start() method to initiate the authentication flow.

Import

import { useMoonKey } from '@moon-key/react-auth';

Configuration

Email OTP Configuration

You can customize the email OTP flow by passing an EmailOtpConfig object to the MoonKeyProvider:
skipVerifiedSuccess
boolean
Skip the success screen after successful verification
expiresIn
number
OTP expiration time in seconds (default: 600 = 10 minutes)
verifiedDisplayTime
number
How long to display the success message in milliseconds
verifyEmailTitle
string
Custom title for the email verification screen
verifyEmailResendTitle
string
Custom text for the resend code button
resendCodeTime
number
Time in seconds before resend is allowed (default: 60)
verifiedNewUserMessage
string
Custom message shown to new users after verification
verifiedExistingUserMessage
string
Custom message shown to existing users after verification
verifiedSuccessMessage
string
Custom success message after verification

Example Configuration

import { MoonKeyProvider } from '@moon-key/react-auth';

export default function App() {
  return (
    <MoonKeyProvider
      publishableKey="your_publishable_key"
      config={{
        loginMethods: ['email', 'google'],
        emailOtp: {
          skipVerifiedSuccess: false,
          expiresIn: 600,
          verifiedDisplayTime: 3000,
          verifyEmailTitle: 'Verify your email',
          verifyEmailResendTitle: 'Resend code',
          resendCodeTime: 60,
          verifiedNewUserMessage: 'Welcome! Your account has been created.',
          verifiedExistingUserMessage: 'Welcome back!',
          verifiedSuccessMessage: 'Email verified successfully'
        }
      }}
    >
      {/* Your app */}
    </MoonKeyProvider>
  );
}

Using the start() method

Start authentication flow

Initiate the authentication flow with email or other login methods:
start: (params?: {
  loginMethods?: ('email' | 'google' | 'apple' | 'wallet')[];
  prefill?: { type: 'email' | 'phone'; value: string };
  disableSignup?: boolean;
  walletChainType?: 'ethereum' | 'solana' | 'ethereum-or-solana';
}) => void

Parameters

loginMethods
array
Array of login methods to show. Options: 'email', 'google', 'apple', 'wallet'. If not provided, uses login methods configured in the provider.
prefill
object
Pre-fill the authentication form with an email or phone number.
  • type: Either 'email' or 'phone'
  • value: The email address or phone number to prefill
disableSignup
boolean
If true, prevents new user signups and only allows existing users to login.
walletChainType
string
Specify which wallet type(s) to support. Options: 'ethereum', 'solana', or 'ethereum-or-solana'.

Returns

void
void
Opens the authentication modal/flow. No return value.

Basic Usage

import { useMoonKey } from '@moon-key/react-auth';

export default function LoginButton() {
  const { start, isAuthenticated, user } = useMoonKey();

  if (isAuthenticated) {
    return <div>Welcome, {user.email}!</div>;
  }

  return (
    <button onClick={() => start()}>
      Login
    </button>
  );
}

Email-only login

To show only email authentication:
import { useMoonKey } from '@moon-key/react-auth';

export default function EmailOnlyLogin() {
  const { start, isAuthenticated, user } = useMoonKey();

  if (isAuthenticated) {
    return (
      <div>
        <h2>Welcome, {user.email}!</h2>
        <p>You're successfully logged in.</p>
      </div>
    );
  }

  return (
    <button onClick={() => start({ loginMethods: ['email'] })}>
      Login with Email
    </button>
  );
}

Customizing Messages and Behavior

Here’s an example with custom configuration for a better user experience:
import { MoonKeyProvider } from '@moon-key/react-auth';

export default function App() {
  return (
    <MoonKeyProvider
      publishableKey="your_publishable_key"
      config={{
        loginMethods: ['email', 'google'],
        emailOtp: {
          // Skip the success screen for faster flow
          skipVerifiedSuccess: true,
          
          // Custom title for verification
          verifyEmailTitle: 'Check your inbox',
          
          // Shorter resend time for better UX
          resendCodeTime: 30,
          
          // Custom messages
          verifiedNewUserMessage: '🎉 Welcome! Your account is ready.',
          verifiedExistingUserMessage: '👋 Welcome back!',
          verifiedSuccessMessage: '✓ Email verified'
        }
      }}
    >
      {/* Your app */}
    </MoonKeyProvider>
  );
}

Prefill email address

Prefill the email input when you know the user’s email:
import { useMoonKey } from '@moon-key/react-auth';

export default function LoginWithPrefill({ userEmail }: { userEmail: string }) {
  const { start } = useMoonKey();

  return (
    <button 
      onClick={() => start({
        loginMethods: ['email'],
        prefill: { type: 'email', value: userEmail }
      })}
    >
      Login as {userEmail}
    </button>
  );
}

Login-only (no signup)

Prevent new user signups and only allow existing users to login:
import { useMoonKey } from '@moon-key/react-auth';

export default function LoginOnly() {
  const { start } = useMoonKey();

  return (
    <button 
      onClick={() => start({
        loginMethods: ['email', 'google'],
        disableSignup: true
      })}
    >
      Login (Existing Users)
    </button>
  );
}

Multiple login methods

Show multiple authentication options:
import { useMoonKey } from '@moon-key/react-auth';

export default function MultiMethodLogin() {
  const { start } = useMoonKey();

  return (
    <button 
      onClick={() => start({
        loginMethods: ['email', 'google', 'apple', 'wallet'],
        walletChainType: 'ethereum-or-solana'
      })}
    >
      Login
    </button>
  );
}

Using the REST API

For custom implementations or backend integration, use the MoonKey REST API directly.

Send Email OTP

Send a one-time passcode to the user’s email:
curl -X POST "https://api.moonkey.fun/v1/auth/otps/email/send" \
  -H "Authorization: Bearer sk_test_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com"
  }'

Response

{
  "success": true,
  "message": "OTP sent successfully"
}

Verify Email OTP

Verify the OTP code and authenticate the user:
curl -X POST "https://api.moonkey.fun/v1/auth/otps/verify" \
  -H "Authorization: Bearer sk_test_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "code": "123456",
    "session_expires_in": 10080
  }'

Parameters

ParameterTypeRequiredDescription
emailstringYesThe user’s email address
codestringYesThe 6-digit OTP code
session_expires_innumberNoSession duration in minutes (default: 10080 = 7 days)

Response

{
  "session_token": "session_abc123...",
  "session_jwt": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "id": "user_xyz789",
    "email": "user@example.com",
    "created_at": 1704067200,
    "updated_at": 1704067200
  },
  "session": {
    "id": "session_abc123",
    "user_id": "user_xyz789",
    "expires_at": 1704672000,
    "created_at": 1704067200
  }
}

Backend Implementation Example

// Express.js backend example
import express from 'express';

const app = express();
app.use(express.json());

// Send OTP endpoint
app.post('/api/auth/send-otp', async (req, res) => {
  const { email } = req.body;

  try {
    const response = await fetch('https://api.moonkey.fun/v1/auth/otps/email/send', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.MOONKEY_SECRET_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ email })
    });

    if (!response.ok) {
      throw new Error('Failed to send OTP');
    }

    res.json({ success: true });
  } catch (error) {
    res.status(500).json({ error: 'Failed to send OTP' });
  }
});

// Verify OTP endpoint
app.post('/api/auth/verify-otp', async (req, res) => {
  const { email, code } = req.body;

  try {
    const response = await fetch('https://api.moonkey.fun/v1/auth/otps/verify', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.MOONKEY_SECRET_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        email,
        code,
        session_expires_in: 10080 // 7 days
      })
    });

    if (!response.ok) {
      return res.status(401).json({ error: 'Invalid OTP code' });
    }

    const data = await response.json();
    
    // Return session credentials to client
    res.json({
      session_token: data.session_token,
      session_jwt: data.session_jwt,
      user: data.user
    });
  } catch (error) {
    res.status(500).json({ error: 'Verification failed' });
  }
});

Configuration

Enable email authentication

  1. Log in to the MoonKey Dashboard
  2. Navigate to Login Methods
  3. Enable Email OTP
  4. Save your changes

Customize email templates

You can customize the OTP email template in the dashboard:
  1. Navigate to Email Templates
  2. Select OTP Email
  3. Customize the subject, content, and styling
  4. Preview and save

Best Practices

Security

  • Rate limiting: Implement rate limits on OTP sending to prevent abuse
  • Code expiration: OTP codes expire after 10 minutes
  • Maximum attempts: Limit verification attempts to prevent brute force
  • Secure transmission: Always use HTTPS for API calls

User Experience

  • Clear instructions: Tell users where to find the code
  • Resend option: Allow users to request a new code if needed
  • Loading states: Show loading indicators during API calls
  • Error handling: Provide clear error messages

Implementation

The MoonKey SDK handles the entire email OTP flow automatically, including:
  • Sending the OTP code
  • Displaying the code input form
  • Verifying the code
  • Managing resend functionality with countdown timer
  • Error handling and validation
You can customize this behavior using the emailOtp configuration in the provider (see Configuration section above).

Error Handling

The MoonKey SDK automatically handles common errors in the authentication flow:
  • Invalid or expired codes - Shows an error message and allows the user to request a new code
  • Rate limiting - Prevents too many code requests in a short time period
  • Invalid email format - Validates email addresses before sending codes
  • Network errors - Displays appropriate error messages for connectivity issues
You can customize error messages using the emailOtp configuration options like verifiedNewUserMessage, verifiedExistingUserMessage, and verifiedSuccessMessage.

Testing

Test with different email providers

Test your email authentication flow with various email providers:
  • Gmail
  • Outlook/Hotmail
  • Yahoo
  • Corporate email domains
  • Custom domains

Test edge cases

  • Invalid email format
  • Non-existent email addresses
  • Expired codes
  • Already used codes
  • Multiple login attempts

Troubleshooting

Emails not being received

  1. Check spam/junk folders
  2. Verify email domain is not blocked
  3. Check email template configuration in dashboard
  4. Verify DKIM and SPF records (for custom domains)

Code verification fails

  1. Ensure code hasn’t expired (10-minute window)
  2. Check for typos in the code
  3. Verify the email address matches
  4. Check that the code hasn’t been used already

Session not created

  1. Verify session_expires_in is within valid range (5 minutes to 366 days)
  2. Check API key has correct permissions
  3. Ensure backend is properly handling the response