> ## Documentation Index
> Fetch the complete documentation index at: https://docs.streambird.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Whitelabel

MoonKey's React SDK lets you fully customize authentication to match your app's brand and user experience. You can implement every authentication flow—including email OTP, OAuth (Google and Apple), and Web3 wallet signatures—with your own UI, while MoonKey manages security and backend logic.

<Info>
  All of MoonKey's authentication methods can be whitelabeled, allowing you to build completely custom authentication flows without using the default UI components.
</Info>

## Available authentication methods

MoonKey provides headless authentication hooks that give you full control over your UI:

<CardGroup cols={3}>
  <Card title="Email OTP" icon="envelope">
    Passwordless email authentication with one-time codes
  </Card>

  <Card title="OAuth" icon="user-lock">
    Social login with Google and Apple
  </Card>

  <Card title="Wallet Auth" icon="wallet">
    Web3 authentication with SIWE and SIWS
  </Card>
</CardGroup>

## Email OTP Authentication

To whitelabel MoonKey's passwordless email flow, use the `useLoginWithEmail` hook. Call `sendCode` to send a verification code and `loginWithCode` to authenticate the user.

### Basic usage

```tsx theme={null}
import { useLoginWithEmail } from '@moon-key/react-auth';

function CustomEmailLogin() {
  const { sendCode, loginWithCode, state } = useLoginWithEmail();
  
  // Send code to user's email
  await sendCode({ email: 'user@example.com' });
  
  // Login with the code
  await loginWithCode({ code: '123456' });
}
```

### With callbacks

You can pass callbacks to handle success and error states:

```tsx theme={null}
import { useLoginWithEmail } from '@moon-key/react-auth';
import { useState } from 'react';

function CustomEmailLogin() {
  const [email, setEmail] = useState('');
  const [code, setCode] = useState('');
  
  const { sendCode, loginWithCode, state } = useLoginWithEmail({
    onComplete: (user) => {
      console.log('✅ Login successful:', user);
      // Redirect to dashboard or update UI
    },
    onError: (error) => {
      console.error('❌ Login failed:', error);
      // Show error message to user
    }
  });
  
  const handleSendCode = async () => {
    try {
      await sendCode({ email });
      alert(`Code sent to ${email}!`);
    } catch (error) {
      // Error handled by onError callback
    }
  };
  
  const handleLogin = async () => {
    try {
      await loginWithCode({ code });
    } catch (error) {
      // Error handled by onError callback
    }
  };
  
  const isLoading = state.status === 'sending-code' || 
                    state.status === 'verifying-code';
  
  return (
    <div>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="Enter your email"
        disabled={isLoading}
      />
      <button onClick={handleSendCode} disabled={isLoading}>
        {state.status === 'sending-code' ? 'Sending...' : 'Send Code'}
      </button>
      
      {state.status === 'waiting-code-input' && (
        <>
          <input
            type="text"
            value={code}
            onChange={(e) => setCode(e.target.value)}
            placeholder="Enter verification code"
            maxLength={6}
            disabled={isLoading}
          />
          <button onClick={handleLogin} disabled={isLoading}>
            {state.status === 'verifying-code' ? 'Verifying...' : 'Login'}
          </button>
        </>
      )}
      
      {state.status === 'error' && state.error && (
        <p className="error">Error: {state.error.message}</p>
      )}
    </div>
  );
}
```

### Flow state tracking

The `state` object tracks the current status of the authentication flow:

<ParamField path="state.status" type="string">
  The current status of the authentication flow.

  **Possible values:**

  * `'initial'` - Flow hasn't started
  * `'sending-code'` - Sending OTP to email
  * `'waiting-code-input'` - Code sent, waiting for user input
  * `'verifying-code'` - Verifying the entered code
  * `'complete'` - Authentication successful
  * `'error'` - An error occurred
</ParamField>

<ParamField path="state.error" type="Error | null">
  Error object if an error occurred, `null` otherwise.
</ParamField>

Learn more about [email authentication](/authentication/user-authentication/authentication-methods/email).

## OAuth Authentication

To whitelabel OAuth login with Google and Apple, use the `useLoginWithOAuth` hook and call `initOAuth` with your desired provider.

### Basic usage

