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

# Authentication Methods

# Authentication Methods

Scute provides a comprehensive set of authentication methods designed to handle various user scenarios while maintaining security and user experience. This guide explains the different authentication methods, their use cases, and how to implement them effectively.

## Authentication Flow Overview

Scute's authentication system follows a hierarchical approach:

1. **WebAuthn/Passkeys** (highest security, best UX when available)
2. **Magic Links** (secure, no password required)
3. **OTP (One-Time Password)** (secure, works on all devices)
4. **OAuth/Social Login** (convenient for users with existing accounts)

## Core Authentication Methods

### `signInOrUp(identifier, options?)`

**The recommended unified authentication method** that intelligently handles both sign-in and sign-up scenarios in a single call. This method provides the most seamless user experience by automatically selecting the best available authentication method.

#### Authentication Flow:

1. **WebAuthn Check**: If supported and user has registered devices, attempts passkey authentication
2. **User Detection**: Checks if the identifier exists in the system
3. **Smart Routing**:
   * Existing user → Sign-in flow
   * New user → Sign-up flow
4. **Method Selection**: Based on identifier type and app configuration:
   * Email: Magic link (default) or OTP based on `email_auth_type`
   * Phone: Always OTP

#### Use Cases:

* Universal "Continue with Email/Phone" buttons
* Simplified onboarding flows
* Applications wanting to minimize user friction
* Progressive web apps supporting multiple auth methods

#### Examples:

```typescript theme={null}
// Basic usage with email
const { data, error } = await scuteClient.signInOrUp('user@example.com');

if (error) {
  console.error('Authentication failed:', error.message);
  return;
}

if (!data) {
  // User authenticated with passkey - they're signed in immediately
  console.log('User signed in with passkey');
  navigate('/dashboard');
} else {
  // Additional verification needed (magic link or OTP sent)
  console.log('Verification sent via:', data.type); // 'magic_link' or 'otp'
  showVerificationUI(data.id); // Use data.id to check status
}

// Phone number authentication
const { data, error } = await scuteClient.signInOrUp('+1234567890');

// With options to disable WebAuthn
const { data, error } = await scuteClient.signInOrUp('user@example.com', {
  webauthn: 'disabled'
});
```

#### Response Handling:

```typescript theme={null}
interface SignInOrUpResponse {
  data: {
    type: 'magic_link' | 'otp';
    id: string; // Use this ID to check verification status
  } | null;
  error: ScuteError | null;
}

// Complete example with error handling
const handleUniversalAuth = async (identifier: string) => {
  try {
    const { data, error } = await scuteClient.signInOrUp(identifier);
    
    if (error) {
      // Handle specific error types
      if (error instanceof TechnicalError) {
        showErrorMessage('Something went wrong. Please try again.');
      } else {
        showErrorMessage(error.message);
      }
      return;
    }

    if (!data) {
      // WebAuthn success - user is already signed in
      onAuthenticationSuccess();
    } else {
      // Show appropriate verification UI
      if (data.type === 'magic_link') {
        showMagicLinkSentMessage(identifier);
      } else {
        showOTPInputForm(identifier, data.id);
      }
    }
  } catch (err) {
    console.error('Unexpected error:', err);
    showErrorMessage('An unexpected error occurred.');
  }
};
```

### `signIn(identifier, options?)`

**For existing users only.** This method will fail if the user doesn't exist in the system, making it ideal for dedicated sign-in flows where you want to ensure only existing users can authenticate.

#### Key Features:

* Requires user to exist in the system
* Returns `IdentifierNotRecognizedError` if user doesn't exist
* Still performs WebAuthn check first for registered users
* Respects app configuration for fallback methods

#### Use Cases:

* Dedicated "Sign In" forms separate from registration
* Applications with distinct sign-in/sign-up flows
* When you want to validate user existence before authentication
* Member-only areas or private applications

#### Examples:

