npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@seaverse/auth-sdk

v0.4.10

Published

SDK for SeaVerse Backend API

Readme

@seaverse/auth-sdk

SeaVerse Backend API Client SDK - Provides complete authentication, container management, skill marketplace, and more

npm version License: MIT

Features

  • 🔐 User Authentication - Registration, login, logout, password reset
  • 🌐 OAuth Login - Google, Discord, GitHub third-party login
  • 🎨 Login UI Components - Out-of-the-box beautiful login modal
  • Toast Notifications - Modern glassmorphism alerts with auto-injected CSS
  • 🌍 Multi-Environment Support - Production, Staging, Development, Local
  • TypeScript - Full type definitions
  • 🔧 Auth Integration - Built-in Auth and Hooks system support
  • 🔄 Response Format Compatibility - Automatic compatibility with multiple API response formats

Installation

npm install @seaverse/auth-sdk
# or
pnpm add @seaverse/auth-sdk

Core Concepts

App ID and Multi-Tenant Architecture

Important: Starting from v0.2.0, appId is a required parameter when initializing the SDK.

What is App ID?

Each application has a unique app_id:

  • app_id = "your app id"

Multi-Tenant Isolation

  • User data is isolated by app_id
  • The same email can be registered in different applications with different passwords
  • Each application has its own independent user pool

X-App-ID Request Header

The SDK automatically adds X-App-ID to every request header, no manual configuration needed:

const client = new SeaVerseBackendAPIClient({
  appId: 'game-abc123',  // SDK automatically adds this value to all requests' X-App-ID header
});

// All API calls will automatically carry X-App-ID: game-abc123
await client.login({ email, password });
await client.getCurrentUser();

Quick Start

1. Basic Usage

import { SeaVerseBackendAPIClient } from '@seaverse/auth-sdk';

// Method 1: SeaVerse platform application (auto-detect environment)
const client = new SeaVerseBackendAPIClient({
  appId: 'your app id', // Required: Application ID
});

// Method 2: Third-party application (specify environment)
const client = new SeaVerseBackendAPIClient({
  appId: 'your app id',        // Required: Your application ID
  environment: 'production',   // 'production' | 'staging' | 'development' | 'local'
});

// Method 3: Custom URL
const client = new SeaVerseBackendAPIClient({
  appId: 'your app id',             // Required: Your application ID
  baseURL: 'https://custom-api.example.com',
});

// Method 4: Enable request retry (disabled by default)
const client = new SeaVerseBackendAPIClient({
  appId: 'your app id',
  retryOptions: {
    maxRetries: 3,                  // Retry up to 3 times
    retryDelay: 1000,               // Initial delay 1000ms, doubles each retry
    retryStatusCodes: [408, 429, 500, 502, 503, 504],  // Retry on these status codes
  },
});

// Health check
const health = await client.getHealth();
console.log('Health:', health);

2. Token Retrieval in Iframe Scenarios

When your application is embedded in an iframe, you can use the getIframeToken() method to retrieve the authentication token from the parent page. This is particularly useful for third-party login redirect scenarios.

Check if Running in Iframe

Before calling iframe-related methods, it's recommended to check if the application is running in an iframe:

import { SeaVerseBackendAPIClient } from '@seaverse/auth-sdk';

// Method 1: Use static method (no client instance needed)
if (SeaVerseBackendAPIClient.isInIframe()) {
  console.log('Application is running in iframe');
} else {
  console.log('Application is not running in iframe');
}

// Method 2: Use instance method
const client = new SeaVerseBackendAPIClient({
  appId: 'your app id',
});

if (client.isInIframe()) {
  console.log('Application is running in iframe');
  // Safe to call getIframeToken()
}

Child Page (Inside Iframe) Usage

import { SeaVerseBackendAPIClient } from '@seaverse/auth-sdk';

const client = new SeaVerseBackendAPIClient({
  appId: 'your app id',
});

// First check if running in iframe
if (!client.isInIframe()) {
  console.log('Not in iframe, skip token retrieval');
  // Can use other login methods
} else {
  // Request token from parent page while in iframe
  try {
    const token = await client.getIframeToken({
      timeout: 10000 // Optional, default 30 seconds
    });

    // Set token to client
    client.setToken(token);
    localStorage.setItem('token', token);

    console.log('Successfully retrieved token from parent page');

    // Now can use authenticated APIs
    const user = await client.getCurrentUser();
    console.log('Current user:', user);
  } catch (error) {
    console.error('Failed to retrieve token:', error);
  }
}

Parent Page Listens and Responds to Token Requests

// Parent page needs to listen for token requests from iframe
window.addEventListener('message', (event) => {
  // Check message type
  if (event.data.type === 'seaverse:get_token') {
    const requestId = event.data.requestId;

    // Get your token (from localStorage, API, etc)
    const token = localStorage.getItem('token');

    if (token) {
      // Send token to iframe
      event.source.postMessage({
        type: 'seaverse:token',
        requestId: requestId,
        payload: {
          accessToken: token,
          expiresIn: 3600,
        },
      }, '*');
    } else {
      // Send error message
      event.source.postMessage({
        type: 'seaverse:token_error',
        requestId: requestId,
        error: 'No token available',
      }, '*');
    }
  }
});

Complete Iframe Login Flow Example

// Child page (iframe)
import { SeaVerseBackendAPIClient } from '@seaverse/auth-sdk';

const client = new SeaVerseBackendAPIClient({
  appId: 'your app id',
});

async function initIframeApp() {
  // 0. Check if running in iframe
  if (!client.isInIframe()) {
    console.log('Not in iframe, use regular login flow');
    // Use regular login flow
    showLoginForm();
    return;
  }

  try {
    // 1. Try to get token from parent page
    const token = await client.getIframeToken();
    client.setToken(token);
    localStorage.setItem('token', token);

    // 2. Validate token and get user info
    const user = await client.getCurrentUser();
    console.log('Logged in:', user);

    // 3. Continue app logic
    startApp(user);
  } catch (error) {
    // 4. If failed, notify parent page that login is needed
    window.parent.postMessage({
      type: 'seaverse:need_login',
    }, '*');
  }
}

initIframeApp();
// Parent page
window.addEventListener('message', (event) => {
  // Handle token request
  if (event.data.type === 'seaverse:get_token') {
    const requestId = event.data.requestId;
    const token = localStorage.getItem('token');

    if (token) {
      event.source.postMessage({
        type: 'seaverse:token',
        requestId: requestId,
        payload: {
          accessToken: token,
          expiresIn: 3600,
        },
      }, '*');
    } else {
      // If no token, trigger login
      showLoginModal();
    }
  }

  // Handle need login message
  if (event.data.type === 'seaverse:need_login') {
    showLoginModal();
  }
});