```tsx theme={null}
import { useLoginWithOAuth } from '@moon-key/react-auth';

function CustomOAuthLogin() {
  const { initOAuth } = useLoginWithOAuth();
  
  // Initiate Google OAuth flow
  initOAuth({ provider: 'google' });
  
  // Initiate Apple OAuth flow
  initOAuth({ provider: 'apple' });
}
```

### With callbacks

```tsx theme={null}
import { useLoginWithOAuth } from '@moon-key/react-auth';

function CustomOAuthLogin() {
  const { initOAuth, state } = useLoginWithOAuth({
    onSuccess: (user) => {
      console.log('✅ OAuth login successful:', user);
      // User is authenticated and stays on current page
    },
    onError: (error) => {
      console.error('❌ OAuth login failed:', error);
      // Handle error (e.g., show error message)
    }
  });
  
  const handleGoogleLogin = () => {
    initOAuth({ provider: 'google' });
  };
  
  const handleAppleLogin = () => {
    initOAuth({ provider: 'apple' });
  };
  
  return (
    <div>
      <button 
        onClick={handleGoogleLogin}
        disabled={state.status === 'loading'}
      >
        {state.status === 'loading' ? 'Signing in...' : 'Sign in with Google'}
      </button>
      
      <button 
        onClick={handleAppleLogin}
        disabled={state.status === 'loading'}
      >
        {state.status === 'loading' ? 'Signing in...' : 'Sign in with Apple'}
      </button>
      
      {state.status === 'error' && state.error && (
        <p className="error">Error: {state.error.message}</p>
      )}
    </div>
  );
}
```

### OAuth state tracking

The `state` object tracks the OAuth flow:

<ParamField path="state.status" type="string">
  The current status of the OAuth flow.

  **Possible values:**

  * `'idle'` - No OAuth flow in progress
  * `'loading'` - OAuth flow in progress
  * `'done'` - OAuth authentication successful
  * `'error'` - An error occurred
</ParamField>

<ParamField path="state.user" type="User | null">
  The authenticated user object after successful OAuth login.
</ParamField>

<ParamField path="state.error" type="Error | null">
  Error object if an error occurred.
</ParamField>

<Info>
  You must configure OAuth credentials in the [MoonKey Dashboard](/get-started/dashboard/login-methods) before using OAuth authentication.
</Info>

Learn more about [OAuth authentication](/authentication/user-authentication/authentication-methods/oauth).

## Complete example

Here's a complete whitelabel authentication example with both email and OAuth:

```tsx theme={null}
'use client';
import { useLoginWithEmail, useLoginWithOAuth, useMoonKey } from '@moon-key/react-auth';
import { useState } from 'react';

export default function CustomAuthPage() {
  const { isAuthenticated, user, logout, ready } = useMoonKey();
  const [email, setEmail] = useState('');
  const [code, setCode] = useState('');
  const [message, setMessage] = useState('');
  
  // Email authentication hook
  const { sendCode, loginWithCode, state: emailState } = useLoginWithEmail({
    onComplete: (user) => {
      console.log('✅ Email login successful:', user);
      setMessage('Login successful!');
    },
    onError: (error) => {
      console.error('❌ Email login failed:', error);
      setMessage(`Error: ${error.message}`);
    }
  });
  
  // OAuth authentication hook
  const { initOAuth, state: oauthState } = useLoginWithOAuth({
    onSuccess: (user) => {
      console.log('✅ OAuth login successful:', user);
      setMessage('OAuth login successful!');
    },
    onError: (error) => {
      console.error('❌ OAuth login failed:', error);
      setMessage(`OAuth error: ${error.message}`);
    }
  });
  
  const handleSendCode = async () => {
    setMessage('');
    try {
      await sendCode({ email });
      setMessage(`Code sent to ${email}!`);
    } catch (error) {
      // Handled by onError
    }
  };
  
  const handleLogin = async () => {
    setMessage('');
    try {
      await loginWithCode({ code });
    } catch (error) {
      // Handled by onError
    }
  };
  
  const handleLogout = async () => {
    await logout();
    setMessage('Logged out successfully');
    setEmail('');
    setCode('');
  };
  
  const isLoading = emailState.status === 'sending-code' || 
                    emailState.status === 'verifying-code' ||
                    oauthState.status === 'loading';
  
  if (!ready) {
    return <div>Loading MoonKey SDK...</div>;
  }
  
  if (isAuthenticated && user) {
    return (
      <div className="auth-container">
        <h1>Welcome!</h1>
        <p>User ID: {user.id}</p>
        <p>Email: {user.email?.address}</p>
        <button onClick={handleLogout}>Logout</button>
      </div>
    );
  }
  
  return (
    <div className="auth-container">
      <h1>Sign In</h1>
      
      {message && <div className="message">{message}</div>}
      
      {/* Email Authentication */}
      <div className="auth-section">
        <h2>Email Login</h2>
        <div className="form-group">
          <input
            type="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            placeholder="Enter your email"
            disabled={isLoading}
          />
          <button onClick={handleSendCode} disabled={!email || isLoading}>
            {emailState.status === 'sending-code' ? 'Sending...' : 'Send Code'}
          </button>
        </div>
        
        {emailState.status === 'waiting-code-input' && (
          <div className="form-group">
            <input
              type="text"
              value={code}
              onChange={(e) => setCode(e.target.value)}
              placeholder="Enter 6-digit code"
              maxLength={6}
              disabled={isLoading}
            />
            <button onClick={handleLogin} disabled={!code || isLoading}>
              {emailState.status === 'verifying-code' ? 'Verifying...' : 'Login'}
            </button>
          </div>
        )}
        
        {emailState.status === 'error' && emailState.error && (
          <p className="error">{emailState.error.message}</p>
        )}
      </div>
      
      <div className="divider">OR</div>
      
      {/* OAuth Authentication */}
      <div className="auth-section">
        <h2>Social Login</h2>
        <button 
          className="oauth-button google"
          onClick={() => initOAuth({ provider: 'google' })}
          disabled={isLoading}
        >
          <GoogleIcon />
          {oauthState.status === 'loading' ? 'Signing in...' : 'Continue with Google'}
        </button>
        
        <button 
          className="oauth-button apple"
          onClick={() => initOAuth({ provider: 'apple' })}
          disabled={isLoading}
        >
          <AppleIcon />
          {oauthState.status === 'loading' ? 'Signing in...' : 'Continue with Apple'}
        </button>
        
        {oauthState.status === 'error' && oauthState.error && (
          <p className="error">{oauthState.error.message}</p>
        )}
      </div>
    </div>
  );
}

// Icon components (simplified)
function GoogleIcon() {
  return <span>G</span>;
}

function AppleIcon() {
  return <span>🍎</span>;
}
```

### Styling example

```css theme={null}
.auth-container {
  max-width: 400px;
  margin: 0 auto;
  padding: 2rem;
}

.auth-section {
  margin: 2rem 0;
}

.form-group {
  display: flex;
  gap: 0.5rem;
  margin-bottom: 1rem;
}

.form-group input {
  flex: 1;
  padding: 0.75rem;
  border: 1px solid #ddd;
  border-radius: 0.5rem;
}

.form-group button {
  padding: 0.75rem 1.5rem;
  background: #6366f1;
  color: white;
  border: none;
  border-radius: 0.5rem;
  cursor: pointer;
}

.form-group button:disabled {
  background: #d1d5db;
  cursor: not-allowed;
}

.oauth-button {
  width: 100%;
  padding: 0.75rem;
  margin-bottom: 0.5rem;
  border: 1px solid #ddd;
  border-radius: 0.5rem;
  background: white;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
}

.oauth-button:hover {
  background: #f9fafb;
}

.oauth-button.apple {
  background: #000;
  color: white;
}

.message {
  padding: 1rem;
  margin-bottom: 1rem;
  background: #d1fae5;
  border-radius: 0.5rem;
  color: #065f46;
}

.error {
  color: #dc2626;
  font-size: 0.875rem;
  margin-top: 0.5rem;
}

.divider {
  text-align: center;
  margin: 2rem 0;
  position: relative;
}

.divider::before,
.divider::after {
  content: '';
  position: absolute;
  top: 50%;
  width: 45%;
  height: 1px;
  background: #ddd;
}

.divider::before {
  left: 0;
}

.divider::after {
  right: 0;
}
```

## Best practices