```typescript theme={null}
// Basic sign-in
const { data, error } = await scuteClient.signIn('user@example.com');

if (error) {
  if (error instanceof IdentifierNotRecognizedError) {
    showMessage('No account found with this email. Would you like to sign up?');
    // Redirect to sign-up flow
  } else {
    showErrorMessage(error.message);
  }
  return;
}

// Handle successful authentication or verification needed
handleAuthResponse(data);

// Sign-in with WebAuthn disabled
const { data, error } = await scuteClient.signIn('user@example.com', {
  webauthn: 'disabled'
});

// Phone number sign-in
const { data, error } = await scuteClient.signIn('+1234567890');
```

#### WebAuthn Options:

```typescript theme={null}
type ScuteWebauthnOption = 'strict' | 'optional' | 'disabled';

// Strict: Require WebAuthn if user has it enabled (fails if WebAuthn fails)
await scuteClient.signIn('user@example.com', { webauthn: 'strict' });

// Optional: Try WebAuthn first, fallback to other methods (default)
await scuteClient.signIn('user@example.com', { webauthn: 'optional' });

// Disabled: Skip WebAuthn entirely
await scuteClient.signIn('user@example.com', { webauthn: 'disabled' });
```

### `signUp(identifier, options?)`

**For new users only.** This method creates new user accounts and will fail if the user already exists, making it perfect for dedicated registration flows.

#### Key Features:

* Only creates new users
* Returns `IdentifierAlreadyExistsError` if user exists
* Accepts additional user metadata during registration
* No WebAuthn check (since it's a new user)
* Sends verification based on identifier type and app config

#### Use Cases:

* Dedicated registration/sign-up forms
* Onboarding flows with user data collection
* Invitation-based registration
* Applications requiring explicit user consent for account creation

#### Examples:

```typescript theme={null}
// Basic sign-up
const { data, error } = await scuteClient.signUp('newuser@example.com');

if (error) {
  if (error instanceof IdentifierAlreadyExistsError) {
    showMessage('An account with this email already exists. Sign in instead?');
    // Redirect to sign-in flow
  } else {
    showErrorMessage(error.message);
  }
  return;
}

// Sign-up with user metadata
const { data, error } = await scuteClient.signUp('newuser@example.com', {
  userMeta: {
    name: 'John Doe',
    company: 'Acme Inc',
    role: 'developer',
    agreed_to_terms: true,
    marketing_consent: false
  }
});

// Phone number sign-up
const { data, error } = await scuteClient.signUp('+1234567890', {
  userMeta: {
    name: 'Jane Smith',
    preferred_language: 'es'
  }
});
```

#### User Metadata Schema:

```typescript theme={null}
interface UserMeta {
  // String fields
  name?: string;
  company?: string;
  role?: string;
  
  // Boolean fields
  agreed_to_terms?: boolean;
  marketing_consent?: boolean;
  newsletter_subscription?: boolean;
  
  // Number fields
  age?: number;
  
  // Any other custom fields defined in your app config
  [key: string]: string | number | boolean | undefined;
}
```

#### Complete Registration Flow:

```typescript theme={null}
const handleSignUp = async (identifier: string, userData: UserMeta) => {
  // Validate required fields first
  if (!userData.name || !userData.agreed_to_terms) {
    showErrorMessage('Please fill in all required fields');
    return;
  }

  const { data, error } = await scuteClient.signUp(identifier, {
    userMeta: userData
  });

  if (error) {
    if (error instanceof IdentifierAlreadyExistsError) {
      // Offer to sign in instead
      const shouldSignIn = await showConfirmDialog(
        'An account already exists with this email. Sign in instead?'
      );
      
      if (shouldSignIn) {
        return handleSignIn(identifier);
      }
    } else {
      showErrorMessage(error.message);
    }
    return;
  }

  // Show verification UI
  if (data.type === 'magic_link') {
    showMessage(`We've sent a verification link to ${identifier}`);
  } else {
    showOTPInput(identifier, data.id);
  }
};
```

## Direct Authentication Methods

### `sendLoginMagicLink(identifier, webauthnEnabled?)`

**Explicitly sends a magic link** for authentication, bypassing any passkey checks. This method gives you direct control over the authentication flow when you specifically want email-based authentication.

#### Key Features:

* Bypasses WebAuthn/passkey authentication
* Works for both existing and new users
* Sends email with secure, time-limited authentication link
* Returns polling data to check verification status

#### Use Cases:

* "Continue with Email" buttons
* Users who prefer email-based authentication
* Fallback when WebAuthn fails or is unavailable
* Shared device scenarios where passkeys aren't appropriate

#### Examples:

```typescript theme={null}
// Send magic link for existing or new user
const { data, error } = await scuteClient.sendLoginMagicLink('user@example.com');