// After successful login, notify iframe
function onLoginSuccess(newToken) {
  const iframe = document.querySelector('iframe');
  iframe.contentWindow.postMessage({
    type: 'seaverse:token',
    requestId: 'auto', // Can use fixed ID for auto-login
    payload: {
      accessToken: newToken,
      expiresIn: 3600,
    },
  }, '*');
}

TypeScript Type Definitions

The SDK provides complete TypeScript type support:

import type {
  IframeTokenRequestMessage,
  IframeTokenResponseMessage,
  IframeTokenErrorMessage,
} from '@seaverse/auth-sdk';

// Token request message
const request: IframeTokenRequestMessage = {
  type: 'seaverse:get_token',
  requestId: 'unique-id',
};

// Token response message
const response: IframeTokenResponseMessage = {
  type: 'seaverse:token',
  requestId: 'unique-id',
  payload: {
    accessToken: 'jwt-token',
    expiresIn: 3600,
  },
};

// Error message
const error: IframeTokenErrorMessage = {
  type: 'seaverse:token_error',
  requestId: 'unique-id',
  error: 'Token not available',
};

Security Considerations

  1. Use specific origin: In production, use specific origin instead of '*':

    // Parent page
    const ALLOWED_ORIGIN = 'https://your-iframe-domain.com';
    event.source.postMessage(message, ALLOWED_ORIGIN);
    
    // Child page can also specify when sending
    window.parent.postMessage(message, 'https://parent-domain.com');
  2. Verify message origin: Validate origin when handling postMessage:

    window.addEventListener('message', (event) => {
      // Verify origin
      if (event.origin !== 'https://trusted-domain.com') {
        return;
      }
      // Handle message...
    });
  3. Handle token expiration: Remember to handle token expiration:

    try {
      const user = await client.getCurrentUser();
    } catch (error) {
      if (error.response?.status === 401) {
        // Token expired, re-retrieve
        const newToken = await client.getIframeToken();
        client.setToken(newToken);
      }
    }

3. User Authentication

// Register new user
const registerResult = await client.register({
  email: '[email protected]',
  password: 'SecurePassword123',
  username: 'johndoe',              // Optional, auto-generated from email if not provided
  invitation_code: 'INVITE123',     // Optional
  frontend_url: 'https://mygame.com/verify',  // Optional, frontend URL for email verification link, defaults to window.location.href
});

// Check registration result
if (registerResult.success) {
  console.log('Registration successful:', registerResult);
} else if (registerResult.code === 'ACCOUNT_EXISTS') {
  console.log('Account exists, please login directly');
  console.log('Error details:', registerResult.details);
}

// Login
const loginResult = await client.login({
  email: '[email protected]',
  password: 'SecurePassword123',
});

// Save token
localStorage.setItem('token', loginResult.token);

// ⚠️ Note: Automatic invitation code redirect
// If server returns INVITE_CODE_REQUIRED error and includes redirectUrl,
// SDK will automatically redirect user to that URL (usually invitation code input page)
// No extra handling needed, page will redirect automatically

// Get current user info
const user = await client.getCurrentUser();
console.log('User:', user);
console.log('App ID:', user.app_id);           // Multi-tenant application ID
console.log('Email verified:', user.email_verified);

// Logout
await client.logout();

// Forgot password
await client.forgotPassword({
  email: '[email protected]',
  frontend_url: 'https://mygame.com/',  // Optional, defaults to window.location.href
});

// Reset password
await client.resetPassword({
  token: 'reset-token-from-email',
  new_password: 'NewSecurePassword123',
});

4. OAuth Third-Party Login (Backend Proxy Mode)

The SDK uses Backend Proxy Mode where Client Secret is never exposed to the frontend, providing higher security.

Advantages:

  • ✅ Client Secret is never exposed to frontend
  • ✅ Supports any developer domain (no need to configure in OAuth platform)
  • ✅ Built-in CSRF protection
  • ✅ Zero configuration, works out of the box

Workflow:

  1. Frontend calls {provider}Authorize() to get OAuth URL
  2. Frontend redirects user to OAuth provider
  3. After user authorization, OAuth provider callbacks to fixed account-hub URL
  4. account-hub handles OAuth, creates JWT token
  5. account-hub 302 redirects to return_url?token=xxx
  6. Frontend extracts token from URL and stores it

Usage Examples

Method 1: Use default return_url (current page)

// Google login
const { authorize_url } = await client.googleAuthorize();
window.location.href = authorize_url;

// Discord login
const { authorize_url } = await client.discordAuthorize();
window.location.href = authorize_url;

// GitHub login
const { authorize_url } = await client.githubAuthorize();
window.location.href = authorize_url;

Method 2: Custom return_url

// Redirect to dashboard after login
const { authorize_url } = await client.googleAuthorize({
  return_url: 'https://mygame.com/dashboard'
});
window.location.href = authorize_url;

Extract token in callback page:

// URL: https://mygame.com/?token=eyJhbGc...
const token = new URLSearchParams(window.location.search).get('token');
if (token) {
  localStorage.setItem('token', token);
  // Login successful, redirect or update UI
}

OAuth Account Unlinking

// Unlink Google account
await client.unlinkGoogle();

// Unlink Discord account
await client.unlinkDiscord();

// Unlink GitHub account
await client.unlinkGithub();

5. Using Login UI Components

The SDK provides beautiful login modal components with dark and light themes:

Theme Comparison