<AccordionGroup>
  <Accordion title="Handle loading states">
    Always show loading indicators during authentication:

    ```tsx theme={null}
    const isLoading = state.status === 'sending-code' || 
                      state.status === 'verifying-code';

    <button disabled={isLoading}>
      {isLoading ? 'Loading...' : 'Send Code'}
    </button>
    ```
  </Accordion>

  <Accordion title="Provide clear error messages">
    Display user-friendly error messages:

    ```tsx theme={null}
    {state.status === 'error' && state.error && (
      <div className="error">
        {state.error.message}
      </div>
    )}
    ```
  </Accordion>

  <Accordion title="Use callbacks for side effects">
    Handle navigation and UI updates in callbacks:

    ```tsx theme={null}
    const { sendCode } = useLoginWithEmail({
      onComplete: (user) => {
        // Navigate to dashboard
        router.push('/dashboard');
      },
      onError: (error) => {
        // Show toast notification
        toast.error(error.message);
      }
    });
    ```
  </Accordion>

  <Accordion title="Track authentication state">
    Use the flow state to show relevant UI:

    ```tsx theme={null}
    // Show code input only after sending code
    {state.status === 'waiting-code-input' && (
      <input placeholder="Enter code" />
    )}

    // Show success message
    {state.status === 'complete' && (
      <p>Authentication successful!</p>
    )}
    ```
  </Accordion>

  <Accordion title="Disable inputs during loading">
    Prevent users from modifying inputs during operations:

    ```tsx theme={null}
    <input
      value={email}
      onChange={(e) => setEmail(e.target.value)}
      disabled={isLoading || !ready}
    />
    ```
  </Accordion>

  <Accordion title="Wait for SDK to be ready">
    Check the `ready` state before rendering auth UI:

    ```tsx theme={null}
    const { ready } = useMoonKey();

    if (!ready) {
      return <div>Loading...</div>;
    }
    ```
  </Accordion>
</AccordionGroup>

## Advanced patterns

### Multi-step forms

Build multi-step authentication flows:

```tsx theme={null}
function MultiStepAuth() {
  const [step, setStep] = useState<'email' | 'code' | 'complete'>('email');
  const { sendCode, loginWithCode, state } = useLoginWithEmail({
    onComplete: () => setStep('complete')
  });
  
  switch (step) {
    case 'email':
      return <EmailStep onNext={() => setStep('code')} />;
    case 'code':
      return <CodeStep onBack={() => setStep('email')} />;
    case 'complete':
      return <CompleteStep />;
  }
}
```

### Conditional authentication

Show different auth methods based on user type:

```tsx theme={null}
function ConditionalAuth({ userType }: { userType: 'new' | 'returning' }) {
  if (userType === 'new') {
    return <EmailOnlyAuth />;
  }
  
  return <AllAuthMethods />;
}
```

### Prefilled email

Pre-populate email for invited users:

```tsx theme={null}
function InviteAuth({ inviteEmail }: { inviteEmail: string }) {
  const [email] = useState(inviteEmail);
  const { sendCode } = useLoginWithEmail();
  
  useEffect(() => {
    // Automatically send code to invited email
    sendCode({ email });
  }, []);
  
  return <CodeInput />;
}
```

## TypeScript types

For TypeScript users, here are the relevant types:

```typescript theme={null}
// Email authentication
interface UseLoginWithEmailOptions {
  onComplete?: (user: User) => void;
  onError?: (error: Error) => void;
}

interface EmailAuthState {
  status: 'initial' | 'sending-code' | 'waiting-code-input' | 'verifying-code' | 'complete' | 'error';
  error: Error | null;
}

// OAuth authentication
interface UseLoginWithOAuthOptions {
  onSuccess?: (user: User) => void;
  onError?: (error: Error) => void;
}

interface OAuthState {
  status: 'idle' | 'loading' | 'done' | 'error';
  user: User | null;
  error: Error | null;
}
```

## Troubleshooting

<AccordionGroup>
  <Accordion title="Code not received">
    If users don't receive the verification code:

    * Check spam/junk folders
    * Verify the email address is correct
    * Wait a few moments before resending
    * Check your email configuration in the MoonKey Dashboard
  </Accordion>

  <Accordion title="OAuth redirect issues">
    If OAuth redirects fail:

    * Verify redirect URLs in the MoonKey Dashboard
    * Check that OAuth credentials are configured
    * Ensure your domain is whitelisted
    * Test in an incognito window to rule out browser issues
  </Accordion>

  <Accordion title="State not updating">
    If the state doesn't update correctly:

    * Ensure you're using the latest SDK version
    * Check that callbacks are properly defined
    * Verify the SDK is initialized correctly
    * Look for console errors
  </Accordion>
</AccordionGroup>