if (error) {
  showErrorMessage('Failed to send magic link');
  return;
}

// Show user-friendly message
showMessage('Check your email for a sign-in link');

// Optional: Poll for verification status
pollForMagicLinkVerification(data.id);

// Disable WebAuthn in the magic link token
const { data, error } = await scuteClient.sendLoginMagicLink(
  'user@example.com',
  false // webauthnEnabled = false
);
```

#### Magic Link Verification:

```typescript theme={null}
// Poll for magic link verification
const pollForMagicLinkVerification = async (magicLinkId: string) => {
  const maxAttempts = 30; // 5 minutes with 10-second intervals
  let attempts = 0;

  const checkStatus = async () => {
    try {
      const { data, error } = await scuteClient.getMagicLinkStatus(magicLinkId);
      
      if (!error && data) {
        // Magic link was clicked - sign in the user
        await scuteClient.signInWithTokenPayload(data);
        onAuthenticationSuccess();
        return;
      }
    } catch (err) {
      // Continue polling
    }

    attempts++;
    if (attempts < maxAttempts) {
      setTimeout(checkStatus, 10000); // Check every 10 seconds
    } else {
      showMessage('Magic link expired. Please try again.');
    }
  };

  checkStatus();
};

// Handle magic link from URL (when user clicks the link)
const handleMagicLinkFromURL = async () => {
  const token = scuteClient.getMagicLinkToken(); // Gets token from current URL
  
  if (token) {
    const { error } = await scuteClient.signInWithMagicLinkToken(token);
    
    if (error) {
      showErrorMessage('Invalid or expired magic link');
    } else {
      onAuthenticationSuccess();
    }
  }
};
```

### `sendLoginOtp(identifier, webauthnEnabled?)`

**Explicitly sends a one-time password (OTP)** for authentication. This method is ideal for phone-based authentication or when you need a more immediate verification method.

#### Key Features:

* Bypasses WebAuthn/passkey authentication
* Sends SMS or email-based OTP
* Works for both existing and new users
* 6-digit numeric code with time expiration

#### Use Cases:

* Phone number authentication
* Regions where SMS is preferred over email
* Two-factor authentication flows
* Users without reliable email access
* High-security applications

#### Examples:

```typescript theme={null}
// Send OTP to phone number
const { data, error } = await scuteClient.sendLoginOtp('+1234567890');

if (error) {
  showErrorMessage('Failed to send verification code');
  return;
}

showMessage('Enter the 6-digit code sent to your phone');
showOTPInput(data.id);

// Send OTP to email (if app is configured for email OTP)
const { data, error } = await scuteClient.sendLoginOtp('user@example.com');
```

#### OTP Verification:

```typescript theme={null}
// Complete OTP verification flow
const handleOTPVerification = async (otp: string, identifier: string) => {
  if (!/^\d{6}$/.test(otp)) {
    showErrorMessage('Please enter a valid 6-digit code');
    return;
  }

  try {
    const { data, error } = await scuteClient.verifyOtp(otp, identifier);
    
    if (error) {
      showErrorMessage('Invalid or expired code. Please try again.');
      return;
    }

    // OTP verified - sign in the user
    await scuteClient.signInWithTokenPayload(data.authPayload);
    onAuthenticationSuccess();
    
  } catch (err) {
    showErrorMessage('Verification failed. Please try again.');
  }
};