| Theme | Design Style | Use Cases | |------|---------|---------| | Dark 🌙 | Modern glassmorphism with brand color (#00E599) | Tech products, gaming platforms, dark interface apps | | Light ☀️ | Elevated card design + soft shadows | Business apps, content platforms, light interface apps |

Login Flow Design

The dark theme login interface features a dual-view system with progressive disclosure:

New Style View (Default):

  1. Initial View: Shows OAuth login buttons (Google primary, GitHub/Discord secondary) with brand green (#00E599) for the primary action
  2. Email Option: A "Sign in with Email" button allows users to switch to traditional email/password login
  3. Smart View Reset: When navigating from signup or other forms back to login, automatically resets to this modern OAuth-first view

Old Style View (On-Demand):

  1. Traditional Form: Activated by clicking "Sign in with Email", displays the classic email and password input fields
  2. Complete Experience: Includes "Welcome back." title, signup link, and all familiar login elements
  3. Back Navigation: Users can easily return to the OAuth options view with "Back to other options" button

This dual-view design:

  • ✨ Prioritizes OAuth login for better conversion rates
  • 🔄 Keeps traditional email login accessible when needed
  • 🎯 Reduces cognitive load with clear, focused interfaces
  • 📱 Provides smooth transitions between authentication methods

Basic Usage

import { SeaVerseBackendAPIClient, AuthModal } from '@seaverse/auth-sdk';
import '@seaverse/auth-sdk/auth-modal.css'; // Import styles

const client = new SeaVerseBackendAPIClient({
  appId: 'your app id',         // Required: Your application ID
  environment: 'production',
});

// Create login modal
const authModal = new AuthModal({
  client,
  theme: 'dark', // 'dark' | 'light' - defaults to 'dark'

  // Login success callback
  onLoginSuccess: (token, user) => {
    localStorage.setItem('token', token);
    console.log('Login successful:', user);
  },

  // Signup success callback
  onSignupSuccess: (token, user) => {
    localStorage.setItem('token', token);
    console.log('Signup successful:', user);
  },

  // Invitation code required callback (optional)
  onInviteCodeRequired: (userId, email) => {
    console.log('Need invitation code to activate account:', userId, email);
    // AuthModal will automatically show invitation code input interface
  },

  // Apply invite success callback (optional)
  onApplyInviteSuccess: (applicationId, email) => {
    console.log('Invitation code application submitted:', applicationId, email);
    // Can add custom logic here, such as showing notifications
  },

  // Error callback
  onError: (error) => {
    console.error('Authentication error:', error.message);
  },

  // OAuth configuration (optional)
  returnUrl: 'https://mygame.com/',  // URL to return after OAuth login, optional, defaults to window.location.href
  enableOAuth: {
    google: true,   // Enable Google login
    discord: true,  // Enable Discord login
    github: true,   // Enable GitHub login
  },
});

// Show login interface
authModal.show('login');

// Show signup interface
authModal.show('signup');

// Hide modal
authModal.hide();

// Destroy modal
authModal.destroy();

✨ New Feature: Auto Toast Notifications

AuthModal has built-in modern Toast notification system, no extra configuration needed:

// Toast displays automatically, CSS auto-injected
// - Registration success → Green success notification
// - Account exists → Yellow warning notification
// - Login failure → Red error notification
// - Reset password → Blue info notification

// You can also use Toast independently (CSS auto-injected)
import { Toast } from '@seaverse/auth-sdk';

Toast.success('Success', 'Data saved');
Toast.error('Failed', 'Please try again later');
Toast.warning('Notice', 'Account already exists');
Toast.info('Info', 'Email sent');

Password Reset Flow

AuthModal supports complete password reset flow:

  1. User triggers forgot password: Click "Forgot Password?" link in login interface
  2. Send reset email: After entering email, system sends reset link with reset_token
  3. Auto trigger reset modal: When user clicks link in email and returns to website, AuthModal automatically detects reset_token parameter in URL and shows reset password form
  4. Set new password: User enters and confirms new password then submits
  5. Auto cleanup URL: After successful reset, automatically clears reset_token parameter from URL

The entire process requires no extra code, AuthModal handles it automatically:

// 1. Initialize AuthModal (only once)
const authModal = new AuthModal({
  client,
  // ... other configuration
});

// 2. After user clicks reset link in email, AuthModal automatically:
//    - Detects ?reset_token=xxx parameter in URL
//    - Shows reset password form
//    - User submits new password
//    - Calls client.resetPassword() API
//    - Cleans reset_token parameter from URL
//    - Shows success message

OAuth Configuration Instructions

The enableOAuth parameter is completely optional:

  • If not provided, no third-party login buttons will be shown
  • If partially configured (e.g., only enable Google), only enabled buttons will be shown
  • If fully configured for all platforms, all third-party login buttons will be shown

Configuration field descriptions:

  • returnUrl: Optional - URL to return after OAuth login, defaults to window.location.href if not provided
  • enableOAuth.google: Whether to enable Google login
  • enableOAuth.discord: Whether to enable Discord login
  • enableOAuth.github: Whether to enable GitHub login
// Example 1: No OAuth buttons
const authModal1 = new AuthModal({
  client,
  theme: 'dark',
  // No enableOAuth, no OAuth buttons shown
});

// Example 2: Only show Google login
const authModal2 = new AuthModal({
  client,
  theme: 'light',
  returnUrl: 'https://mygame.com/dashboard',
  enableOAuth: {
    google: true,
    // Discord and GitHub not enabled, won't show these buttons
  },
});

// Example 3: Show all OAuth buttons
const authModal3 = new AuthModal({
  client,
  theme: 'dark',
  enableOAuth: {
    google: true,
    discord: true,
    github: true,
  },
});

Handling OAuth Callback

In Backend Proxy Mode, after OAuth login it redirects to returnUrl?token=xxx. Check and handle token on page load:

// Automatically handle OAuth callback on page load
const result = AuthModal.handleOAuthCallback({
  client,
  onLoginSuccess: (token) => {
    localStorage.setItem('token', token);
    console.log('OAuth login successful');

    // Can now call authenticated APIs directly
    // handleOAuthCallback has automatically called client.setToken()
    client.getCurrentUser()
      .then(user => console.log('User info:', user));
  },
});

if (result) {
  console.log('Handled OAuth callback, token:', result.token);
}

Important Note:

  • handleOAuthCallback() automatically calls client.setToken(token) to update client authentication configuration
  • This means after the onLoginSuccess callback, all authenticated APIs (such as getCurrentUser(), logout(), etc.) will automatically include the Authorization header

Manual Token Setting

If you don't use AuthModal.handleOAuthCallback(), you can also set the token manually:

// Extract token from URL
const token = new URLSearchParams(window.location.search).get('token');
if (token) {
  // Manually set token
  client.setToken(token);
  localStorage.setItem('token', token);

  // Now can call authenticated APIs
  const user = await client.getCurrentUser();
}

6. Container Management

// List all containers
const containers = await client.listContainers();

// Register new container
const result = await client.registerContainer({
  containerId: 'container-123',
  metadata: {
    version: '1.0.0',
    capabilities: ['skill-execution'],
  },
});

// Get container info
const container = await client.getContainer({
  containerId: 'container-123',
});

// Container heartbeat
await client.containerHeartbeat({
  containerId: 'container-123',
  status: 'healthy',
});

// Unregister container
await client.unregisterContainer({
  containerId: 'container-123',
});

// Get container stats
const stats = await client.getContainerStats();

7. Skill Marketplace

// List marketplace skills
const skills = await client.listMarketplaceSkills({
  category: 'productivity',
  page: 1,
  pageSize: 20,
});

// Get skill details
const skill = await client.getMarketplaceSkill({
  skillId: 'skill-123',
});

// Install skill
await client.installSkill({
  skillId: 'skill-123',
});

// List installed skills
const userSkills = await client.listUserSkills();

// Uninstall skill
await client.uninstallSkill({
  skillId: 'skill-123',
});

8. Invitation Code Management

// Apply for invitation code (when user doesn't have one)
const application = await client.applyInvite({
  email: '[email protected]',
  reason: 'I want to join this amazing platform to build innovative applications and connect with the community.'
});

if (application.success) {
  console.log('Application submitted:', application.data);
  console.log('Application ID:', application.data.id);
  console.log('Status:', application.data.status); // 'pending'
} else {
  // Handle error
  if (application.code === 'APPLICATION_DUPLICATE') {
    console.log('You have already submitted an application in the past 24 hours');
  } else {
    console.error('Application failed:', application.error);
  }
}

// List my invitation codes
const invites = await client.listInvites({
  status: 'active',
  page: 1,
  page_size: 20,
});
console.log('My invitation codes:', invites.data.invites);

// Get invitation code stats
const stats = await client.getInviteStats();
console.log('Total invitation codes:', stats.data.total_codes);
console.log('Active invitation codes:', stats.data.active_codes);
console.log('Total usage count:', stats.data.total_uses);

// Get invitation code details
const invite = await client.getInvite('inv_abc123');
console.log('Invitation code:', invite.data.code);
console.log('Used count:', invite.data.used_count);
console.log('Max uses:', invite.data.max_uses);

// Get invitation code usage records
const usages = await client.getInviteUsages('inv_abc123', {
  page: 1,
  page_size: 20,
});
console.log('Usage records:', usages.data.usages);

9. Email Verification and Invitation Code Binding

Email Verification (Auto Login)

After registration, users receive a verification email containing a verification link in the format: frontend_url?verify_token=xxx

Method 1: Use AuthModal Auto-Handling (Recommended)

import { AuthModal } from '@seaverse/auth-sdk';

// Create AuthModal instance
const modal = new AuthModal({
  client,
  onLoginSuccess: (token, user) => {
    localStorage.setItem('token', token);
    console.log('Email verified and logged in successfully:', user);
  },
  onError: (error) => {
    console.error('Email verification failed:', error);
  }
});

// AuthModal will automatically detect verify_token parameter in URL
// After detection, it will automatically:
// 1. Call verifyEmail() API to verify email
// 2. Get returned JWT token and auto-login
// 3. Trigger onLoginSuccess callback
// 4. Clean verify_token parameter from URL
// 5. Show success message

Debugging Tips:

If email verification has issues, check browser console logs:

// Normal case should show:
[AuthModal] Detected verify_token, starting email verification...
[AuthModal] Email verification successful: { id: "xxx", email: "[email protected]", ... }

// If verification fails, will show:
[AuthModal] Email verification failed: Error: ...

Method 2: Manual Email Verification Handling

// Verify email (get verify_token from email link)
const urlParams = new URLSearchParams(window.location.search);
const verifyToken = urlParams.get('verify_token');

if (verifyToken) {
  const result = await client.verifyEmail(verifyToken);

  // Auto-login: save returned token
  localStorage.setItem('token', result.data.token);
  localStorage.setItem('refreshToken', result.data.refreshToken);

  console.log('Email verified successfully, auto-logged in:', result.data.user);

  // Redirect to homepage
  window.location.href = '/';
}

Invitation Code Binding (Account Activation)

When registering with external email or OAuth login without providing invitation code, a temporary account is created and requires subsequent invitation code binding for activation.

Apply for Invitation Code Feature:

If user doesn't have an invitation code, AuthModal provides built-in application feature:

  1. In invitation code input interface, user can click "Don't have an invitation code? Apply for one" link
  2. Automatically jumps to apply for invitation code form
  3. User fills in email and application reason (10-500 characters)
  4. After submission, calls applyInvite() API, after backend approval, invitation code will be sent via email

Auto Redirect Mechanism:

  • When login() returns INVITE_CODE_REQUIRED error and includes redirectUrl, SDK automatically redirects page to that URL
  • redirectUrl usually contains error_code=INVITE_CODE_REQUIRED&user_id=xxx&email=xxx parameters
  • No need to manually handle redirect logic, SDK completes automatically

For other scenarios (such as redirects after OAuth login), backend directly redirects to frontend_url?error_code=INVITE_CODE_REQUIRED&user_id=xxx&email=xxx.

Method 1: Use AuthModal Auto-Handling (Recommended)

import { AuthModal } from '@seaverse/auth-sdk';

// Create AuthModal instance
const modal = new AuthModal({
  client,
  onLoginSuccess: (token, user) => {
    localStorage.setItem('token', token);
    console.log('Login successful:', user);
  },
  onInviteCodeRequired: (userId, email) => {
    // Optional: custom handling when invitation code is needed
    console.log('Need invitation code to activate account:', userId, email);
  }
});

// AuthModal will automatically detect the following parameter combinations in URL:
// - error_code=INVITE_CODE_REQUIRED
// - user_id or temp_user_id (user ID)
// - email (optional, user email)
//
// After detection, it will automatically:
// 1. Show invitation code input interface
// 2. After user inputs invitation code, call bindInviteCode() API
// 3. After successful activation, auto-login (save token)
// 4. Clean parameters from URL

Debugging Tips:

If invitation code modal doesn't appear, check browser console for the following logs:

// Normal case should show:
[AuthModal] Detected INVITE_CODE_REQUIRED: {
  errorCode: "INVITE_CODE_REQUIRED",
  userId: "xxx",
  tempUserId: null,
  email: "[email protected]",
  fullURL: "http://localhost:8001/?error_code=INVITE_CODE_REQUIRED&user_id=xxx&email=xxx"
}

// If user_id is missing, will show error:
[AuthModal] Missing user_id in URL parameters.
// And shows alert with full URL for troubleshooting backend redirect issues

Method 2: Manual Invitation Code Binding Handling

// 1. Check if invitation code is needed in URL
const urlParams = new URLSearchParams(window.location.search);
const errorCode = urlParams.get('error_code');
const userId = urlParams.get('user_id');

if (errorCode === 'INVITE_CODE_REQUIRED' && userId) {
  // 2. Show invitation code input interface (custom UI)
  const inviteCode = await showInviteCodeInput(); // Your custom UI

  // 3. Bind invitation code
  const result = await client.bindInviteCode({
    user_id: userId,
    invite_code: inviteCode
  });

  // 4. Activation successful, auto-login
  localStorage.setItem('token', result.data.token);
  localStorage.setItem('refreshToken', result.data.refreshToken);
  console.log('Account activated successfully:', result.data.user);

  // 5. Redirect to homepage
  window.location.href = '/';
}

Method 3: Use Static Method Handling

import { AuthModal } from '@seaverse/auth-sdk';

// AuthModal provides static method to handle invitation code scenario
const inviteCodeInfo = AuthModal.handleInviteCodeRequired(
  { client },
  (userId, email) => {
    // Custom handling logic
    const code = prompt(`Please enter invitation code to activate account (${email}):`);
    if (code) {
      client.bindInviteCode({ user_id: userId, invite_code: code })
        .then(res => {
          localStorage.setItem('token', res.data.token);
          window.location.reload();
        });
    }
  }
);

10. Other Features

// Get API Service Token
const apiToken = await client.getApiServiceToken();

// Get conversation status
const status = await client.getConversationStatus({
  conversationId: 'conv-123',
});

// Get speech token
const speechToken = await client.getSpeechToken();

Advanced Configuration

Custom Authentication

import { AuthFactory } from '@seaverse/auth-sdk';

const client = new SeaVerseBackendAPIClient({
  appId: 'your app id',         // Required: Application ID
  environment: 'production',

  // Use JWT authentication
  auth: AuthFactory.create({
    type: 'jwt',
    credentials: {
      type: 'jwt',
      token: localStorage.getItem('token'),
    },
  }),
});

Custom Hooks

import { BuiltInHooks } from '@seaverse/auth-sdk';

const client = new SeaVerseBackendAPIClient({
  appId: 'your app id',         // Required: Application ID
  environment: 'production',

  hooks: {
    hooks: [
      // Logger Hook
      BuiltInHooks.createLoggerHook({
        logLevel: 'debug',
        logRequestBody: true,
        logResponseBody: true,
      }),

      // Request ID Hook
      BuiltInHooks.createRequestIdHook(),

      // Custom Hook
      {
        type: 'beforeRequest',
        name: 'custom-hook',
        priority: 100,
        handler: async (context) => {
          console.log('Before request:', context.config.url);
        },
      },
    ],
  },
});

Request Retry Configuration

The SDK supports custom HTTP request retry strategies. By default, retry functionality is disabled (maxRetries: 0) to ensure request predictability.

Default Behavior (Retry Disabled)

const client = new SeaVerseBackendAPIClient({
  appId: 'your app id',
  // retryOptions not set, no retry by default
});

// If request fails, will return error immediately, no retry

Enable Basic Retry

import { SeaVerseBackendAPIClient } from '@seaverse/auth-sdk';

const client = new SeaVerseBackendAPIClient({
  appId: 'your app id',
  retryOptions: {
    maxRetries: 3,        // Retry up to 3 times
    retryDelay: 1000,     // Initial delay 1000ms (1 second)
    // Defaults to retry on these status codes: [408, 429, 500, 502, 503, 504]
  },
});

Retry Strategy Explanation:

  • Uses exponential backoff algorithm: 1st retry waits 1 second, 2nd waits 2 seconds, 3rd waits 4 seconds
  • HTTP status codes that trigger auto-retry:
    • 408 - Request Timeout
    • 429 - Too Many Requests (rate limiting)
    • 500 - Internal Server Error
    • 502 - Bad Gateway
    • 503 - Service Unavailable
    • 504 - Gateway Timeout
  • Network errors (no response) will also trigger retry

Custom Retry Status Codes

const client = new SeaVerseBackendAPIClient({
  appId: 'your app id',
  retryOptions: {
    maxRetries: 5,
    retryDelay: 2000,
    retryStatusCodes: [503, 504],  // Only retry on 503 and 504
  },
});

Custom Retry Logic

import type { RetryOptions } from '@seaverse/auth-sdk';

const retryOptions: RetryOptions = {
  maxRetries: 3,
  retryDelay: 1000,
  // Custom judgment logic: only retry on specific errors
  shouldRetry: (error) => {
    // Only retry on service unavailable errors
    if (error.response?.status === 503) {
      return true;
    }
    // Retry on network errors with no response
    if (!error.response) {
      return true;
    }
    return false;
  },
};

const client = new SeaVerseBackendAPIClient({
  appId: 'your app id',
  retryOptions,
});

RetryOptions Type Definition

interface RetryOptions {
  maxRetries?: number;           // Maximum retry count, default 0 (disabled)
  retryDelay?: number;           // Initial retry delay (milliseconds), default 1000
  retryStatusCodes?: number[];   // List of status codes that trigger retry
  shouldRetry?: (error: AxiosError) => boolean;  // Custom retry judgment function
}

Usage Recommendations:

  • ⚠️ Recommended to disable retry in production (default behavior), avoid duplicate requests on business logic errors
  • ✅ Only enable retry in unstable network scenarios, such as mobile apps, weak network environments
  • ✅ Ensure backend APIs support idempotent operations, avoid side effects from retries
  • ✅ For critical business (such as payments), recommended to use custom shouldRetry to carefully control retry logic

Environment Configuration

The SDK supports the following environments:

| Environment | Description | BaseURL | |------|------|---------| | production | Production environment | https://account-hub.seaverse.ai | | staging | Staging environment | https://api.staging.seaverse.dev | | development | Development environment | https://api.dev.seaverse.dev | | local | Local environment | http://localhost:3000 |

Auto-detection rules:

  • *.seaverse.com → production
  • *.staging.seaverse.dev → staging
  • *.dev.seaverse.dev → development
  • localhost → local

API Reference

Authentication Related

| Method | Parameters | Return Value | Description | |------|------|--------|------| | register() | { email, password, username?, invitation_code?, frontend_url? } | RegisterResponse | Register new user, frontend_url is frontend URL for email verification link, defaults to window.location.href | | login() | { email, password, frontend_url? } | LoginResponse | User login, frontend_url used for sending verification email when email not verified, defaults to https://seaverse.ai/. ⚠️ If returns INVITE_CODE_REQUIRED with redirectUrl, will automatically redirect | | getCurrentUser() | - | User | Get current user | | logout() | - | SuccessResponse | Logout | | verifyEmail() | verifyToken: string | EmailVerificationResponse | Verify email and return auto-login token | | bindInviteCode() | { user_id, invite_code } | BindInviteCodeResponse | Bind invitation code to activate temporary account and auto-login | | forgotPassword() | { email, frontend_url? } | SuccessResponse | Forgot password, frontend_url defaults to window.location.href | | resetPassword() | { token, new_password } | SuccessResponse | Reset password | | setToken() | token: string | void | Set authentication token (used after OAuth login) | | isInIframe() | - | boolean | Check if application is running in iframe (supports static method and instance method) | | getIframeToken() | { timeout? } | Promise<string> | Get token from parent page (iframe scenario only), timeout defaults to 30000ms | | getApiServiceToken() | - | ApiServiceTokenResponse | Get API Token |

Registration Flow Description

The register() method supports three registration flows, SDK automatically handles based on backend response:

Flow 1: Immediate Activation (Default)

const response = await client.register({ email, password });
// response.success === true
// response.requiresEmailVerification === undefined (or false)
// response.requiresInvitationCode === undefined (or false)
// → SDK automatically calls login() and triggers onSignupSuccess callback

Flow 2: Requires Email Verification

const response = await client.register({ email, password });
// response.success === true
// response.requiresEmailVerification === true
// → SDK shows Toast prompting user to check email
// → Does not auto-call login(), user needs to click verification link in email

Flow 3: Requires Invitation Code Activation

const response = await client.register({ email, password });
// response.success === true
// response.requiresInvitationCode === true
// response.tempUserId === "temp_xxx"
// → SDK shows invitation code input form
// → Does not auto-call login(), user needs to input invitation code

Important Note:

  • AuthModal automatically handles these three flows, no manual judgment needed
  • Only when no verification or activation is needed, will login() be automatically called
  • This avoids unnecessary login requests when email is not verified or account is not activated

OAuth Related

| Method | Parameters | Return Value | Description | |------|------|--------|------| | googleAuthorize() | { return_url? } | OAuthAuthorizeResponse | Google OAuth authorization URL | | discordAuthorize() | { return_url? } | OAuthAuthorizeResponse | Discord OAuth authorization URL | | githubAuthorize() | { return_url? } | OAuthAuthorizeResponse | GitHub OAuth authorization URL | | unlinkGoogle() | - | SuccessResponse | Unlink Google | | unlinkDiscord() | - | SuccessResponse | Unlink Discord | | unlinkGithub() | - | SuccessResponse | Unlink GitHub |

Container Management

| Method | Parameters | Return Value | Description | |------|------|--------|------| | listContainers() | - | ContainerListResponse | List containers | | registerContainer() | { containerId, metadata } | SuccessResponse | Register container | | getContainer() | { containerId } | Container | Get container info | | unregisterContainer() | { containerId } | SuccessResponse | Unregister container | | containerHeartbeat() | { containerId, status } | SuccessResponse | Container heartbeat | | getContainerStats() | - | ContainerStatsResponse | Container stats |

Skill Marketplace

| Method | Parameters | Return Value | Description | |------|------|--------|------| | listMarketplaceSkills() | { category?, page?, pageSize? } | MarketplaceSkillsListResponse | List marketplace skills | | getMarketplaceSkill() | { skillId } | MarketplaceSkill | Get skill details | | installSkill() | { skillId } | SuccessResponse | Install skill | | listUserSkills() | - | UserInstalledSkillsListResponse | List installed skills | | uninstallSkill() | { skillId } | SuccessResponse | Uninstall skill |

Invitation Code Management

| Method | Parameters | Return Value | Description | |------|------|--------|------| | applyInvite() | { email, reason } | ApplyInviteResponse | Apply for invitation code (reason needs 10-500 characters)| | listInvites() | { page?, page_size?, status? } | ListInvitesResponse | List my invitation codes | | getInviteStats() | - | InviteStatsResponse | Get invitation code stats | | getInvite() | inviteId: string | InviteCodeDetailResponse | Get invitation code details | | getInviteUsages() | inviteId: string, { page?, page_size? } | ListInviteUsagesResponse | Get invitation code usage records |

Others

| Method | Parameters | Return Value | Description | |------|------|--------|------| | getHealth() | - | HealthResponse | Health check | | getConversationStatus() | { conversationId } | ConversationStatusResponse | Get conversation status | | getSpeechToken() | - | SpeechTokenResponse | Get speech token |

Response Format Compatibility

Auto Response Unwrapping

Starting from v0.2.5, SDK automatically supports two API response formats, no manual handling needed:

Format 1: Wrapped Format (Recommended)

{
  "data": {
    "token": "eyJhbGc...",
    "user": { ... }
  },
  "success": true
}

Format 2: Flat Format (Backward Compatible)

{
  "token": "eyJhbGc...",
  "user": { ... }
}

SDK automatically detects response format and extracts correct data:

// Regardless of which format backend returns, the following code works
const loginResult = await client.login({
  email: '[email protected]',
  password: 'password123',
});

console.log(loginResult.token); // ✅ Always gets token correctly
console.log(loginResult.user);  // ✅ Always gets user correctly

Affected Methods

The following methods have implemented auto response unwrapping:

  • login() - Login API
  • register() - Registration API
  • getCurrentUser() - Get current user

Implementation Principle

SDK internally handles response format through the following logic:

const response = await httpClient.request(config);
const responseData = response.data;

// Detect and unwrap
if (responseData.data && typeof responseData.data === 'object') {
  // Wrapped format: extract data field
  return responseData.data;
}
// Flat format: return directly
return responseData;

This means:

  • 🔄 Backend format changes don't require frontend modifications - Backend can freely adjust response format
  • 🔧 Progressive migration - Different APIs can be migrated to new format gradually
  • Backward compatible - Old code continues to work without modifications

Error Handling

Error Response Format

All API errors follow a unified response format:

interface ApiError {
  success: false;           // Always false on error
  error: string;            // Human-readable error message
  code?: string;            // Machine-readable error code
  details?: any;            // Additional error details
}

Error Codes

SDK provides standard error code enums:

import { ErrorCode } from '@seaverse/auth-sdk';

// Account-related errors
ErrorCode.ACCOUNT_EXISTS         // Account already exists
ErrorCode.ACCOUNT_NOT_FOUND      // Account not found
ErrorCode.ACCOUNT_SUSPENDED      // Account suspended
ErrorCode.INVALID_CREDENTIALS    // Invalid login credentials
ErrorCode.EMAIL_NOT_VERIFIED     // Email not verified

// Validation-related errors
ErrorCode.INVALID_EMAIL          // Invalid email address
ErrorCode.INVALID_PASSWORD       // Invalid password
ErrorCode.PASSWORD_TOO_WEAK      // Password too weak

// Invitation code-related errors
ErrorCode.INVALID_INVITATION_CODE // Invalid invitation code
ErrorCode.INVITATION_REQUIRED     // Invitation code required
ErrorCode.INVITE_CODE_REQUIRED    // Invitation code required (will auto-redirect to redirectUrl)

// Token-related errors
ErrorCode.INVALID_TOKEN          // Invalid token
ErrorCode.TOKEN_EXPIRED          // Token expired

// Internal errors
ErrorCode.INTERNAL_ERROR         // Server internal error

Error Handling Best Practices

1. Handle Duplicate Users During Registration

Since backend returns 200 OK when account exists (success response), frontend won't throw exception, but includes error info in response body.

import { ErrorCode } from '@seaverse/auth-sdk';

const result = await client.register({
  email: '[email protected]',
  password: 'password123',
});

// Check success field in response
if (result.success) {
  console.log('Registration successful:', result);
  // Proceed with follow-up actions, such as auto-login
} else if (result.code === ErrorCode.ACCOUNT_EXISTS) {
  // Account exists, prompt user
  const { email, app_id } = result.details;
  console.log(`Account ${email} already exists in application ${app_id}`);

  // Show friendly prompt
  alert('This email is already registered. Please login instead.');

  // Or guide user to login
  showLoginModal();
} else {
  // Handle other errors
  console.error('Registration failed:', result.error);
}

2. Handle Various Errors During Login

try {
  const result = await client.login({
    email: '[email protected]',
    password: 'wrong-password',
  });
  console.log('Login successful:', result);
} catch (error) {
  const errorCode = error.response?.data?.code;

  switch (errorCode) {
    case ErrorCode.INVALID_CREDENTIALS:
      showError('Incorrect username or password');
      break;
    case ErrorCode.EMAIL_NOT_VERIFIED:
      showError('Please verify your email first');
      showResendVerificationButton();
      break;
    case ErrorCode.ACCOUNT_SUSPENDED:
      showError('Your account has been suspended, please contact administrator');
      break;
    default:
      showError('Login failed, please try again later');
  }
}

3. Generic Error Handling Function

import { ErrorCode } from '@seaverse/auth-sdk';

function handleApiError(error: any) {
  const apiError = error.response?.data;

  if (!apiError) {
    // Network error or other unknown error
    return {
      title: 'Network Error',
      message: 'Please check your network connection',
    };
  }

  // Return user-friendly messages based on error code
  const errorMessages: Record<string, { title: string; message: string }> = {
    [ErrorCode.ACCOUNT_EXISTS]: {
      title: 'Account Exists',
      message: 'This email is already registered, please login directly',
    },
    [ErrorCode.INVALID_CREDENTIALS]: {
      title: 'Login Failed',
      message: 'Incorrect username or password',
    },
    [ErrorCode.EMAIL_NOT_VERIFIED]: {
      title: 'Email Not Verified',
      message: 'Please verify your email address first',
    },
    [ErrorCode.INVALID_INVITATION_CODE]: {
      title: 'Invalid Invitation Code',
      message: 'Please check if your invitation code is correct',
    },
    [ErrorCode.TOKEN_EXPIRED]: {
      title: 'Login Expired',
      message: 'Please login again',
    },
  };

  return errorMessages[apiError.code] || {
    title: 'Operation Failed',
    message: apiError.error || 'Unknown error occurred',
  };
}

// Usage example
try {
  await client.register({ email, password });
} catch (error) {
  const { title, message } = handleApiError(error);
  showNotification(title, message);
}

4. TypeScript Type-Safe Error Handling

import { ErrorCode, models } from '@seaverse/auth-sdk';

async function safeRegister(email: string, password: string) {
  // Register API now returns 200 OK for both success and account exists cases
  const result = await client.register({ email, password });

  if (result.success) {
    return {
      success: true,
      data: result,
    };
  } else if (result.code === ErrorCode.ACCOUNT_EXISTS) {
    // TypeScript knows the type of details
    const details = result.details as models.AccountExistsErrorDetails;
    return {
      success: false,
      error: 'ACCOUNT_EXISTS',
      email: details.email,
      appId: details.app_id,
    };
  } else {
    return {
      success: false,
      error: result.error || 'Unknown error',
    };
  }
}

// Usage
const registerResult = await safeRegister('[email protected]', 'password123');
if (registerResult.success) {
  console.log('Registration successful');
} else if (registerResult.error === 'ACCOUNT_EXISTS') {
  console.log(`Account already exists: ${registerResult.email}`);
}

HTTP Status Code Reference

| HTTP Status Code | Error Code Example | Description | |-----------|----------|------| | 200 OK | ACCOUNT_EXISTS | Account exists (business error but returns success response) | | 400 Bad Request | INVALID_EMAIL, PASSWORD_TOO_WEAK | Invalid request parameters | | 401 Unauthorized | INVALID_CREDENTIALS, TOKEN_EXPIRED | Authentication failed | | 403 Forbidden | EMAIL_NOT_VERIFIED, ACCOUNT_SUSPENDED | Insufficient permissions | | 500 Internal Server Error | INTERNAL_ERROR | Server internal error |

Note: For registration API, account exists case returns 200 OK, but in response body success: false and code: "ACCOUNT_EXISTS". This design makes it easier for frontend to handle this common business scenario.

Type Definitions

// User info
interface User {
  id?: string;
  app_id?: string | null;           // Application ID (multi-tenant support)
  email?: string;
  username?: string;
  created_at?: number;               // Unix timestamp
  email_verified?: boolean;          // Email verification status
  google_id?: string | null;         // Google account ID
  discord_id?: string | null;        // Discord account ID
  github_id?: string | null;         // GitHub account ID

  // Deprecated fields (backward compatible)
  createdAt?: number;                // @deprecated Use created_at
  emailVerified?: boolean;           // @deprecated Use email_verified
  googleId?: string;                 // @deprecated Use google_id
  discordId?: string;                // @deprecated Use discord_id
  githubId?: string;                 // @deprecated Use github_id
}

// Login response
interface LoginResponse {
  token: string;
  user: User;
}

// Registration response
interface RegisterResponse {
  success: boolean;
  message?: string;
  userId?: string;
  // Email verification related
  requiresEmailVerification?: boolean;  // Whether email verification is required
  // Invitation code activation related
  requiresInvitationCode?: boolean;     // Whether invitation code activation is required
  tempUserId?: string;                  // Temporary user ID (needs activation)
  // Error info
  error?: string;
  code?: string;                        // E.g. 'ACCOUNT_EXISTS'
  details?: Record<string, any>;
}

// AuthModal options
interface AuthModalOptions {
  client: SeaVerseBackendAPIClient;
  theme?: 'dark' | 'light';
  onLoginSuccess?: (token: string, user: any) => void;
  onSignupSuccess?: (token: string, user: any) => void;
  onInviteCodeRequired?: (userId: string, email: string) => void; // Callback when invitation code activation is needed
  onApplyInviteSuccess?: (applicationId: string, email: string) => void; // Callback when invitation code application succeeds
  onError?: (error: Error) => void;
  returnUrl?: string;        // URL to return after OAuth login, optional, defaults to window.location.href
  enableOAuth?: {
    google?: boolean;        // Enable Google login
    discord?: boolean;       // Enable Discord login
    github?: boolean;        // Enable GitHub login
  };
}

Complete Examples

Check examples/auth-sdk-demo directory for complete runnable examples.

# Run example
cd examples/auth-sdk-demo
pnpm install
pnpm dev

Migration from Old Versions

v0.1.x → v0.2.0 Upgrade Guide

1. Backward Compatibility

Good news! v0.2.0 is fully backward compatible, existing code works without modifications.

2. Recommended Migration Steps

While not required, we recommend gradually migrating to new field naming conventions:

// Old code (still works)
const user = await client.getCurrentUser();
console.log(user.emailVerified);  // ⚠️ Deprecated but works
console.log(user.createdAt);      // ⚠️ Deprecated but works

// New code (recommended)
const user = await client.getCurrentUser();
console.log(user.email_verified); // ✅ Recommended
console.log(user.created_at);     // ✅ Recommended
console.log(user.app_id);         // ✅ New field: multi-tenant support

3. Registration API Update

// Old code (still works)
await client.register({
  email: '[email protected]',
  password: 'password123',
  invitationCode: 'INVITE123',  // ⚠️ Deprecated but works
});

// New code (recommended)
await client.register({
  email: '[email protected]',
  password: 'password123',
  username: 'myusername',       // ✨ New feature
  invitation_code: 'INVITE123', // ✅ Recommended
});

4. Password Reset API Update

// Old code (needs update)
await client.resetPassword({
  token: 'reset-token',
  newPassword: 'NewPass123',    // ⚠️ Deprecated
});

// New code (recommended)
await client.resetPassword({
  token: 'reset-token',
  new_password: 'NewPass123',   // ✅ Recommended
});

5. TypeScript Type Hints

If you use TypeScript, compiler will automatically hint deprecated fields:

const user = await client.getCurrentUser();
user.emailVerified;  // TypeScript will show strikethrough and deprecation warning
user.email_verified; // ✅ No warning

6. Multi-Tenant Feature (New)

If your application needs multi-tenant architecture support, you can use the new app_id field:

const user = await client.getCurrentUser();

console.log('your app id:', user.app_id);

FAQ

How to Handle Authentication Token?

// Save token after successful login
const loginResult = await client.login({ email, password });
localStorage.setItem('token', loginResult.token);

// Create authenticated client
import { AuthFactory } from '@seaverse/auth-sdk';

const authenticatedClient = new SeaVerseBackendAPIClient({
  appId: 'your app id',         // Required: Application ID
  environment: 'production',
  auth: AuthFactory.create({
    type: 'jwt',
    credentials: {
      type: 'jwt',
      token: localStorage.getItem('token'),
    },
  }),
});

OAuth redirect_uri_mismatch Error?

Ensure the redirect URI in OAuth application configuration exactly matches the redirectUri in code (including protocol, domain, port, path).

How to Customize Request Timeout?

const client = new SeaVerseBackendAPIClient({
  appId: 'your app id',         // Required: Application ID
  environment: 'production',
  timeout: 30000,            // 30 seconds
});

How to Connect to Local API in Local Development?

const client = new SeaVerseBackendAPIClient({
  appId: 'your app id',         // Required: Application ID
  environment: 'local',      // Or use baseURL: 'http://localhost:3000'
});

Development

# Install dependencies
pnpm install

# Build
pnpm build

# Watch mode
pnpm dev

# Test
pnpm test

Related Links

License

MIT © SeaVerse Team

Changelog

v0.4.7 (Current Version)

  • 🎨 UI Style Update: Modernized login interface with refined visual design
    • Updated primary button color to brighter green (#00E599 → #00e599, #00f5a8 on hover)
    • OAuth buttons layout (applies to login and signup pages):
      • Desktop & Mobile: Google and GitHub in first row (2 columns grid), Discord spans full width in second row
      • Uses CSS Grid layout with grid-column: 1 / -1 for Discord button to span both columns
      • Consistent button styling across all OAuth providers
    • Enhanced input field styling with improved hover and focus states
    • Refined modal layout: left panel (374px) + right panel (456px), fixed height (636px)
    • Updated mesh gradient animation for smoother visual effects
    • Improved button styles across all views (signin, signup, forgot password)
    • Enhanced form labels, dividers, and social login buttons
    • Better spacing and typography throughout the interface
    • Optimized responsive design for mobile devices

v0.4.6

  • 🐛 Bug Fix: Fix OAuth buttons not responding in email login view
    • Fixed HTML element ID conflict between new style and old style login views
    • Old style login view OAuth buttons now have unique IDs with 'old' prefix
    • Properly bind click events to all OAuth buttons in both views

v0.4.4

  • 🎨 Login UI Redesign: Dual-view login system with progressive disclosure
    • New Style View (Default): Modern OAuth-first interface with brand color #00E599
      • Google as primary button, GitHub/Discord as secondary options
      • "Sign in with Email" toggle for traditional login
      • Automatically shown when returning to login from other views
    • Old Style View (On-Demand): Classic email/password form
      • Activated by clicking "Sign in with Email"
      • Complete traditional login experience with "Welcome back." title
      • "Back to other options" button to return to OAuth view
    • Enhanced visual design: improved input fields, button states, and typography
    • Smooth view transitions with CSS-scoped styling for each mode

v0.3.6

  • 🧹 Code Cleanup: Remove desktop application OAuth callback related functionality
    • Remove oauthDesktopURL configuration option
    • Simplify OAuth flow, unified use of returnUrl parameter
    • Clean up related documentation and code comments

v0.2.5

  • 🔄 Response Format Compatibility: Automatic compatibility with wrapped and flat API response formats
    • Fix "Invalid response from server" prompt during login
    • login(), register(), getCurrentUser() methods auto-unwrap data field
    • Support two formats: { data: {...}, success: true } and { ... }
  • 📝 Documentation Update: Add response format compatibility section

v0.2.0

  • 🔄 API Path Update: All authentication APIs migrated from /api/auth/* to /sdk/v1/auth/*
  • 🏢 Multi-Tenant Support: User model adds app_id field, supports multi-application isolation
  • 🔑 Important Change: appId is now a required parameter, SDK automatically adds X-App-ID request header to all requests
  • New Features:
    • Registration supports custom username (optional)
    • All fields standardized to snake_case (retains camelCase backward compatibility)
  • 📝 Field Updates:
    • SeaVerseBackendAPIClientOptions: Add required field appId
    • User: Add app_id, created_at, email_verified, google_id, discord_id, github_id
    • RegisterRequest: Add username, invitation_code
    • ResetPasswordRequest: newPasswordnew_password
  • ⚠️ Breaking Changes:
    • Must provide appId parameter to initialize client
    • API path changes require backend support
  • Backward Compatible: Except for appId, all old fields still work

v0.1.5

  • Fix OAuth state management, switch from sessionStorage to localStorage
  • Enhance API response handling

v0.1.4

  • Enhance API response handling
  • Optimize error handling

v0.1.2

  • Update package name to @seaverse/auth-sdk
  • Add multi-environment support

View complete changelog: CHANGELOG.md