// Resend OTP functionality
const resendOTP = async (identifier: string) => {
  const { data, error } = await scuteClient.sendLoginOtp(identifier);
  
  if (!error) {
    showMessage('New verification code sent');
    return data.id; // New OTP session ID
  } else {
    showErrorMessage('Failed to resend code');
    return null;
  }
};
```

### `signInWithOAuthProvider(provider)`

**Initiates OAuth authentication** with external providers like Google, GitHub, or other configured OAuth services.

#### Key Features:

* Redirects to OAuth provider for authentication
* Handles federated identity management
* Supports multiple OAuth providers
* Automatic account linking for existing users

#### Use Cases:

* Social login (Google, Facebook, GitHub, etc.)
* Enterprise SSO integration
* Reducing password fatigue
* Accessing provider-specific APIs
* Streamlined onboarding for users with existing accounts

#### Examples:

```typescript theme={null}
// Redirect to Google OAuth
await scuteClient.signInWithOAuthProvider('google');

// Redirect to GitHub OAuth
await scuteClient.signInWithOAuthProvider('github');

// Get OAuth URL without redirecting (useful for custom handling)
const googleUrl = scuteClient.getOAuthUrl('google');
window.open(googleUrl, '_blank'); // Open in new tab

// Check available OAuth providers
const { data: appData } = await scuteClient.getAppData();
const providers = appData.oauth_providers || [];

providers.forEach(provider => {
  console.log(`Available: ${provider.name} (${provider.provider})`);
});
```

#### OAuth Configuration Example:

```typescript theme={null}
// Display OAuth providers dynamically
const renderOAuthButtons = (providers: ScuteOAuthProviderConfig[]) => {
  return providers.map(provider => (
    <button
      key={provider.provider}
      onClick={() => scuteClient.signInWithOAuthProvider(provider.provider)}
      style={{ backgroundColor: provider.color }}
    >
      <img src={provider.icon} alt={provider.name} />
      Continue with {provider.name}
    </button>
  ));
};

// Handle OAuth callback (in your callback page)
const handleOAuthCallback = async () => {
  try {
    const { error } = await scuteClient.signInWithMagicLink();
    
    if (error) {
      showErrorMessage('OAuth authentication failed');
      navigate('/login');
    } else {
      navigate('/dashboard');
    }
  } catch (err) {
    console.error('OAuth callback error:', err);
    navigate('/login');
  }
};
```

## Token-Based Authentication Methods

### `signInWithTokenPayload(payload)`

**Internal method** used after successful verification of magic links, OTP, or OAuth to establish the user session. This method completes the authentication flow by setting up the user's session.

#### Key Features:

* Establishes authenticated session
* Triggers authentication state change events
* Handles user data fetching
* Sets up session persistence
* Remembers user identifier for future logins

#### Use Cases:

* Completing magic link verification
* Completing OTP verification
* Handling OAuth callbacks
* Custom authentication flows
* Testing and development scenarios

#### Examples:

```typescript theme={null}
// This method is typically called internally, but can be used for custom flows
const completeAuthentication = async (tokenPayload: ScuteTokenPayload) => {
  const { error } = await scuteClient.signInWithTokenPayload(tokenPayload);
  
  if (error) {
    showErrorMessage('Authentication failed');
  } else {
    // User is now signed in
    navigate('/dashboard');
  }
};

// Common usage patterns (these methods call signInWithTokenPayload internally):

// 1. Magic link verification
const { data, error } = await scuteClient.verifyMagicLinkToken(token);
if (!error) {
  await scuteClient.signInWithTokenPayload(data.authPayload);
}

// 2. OTP verification
const { data, error } = await scuteClient.verifyOtp(otp, identifier);
if (!error) {
  await scuteClient.signInWithTokenPayload(data.authPayload);
}

// 3. Magic link from URL
const { error } = await scuteClient.signInWithMagicLink(); // Handles token extraction and signInWithTokenPayload
```

#### Token Payload Structure:

```typescript theme={null}
interface ScuteTokenPayload {
  access: string;           // JWT access token
  access_expires_at: string; // ISO timestamp
  refresh?: string | null;   // Refresh token (if enabled)
  refresh_expires_at?: string | null; // ISO timestamp
}
```

### `signOut()`

**Securely ends the user session** and cleans up all authentication state. This method ensures complete logout across all stored data.

#### Key Features:

* Revokes refresh token on server
* Clears local session storage
* Removes stored credentials
* Triggers sign-out event for UI updates
* Works across browser tabs (broadcast channel)

#### Use Cases:

* User-initiated logout
* Session timeout handling
* Security-sensitive operations
* Switching between accounts
* Admin-initiated session termination

#### Examples:

```typescript theme={null}
// Basic sign out
const handleSignOut = async () => {
  const success = await scuteClient.signOut();
  
  if (success) {
    showMessage('Signed out successfully');
    navigate('/login');
  } else {
    // Still clears local state even if server call fails
    showMessage('Signed out (offline)');
    navigate('/login');
  }
};

// Sign out with confirmation
const handleSignOutWithConfirmation = async () => {
  const confirmed = await showConfirmDialog(
    'Are you sure you want to sign out?'
  );
  
  if (confirmed) {
    await handleSignOut();
  }
};

// Listen for sign-out events (useful for global state management)
scuteClient.onAuthStateChange((event, session, user) => {
  if (event === 'signed_out') {
    // Clear application state
    clearUserData();
    clearLocalCache();
    navigate('/login');
  }
});

// Automatic sign out on session expiration
scuteClient.onAuthStateChange((event, session, user) => {
  if (event === 'session_expired') {
    showMessage('Your session has expired. Please sign in again.');
    navigate('/login');
  }
});
```

#### Sign Out Return Value:

```typescript theme={null}
// signOut() returns a boolean indicating server-side success
const success = await scuteClient.signOut();

if (success) {
  // Refresh token was successfully revoked on server
} else {
  // Local state was cleared, but server revocation failed
  // This can happen due to network issues or already-expired tokens
  // User is still effectively signed out locally
}
```

## WebAuthn/Passkey Methods

### `addDevice()`

**Registers a new WebAuthn device/passkey** for the currently authenticated user. This enhances security and provides passwordless authentication for future logins.

#### Key Features:

* Requires active authentication session
* Triggers browser's WebAuthn UI
* Supports various authenticator types (platform, roaming)
* Stores credential for future authentication
* Emits registration events for UI feedback

#### Examples:

```typescript theme={null}
// Add device after user signs in
const handleAddPasskey = async () => {
  try {
    const { data, error } = await scuteClient.addDevice();
    
    if (error) {
      if (error.message.includes('cancelled')) {
        showMessage('Passkey setup was cancelled');
      } else {
        showErrorMessage('Failed to set up passkey');
      }
      return;
    }
    
    showMessage('Passkey added successfully! You can now sign in faster.');
    
  } catch (err) {
    showErrorMessage('Passkey not supported on this device');
  }
};

// Check if WebAuthn is supported before showing the option
const shouldShowPasskeyOption = () => {
  return scuteClient.isWebauthnSupported();
};

// Listen for passkey registration events
scuteClient.onAuthStateChange((event, session, user) => {
  switch (event) {
    case 'webauthn_register_start':
      showMessage('Setting up your passkey...');
      break;
    case 'webauthn_register_success':
      showMessage('Passkey set up successfully!');
      break;
  }
});
```

### `isWebauthnSupported()`

**Checks if WebAuthn is supported** on the current device and browser.

```typescript theme={null}
const isSupported = scuteClient.isWebauthnSupported();

if (isSupported) {
  // Show passkey options in UI
} else {
  // Hide passkey features
}
```

### `isAnyDeviceRegistered()`

**Checks if the current user has any registered WebAuthn devices** on this browser/device.

```typescript theme={null}
// Requires authentication
try {
  const hasDevices = await scuteClient.isAnyDeviceRegistered();
  
  if (hasDevices) {
    showMessage('You can sign in with your passkey');
  } else {
    showPasskeySetupPrompt();
  }
} catch (error) {
  // User not authenticated
}
```

## Authentication State Management

### `onAuthStateChange(callback)`

**Listen to authentication state changes** to keep your UI synchronized with the user's authentication status. This is the primary method for handling authentication events in your application.

#### Authentication Events:

* `signed_in` - User successfully authenticated
* `signed_out` - User signed out or session ended
* `initial_session` - Initial session check completed
* `session_refetch` - Session data refreshed
* `session_expired` - Session expired and needs renewal
* `token_refreshed` - Access token automatically refreshed
* `magic_pending` - Magic link or OTP sent, waiting for verification
* `magic_new_device_pending` - Magic link sent due to new device detection
* `otp_pending` - OTP sent, waiting for verification
* `otp_new_device_pending` - OTP sent due to new device detection
* `webauthn_register_start` - WebAuthn registration started
* `webauthn_register_success` - WebAuthn registration completed
* `webauthn_verify_start` - WebAuthn verification started
* `webauthn_verify_success` - WebAuthn verification completed

#### Examples:

```typescript theme={null}
// Basic authentication state handling
const unsubscribe = scuteClient.onAuthStateChange((event, session, user) => {
  console.log('Auth event:', event);
  
  switch (event) {
    case 'signed_in':
      console.log('User signed in:', user?.email);
      navigate('/dashboard');
      break;
      
    case 'signed_out':
      console.log('User signed out');
      navigate('/login');
      break;
      
    case 'session_expired':
      showMessage('Your session has expired. Please sign in again.');
      navigate('/login');
      break;
      
    case 'magic_pending':
      showMessage('Check your email for a sign-in link');
      break;
      
    case 'otp_pending':
      showOTPInput();
      break;
      
    case 'webauthn_register_start':
      showMessage('Setting up your passkey...');
      break;
  }
});

// Clean up listener when component unmounts
return () => unsubscribe();
```

#### Advanced State Management:

```typescript theme={null}
// React hook for authentication state
const useAuth = () => {
  const [session, setSession] = useState(null);
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const unsubscribe = scuteClient.onAuthStateChange((event, session, user) => {
      setSession(session);
      setUser(user);
      
      if (event === 'initial_session') {
        setLoading(false);
      }
    });

    return unsubscribe;
  }, []);

  return { session, user, loading };
};

// Global state management (Redux/Zustand example)
const authSlice = createSlice({
  name: 'auth',
  initialState: {
    user: null,
    session: null,
    loading: true
  },
  reducers: {
    setAuth: (state, action) => {
      state.user = action.payload.user;
      state.session = action.payload.session;
      state.loading = false;
    },
    clearAuth: (state) => {
      state.user = null;
      state.session = null;
      state.loading = false;
    }
  }
});

// Set up listener
scuteClient.onAuthStateChange((event, session, user) => {
  if (event === 'signed_in' || event === 'initial_session') {
    store.dispatch(authSlice.actions.setAuth({ user, session }));
  } else if (event === 'signed_out') {
    store.dispatch(authSlice.actions.clearAuth());
  }
});
```

### `getUser(accessToken?)`

**Get the current authenticated user's data** or verify a specific access token.

#### Examples:

```typescript theme={null}
// Get current user (uses stored session)
const { data, error } = await scuteClient.getUser();

if (error) {
  console.log('User not authenticated');
} else {
  console.log('Current user:', data.user);
}

// Verify specific access token
const { data, error } = await scuteClient.getUser(accessToken);
```

### `getAuthToken()`

**Get the current access token** from the stored session.

```typescript theme={null}
const { data, error } = await scuteClient.getAuthToken();

if (!error) {
  console.log('Access token:', data.access);
  console.log('Expires at:', data.access_expires_at);
}
```

## Utility Methods

### `identifierExists(identifier)`

**Check if a user exists** with the given email or phone number without initiating authentication.

```typescript theme={null}
try {
  const user = await scuteClient.identifierExists('user@example.com');
  
  if (user) {
    console.log('User exists:', user.email);
    console.log('Has passkey:', user.webauthn_enabled);
  } else {
    console.log('No user found with this identifier');
  }
} catch (error) {
  console.error('Failed to check identifier:', error);
}
```

### `getAppData(fresh?)`

**Get application configuration and settings** to customize the authentication flow.

```typescript theme={null}
const { data, error } = await scuteClient.getAppData();

if (!error) {
  console.log('App name:', data.name);
  console.log('Email auth type:', data.email_auth_type); // 'magic' or 'otp'
  console.log('OAuth providers:', data.oauth_providers);
  console.log('Required identifiers:', data.required_identifiers);
  console.log('User metadata schema:', data.user_meta_data_schema);
}

// Force fresh data from server
const { data } = await scuteClient.getAppData(true);
```

## Session Management

### `listUserSessions()`

**Get all active sessions** for the current user across all devices.

```typescript theme={null}
const { data, error } = await scuteClient.listUserSessions();

if (!error) {
  data.sessions.forEach(session => {
    console.log('Session:', session.display_name);
    console.log('Type:', session.type); // 'webauthn', 'magic', 'oauth', etc.
    console.log('Last used:', session.last_used_at);
    console.log('Platform:', session.platform);
    console.log('Browser:', session.browser);
  });
}
```

### `revokeSession(sessionId, credentialId?)`

**Revoke a specific session** from another device.

```typescript theme={null}
// Revoke a specific session
await scuteClient.revokeSession('session-id');

// Revoke session and associated WebAuthn credential
await scuteClient.revokeSession('session-id', 'credential-id');
```

### `removeDeviceCredential(credentialId)`

**Remove a WebAuthn credential** without affecting the session.

```typescript theme={null}
await scuteClient.removeDeviceCredential('credential-id');
```

## Error Handling

Scute provides specific error types for different authentication scenarios. For comprehensive error handling documentation, see the [Error Handling Guide](/docs/guides/error-handling).

```typescript theme={null}
// Basic error handling
const { data, error } = await scuteClient.signInOrUp(identifier);

if (error) {
  console.error('Authentication error:', error.message);
  // Handle error based on type
  return;
}

// Continue with success flow
```

## Best Practices

### 1. **Start with `signInOrUp` for Maximum Flexibility**

```typescript theme={null}
// Recommended: Universal authentication
const handleAuth = async (identifier: string) => {
  const { data, error } = await scuteClient.signInOrUp(identifier);
  
  if (error) {
    handleAuthError(error);
    return;
  }

  // Handle success...
};
```

### 2. **Implement Progressive Enhancement**

```typescript theme={null}
// Check capabilities and adjust UI accordingly
const AuthComponent = () => {
  const [webauthnSupported] = useState(scuteClient.isWebauthnSupported());
  const [appData, setAppData] = useState(null);

  useEffect(() => {
    scuteClient.getAppData().then(({ data }) => setAppData(data));
  }, []);

  return (
    <div>
      {/* Always show primary authentication */}
      <button onClick={() => handleAuth(identifier)}>
        Continue with {identifier.includes('@') ? 'Email' : 'Phone'}
      </button>

      {/* Show OAuth if configured */}
      {appData?.oauth_providers?.map(provider => (
        <button
          key={provider.provider}
          onClick={() => scuteClient.signInWithOAuthProvider(provider.provider)}
        >
          Continue with {provider.name}
        </button>
      ))}

      {/* Show passkey option if supported */}
      {webauthnSupported && (
        <button onClick={handlePasskeyAuth}>
          Sign in with Passkey
        </button>
      )}
    </div>
  );
};
```

### 3. **Handle Network Connectivity**

```typescript theme={null}
const handleAuthWithRetry = async (identifier: string, maxRetries = 3) => {
  let attempts = 0;
  
  while (attempts < maxRetries) {
    try {
      const result = await scuteClient.signInOrUp(identifier);
      return result;
    } catch (error) {
      attempts++;
      
      if (error.message.includes('network') && attempts < maxRetries) {
        await new Promise(resolve => setTimeout(resolve, 1000 * attempts));
        continue;
      }
      
      throw error;
    }
  }
};
```

### 4. **Implement Proper Loading States**

```typescript theme={null}
const AuthForm = () => {
  const [loading, setLoading] = useState(false);
  const [step, setStep] = useState('input'); // 'input', 'verification', 'success'

  const handleSubmit = async (identifier: string) => {
    setLoading(true);
    
    try {
      const { data, error } = await scuteClient.signInOrUp(identifier);
      
      if (error) {
        handleAuthError(error);
        return;
      }

      if (!data) {
        // WebAuthn success
        setStep('success');
      } else {
        // Need verification
        setStep('verification');
      }
    } finally {
      setLoading(false);
    }
  };

  return (
    <div>
      {step === 'input' && (
        <form onSubmit={handleSubmit}>
          <input type="email" placeholder="Enter your email" />
          <button type="submit" disabled={loading}>
            {loading ? 'Please wait...' : 'Continue'}
          </button>
        </form>
      )}
      
      {step === 'verification' && (
        <VerificationStep />
      )}
      
      {step === 'success' && (
        <div>Welcome back!</div>
      )}
    </div>
  );
};
```

### 5. **Security Considerations**

* **Always use HTTPS in production** to protect tokens and user data
* **Validate user input** before passing to authentication methods
* **Implement session timeout handling** using auth state events
* **Use appropriate WebAuthn settings** based on your security requirements

### 6. **Testing Authentication Flows**

```typescript theme={null}
// Example: Test helper for authentication flows
const createTestAuthClient = (config = {}) => {
  return createClient({
    appId: 'test-app-id',
    baseUrl: 'http://localhost:3000',
    debug: true,
    ...config
  });
};

// Example: Mock authentication for testing
const mockSuccessfulAuth = () => {
  jest.spyOn(scuteClient, 'signInOrUp').mockResolvedValue({
    data: null,
    error: null
  });
};
```

## Complete Implementation Example

Here's a comprehensive example showing how to implement a full authentication flow with all the best practices:

```typescript theme={null}
import { useState, useEffect } from 'react';
import { createClient, type ScuteError } from '@scute/auth';

const scuteClient = createClient({
  appId: 'your-app-id',
  baseUrl: 'https://api.scute.io'
});

export const AuthProvider = ({ children }) => {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const unsubscribe = scuteClient.onAuthStateChange((event, session, user) => {
      setUser(user);
      
      if (event === 'initial_session') {
        setLoading(false);
      }
    });

    return unsubscribe;
  }, []);

  return (
    <AuthContext.Provider value={{ user, loading, scuteClient }}>
      {children}
    </AuthContext.Provider>
  );
};

export const LoginForm = () => {
  const [identifier, setIdentifier] = useState('');
  const [step, setStep] = useState('input');
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState('');

  const handleAuth = async (e: React.FormEvent) => {
    e.preventDefault();
    setLoading(true);
    setError('');

    const validationError = validateIdentifier(identifier);
    if (validationError) {
      setError(validationError);
      setLoading(false);
      return;
    }

    try {
      const { data, error } = await scuteClient.signInOrUp(identifier);
      
      if (error) {
        setError(getErrorMessage(error));
      } else if (!data) {
        // WebAuthn success - user is signed in
        setStep('success');
      } else {
        // Show verification step
        setStep('verification');
      }
    } catch (err) {
      setError('An unexpected error occurred');
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="auth-form">
      {step === 'input' && (
        <form onSubmit={handleAuth}>
          <h2>Welcome</h2>
          <input
            type="text"
            placeholder="Email or phone number"
            value={identifier}
            onChange={(e) => setIdentifier(e.target.value)}
            disabled={loading}
          />
          {error && <div className="error">{error}</div>}
          <button type="submit" disabled={loading || !identifier}>
            {loading ? 'Please wait...' : 'Continue'}
          </button>
        </form>
      )}
      
      {step === 'verification' && (
        <VerificationStep identifier={identifier} />
      )}
      
      {step === 'success' && (
        <div>Welcome! Redirecting...</div>
      )}
    </div>
  );
};
```

This comprehensive guide covers all the authentication methods available in Scute, providing you with the knowledge and examples needed to implement secure, user-friendly authentication in your application.
