Hiyve Components - v1.0.0
    Preparing search index...

    Module @hiyve/identity-client

    @hiyve/identity-client

    Zero-dependency browser SDK for Hiyve Identity. Built on native fetch and localStorage.

    • Register, login, logout -- email/password auth with automatic token storage
    • Automatic token refresh -- refreshes JWT access tokens before they expire
    • Persistent sessions -- tokens survive page reloads via localStorage (or in-memory for SSR)
    • Password reset -- request reset emails and submit new passwords via token
    • Email verification -- verify addresses and resend verification emails
    • Create tokens -- long-lived API tokens for CI/CD, integrations, and MCP server connections
    • Permission scoping -- restrict tokens to specific capabilities (mcp:data:read, mcp:search:execute, etc.)
    • IP restrictions -- limit token usage to specific CIDR ranges
    • Lifecycle management -- list, validate, and revoke tokens
    • Client management -- create, list, and delete OAuth clients with redirect URI validation
    • Authorization Code flow -- full OAuth 2.1 with automatic PKCE S256 generation
    • Token exchange -- exchange authorization codes for access/refresh token pairs
    • Token refresh -- rotate OAuth refresh tokens
    • Invisible device recognition -- known devices bypass 2FA automatically
    • Email OTP verification -- 6-digit one-time codes for unknown devices
    • Automatic device fingerprinting -- screen resolution, timezone, language, and platform signals
    • TFA event system -- subscribe to tfaRequired events for custom UI flows
    • Configurable per-org -- TFA and email verification can be independently enabled or disabled via organization settings (see Server Integration Guide)
    • List devices -- view all recognized devices for the authenticated user
    • Revoke devices -- remove specific devices or revoke all at once
    • Automatic registration -- devices are registered as trusted after successful OTP verification
    • Get profile -- fetch the full user profile (name, picture, phone, organization, metadata, roles)
    • Update profile -- update display name, picture, phone, organization, and custom metadata
    • Zero runtime dependencies -- ships as ES module (~18 KB) and UMD (~9 KB)
    • TypeScript definitions included
    • Structured errors -- every failure is an AuthError with a typed code and optional details
    • Event system -- subscribe to auth state changes, token refresh, and errors
    • Framework-agnostic -- works with React, Vue, vanilla JS, or any browser environment
    npm install @hiyve/identity-client
    
    <script src="path/to/hiyve-identity-client.umd.cjs"></script>
    <script>
    const auth = new HiyveIdentityClient.HiyveAuth({
    apiKey: 'pk_live_your_key',
    });
    </script>
    import { HiyveAuth } from '@hiyve/identity-client';

    const auth = new HiyveAuth({
    apiKey: 'pk_live_your_api_key',
    });

    // Register a new user
    await auth.register({
    email: 'user@example.com',
    password: 'securePassword123',
    name: 'Jane Doe',
    });

    // Log in (may trigger TFA on unknown devices)
    const result = await auth.login({
    email: 'user@example.com',
    password: 'securePassword123',
    });

    if (result.tfaRequired) {
    // Unknown device -- OTP code sent to user's email
    console.log('Check your email for a verification code');

    // After user enters the code:
    const { user } = await auth.verifyTfa({ code: '123456' });
    console.log('Logged in as:', user.email);
    } else {
    console.log('Logged in as:', result.user.email);
    }

    console.log('Authenticated:', auth.isAuthenticated()); // true

    // Listen for auth state changes
    const unsubscribe = auth.onAuthStateChange(({ authenticated, user }) => {
    console.log('Auth state changed:', authenticated, user);
    });

    // Log out
    await auth.logout();

    // Clean up when done (e.g. component unmount)
    unsubscribe();
    auth.destroy();

    Pass a config object to the HiyveAuth constructor:

    const auth = new HiyveAuth({
    // Required
    apiKey: 'pk_live_your_api_key', // Your public API key

    // Optional (defaults shown)
    environment: 'production', // 'production' or 'development'
    basePath: '/identity/auth', // API route prefix
    tokenStorage: 'localStorage', // 'localStorage' or 'memory'
    autoRefresh: true, // Auto-refresh tokens before expiry
    refreshBuffer: 300, // Seconds before expiry to trigger refresh
    timeout: 30000, // Request timeout in milliseconds
    });
    Option Type Default Description
    apiKey string required* Public API key (pk_live_* or pk_test_*)
    baseUrl string Full base URL for proxy mode (e.g. '/api/hiyve/identity'). When set, apiKey is not required
    environment string 'production' 'production' or 'development'
    basePath string '/identity/auth' API route prefix (ignored when baseUrl is set)
    tokenStorage string 'localStorage' 'localStorage' for persistent sessions, 'memory' for ephemeral. Production recommendation: use 'memory' to protect refresh tokens from XSS — see Security & Compliance
    autoRefresh boolean true Automatically refresh tokens before they expire
    refreshBuffer number 300 How many seconds before expiry to trigger auto-refresh
    timeout number 30000 HTTP request timeout in milliseconds

    *apiKey is required unless baseUrl is provided for proxy mode.

    Create a new user account. Depending on the organization's emailVerificationRequired setting, the server may send a verification email or mark the user as verified immediately.

    try {
    const { user } = await auth.register({
    email: 'user@example.com',
    password: 'securePassword123',
    name: 'Jane Doe', // optional
    metadata: { role: 'viewer' }, // optional -- custom data
    });

    console.log('Registered:', user.id);

    if (user.emailVerified) {
    // Email verification not required -- user can log in immediately
    console.log('Ready to log in');
    } else {
    // Email verification required -- tell user to check their inbox
    console.log('Check your email for a verification link');
    }
    } catch (err) {
    if (err.code === 'USER_ALREADY_EXISTS') {
    console.log('Email already registered');
    }
    }

    Note: Registration does not automatically log the user in. Call auth.login() after registration.

    Authenticate with email and password. On success, tokens are stored automatically and auto-refresh is started.

    try {
    const { user, accessToken, refreshToken, expiresIn } = await auth.login({
    email: 'user@example.com',
    password: 'securePassword123',
    });

    console.log('Welcome,', user.name);
    console.log('Token expires in', expiresIn, 'seconds');
    } catch (err) {
    if (err.code === 'INVALID_CREDENTIALS') {
    console.log('Wrong email or password');
    } else if (err.code === 'EMAIL_NOT_VERIFIED') {
    console.log('Please verify your email first');
    }
    }

    When a user logs in from an unrecognized device, the server returns a TFA challenge instead of tokens. The SDK stores the challenge internally and emits a tfaRequired event.

    const result = await auth.login({
    email: 'user@example.com',
    password: 'securePassword123',
    });

    if (result.tfaRequired) {
    // The response includes:
    // - challengeToken: stored internally by the SDK
    // - methods: ['email_otp'] -- available verification methods
    // - expiresIn: seconds until the challenge expires (default: 600)
    // - user: { id, email, name } -- the user being authenticated

    console.log('TFA required, methods:', result.methods);
    console.log('Code expires in', result.expiresIn, 'seconds');

    // Prompt the user for the OTP code from their email, then:
    const { user, accessToken, deviceId } = await auth.verifyTfa({ code: '123456' });
    console.log('Logged in as:', user.email);
    // The device is now trusted -- future logins from this device skip TFA
    } else {
    // Known device -- direct login (no TFA needed)
    const { user, accessToken } = result;
    console.log('Logged in as:', user.email);
    }

    Subsequent logins from the same device (within 90 days) will bypass TFA automatically.

    Invalidates the session server-side and clears all local tokens. The server call is best-effort -- local state is always cleared even if the network request fails.

    await auth.logout();
    console.log(auth.isAuthenticated()); // false

    Fetch the authenticated user's profile from the server.

    const { user } = await auth.getUser();
    console.log(user.email, user.name);

    This requires a valid access token. If the token is expired and auto-refresh is enabled, the SDK will attempt to refresh it before the request.

    Fetch or update the authenticated user's full profile, which includes additional fields beyond what getUser() returns (picture, phone, organization, metadata, roles, etc.).

    const { profile } = await auth.getProfile();
    console.log(profile.name, profile.email);
    console.log(profile.organization, profile.phone);
    console.log(profile.metadata); // custom key-value data
    const { profile } = await auth.updateProfile({
    name: 'Jane Smith',
    phone: '+1234567890',
    organization: 'Acme Corp',
    metadata: { department: 'Engineering', theme: 'dark' },
    });
    console.log('Updated:', profile.name);

    Updatable fields: name, picture, phone, organization, and metadata. Roles cannot be changed via this method.

    Manually refresh access and refresh tokens. This is handled automatically when autoRefresh is enabled, but you can call it explicitly if needed.

    const { accessToken, refreshToken, expiresIn } = await auth.refreshTokens();
    

    If the refresh token is invalid or expired, this throws an AuthError with code TOKEN_REFRESH_FAILED and clears the local auth state.

    await auth.requestPasswordReset({ email: 'user@example.com' });
    // Server sends an email with a reset link containing a token

    Extract the token from the reset link (typically a URL query parameter) and submit the new password:

    await auth.resetPassword({
    token: 'reset_token_from_email_link',
    password: 'newSecurePassword456',
    });

    Extract the token from the verification link and submit it:

    await auth.verifyEmail({ token: 'verification_token_from_email' });
    
    await auth.resendVerification({ email: 'user@example.com' });
    

    MCP tokens are long-lived API tokens for programmatic access (CI/CD pipelines, integrations, MCP server connections). All MCP token methods require the user to be logged in.

    const { rawToken, token } = await auth.createMCPToken({
    name: 'CI/CD Pipeline',
    permissions: ['mcp:data:read', 'mcp:search:execute'],
    expiresIn: '90d',
    ipRestriction: ['203.0.113.0/24'],
    });

    // Store rawToken securely -- it cannot be retrieved again
    console.log('Token:', rawToken);
    console.log('Token ID:', token.id);
    const { tokens } = await auth.listMCPTokens();
    tokens.forEach((t) => {
    console.log(t.id, t.metadata.name, t.expiresAt);
    });
    const { valid, token } = await auth.validateMCPToken(rawToken);
    if (valid) {
    console.log('Token is valid, permissions:', token.permissions);
    }
    await auth.revokeMCPToken(tokenId);
    

    The SDK supports the full OAuth 2.1 Authorization Code flow with PKCE (S256). This enables secure third-party integrations.

    Create and manage OAuth clients. All client management methods require the user to be logged in.

    // Create a client
    const { clientId, clientSecret, client } = await auth.createOAuthClient({
    clientName: 'My Integration',
    redirectUris: ['https://myapp.com/callback'],
    scopes: ['mcp:data:read'],
    });

    // List clients
    const { clients } = await auth.listOAuthClients();

    // Get a specific client
    const { client: details } = await auth.getOAuthClient(clientId);

    // Delete a client
    await auth.deleteOAuthClient(clientId);

    The full OAuth 2.1 flow with PKCE:

    // Step 1: Create an authorization code (user must be logged in)
    // PKCE verifier/challenge are generated automatically
    const { code, codeVerifier, state } = await auth.createAuthorizationCode({
    clientId: 'your-client-id',
    redirectUri: 'https://myapp.com/callback',
    scope: 'mcp:data:read',
    state: 'random-state-value',
    });

    // Step 2: Exchange the code for tokens (no login required)
    const tokens = await auth.exchangeAuthorizationCode({
    code,
    redirectUri: 'https://myapp.com/callback',
    codeVerifier,
    clientId: 'your-client-id',
    clientSecret: 'your-client-secret',
    });

    console.log(tokens.access_token);
    console.log(tokens.refresh_token);
    console.log(tokens.expires_in);

    // Step 3: Refresh OAuth tokens when they expire
    const newTokens = await auth.refreshOAuthToken({
    refreshToken: tokens.refresh_token,
    clientId: 'your-client-id',
    clientSecret: 'your-client-secret',
    });

    The SDK supports adaptive two-factor authentication. When TFA is enabled on the organization (settings.tfa.enabled), unknown devices trigger an email OTP challenge. Known (trusted) devices bypass TFA automatically. When TFA is disabled, login() always returns tokens directly.

    TFA and email verification are independent settings -- you can use either, both, or neither. See the Server Integration Guide for configuration details.

    Use the onTfaRequired event to react when login triggers a TFA challenge:

    // Listen for TFA events before calling login
    const unsubscribe = auth.onTfaRequired(({ methods, expiresIn, user }) => {
    console.log('TFA required for', user.email);
    console.log('Available methods:', methods); // ['email_otp']
    console.log('Code expires in', expiresIn, 'seconds');

    // Show your OTP input UI here
    });

    const result = await auth.login({ email, password });

    if (result.tfaRequired) {
    // The onTfaRequired callback has already fired
    // Wait for user to enter their OTP code...
    }

    After the user receives and enters the 6-digit code from their email:

    try {
    const { user, accessToken, deviceId } = await auth.verifyTfa({
    code: '123456',
    type: 'email_otp', // optional, defaults to 'email_otp'
    });

    console.log('Authenticated:', user.email);
    console.log('Device trusted:', deviceId);
    // This device is now registered as trusted for future logins
    } catch (err) {
    switch (err.code) {
    case 'TFA_INVALID_CODE':
    console.log('Wrong code, try again');
    break;
    case 'TFA_CHALLENGE_EXPIRED':
    console.log('Code expired, please log in again');
    break;
    case 'TFA_CHALLENGE_LOCKED':
    console.log('Too many attempts, please log in again');
    break;
    case 'INVALID_STATE':
    console.log('No pending TFA challenge -- call login() first');
    break;
    }
    }

    If the user didn't receive the code or it expired:

    const { message, expiresIn } = await auth.resendOtp();
    console.log(message); // 'New code sent'
    console.log('New code expires in', expiresIn, 'seconds');

    After successful TFA verification, the device is automatically registered as trusted. Users can view and manage their trusted devices.

    const { devices } = await auth.listTrustedDevices();
    devices.forEach((device) => {
    console.log(device.deviceId, device.deviceName, device.lastUsedAt);
    });
    await auth.revokeTrustedDevice('device-id-to-revoke');
    // The device will require TFA on next login
    const { count } = await auth.revokeAllTrustedDevices();
    console.log(`Revoked ${count} devices`);
    // All devices (including current) will require TFA on next login

    isAuthenticated() returns true if there is a non-expired access token stored locally. It does not make a network request.

    if (auth.isAuthenticated()) {
    // User has a valid token
    }

    Retrieve the raw access token string for use in custom API calls:

    const token = auth.getAccessToken();
    if (token) {
    fetch('https://your-api-host.com/api/data', {
    headers: { Authorization: `Bearer ${token}` },
    });
    }

    The authStateChange event fires on login, logout, and when a token refresh fails (which clears auth state):

    const unsubscribe = auth.onAuthStateChange(({ authenticated, user }) => {
    if (authenticated) {
    console.log('Signed in as', user.email);
    } else {
    console.log('Signed out');
    }
    });

    // Later: stop listening
    unsubscribe();
    Event Payload When
    authStateChange { authenticated: boolean, user: object | null } Login, logout, or refresh failure
    tokenRefreshed { accessToken: string, expiresIn: number } Successful token refresh
    error AuthError Token refresh failure
    tfaRequired { challengeToken: string, methods: string[], expiresIn: number, user: object } Login requires TFA verification

    The SDK handles tokens automatically, but here is a summary of the behavior:

    • Persistence: Sessions survive page reloads and new tabs when using localStorage storage (the default).
    • Auto-refresh: When enabled, tokens are refreshed refreshBuffer seconds before expiry. No action is needed from your code.
    • Expiry check: isAuthenticated() returns whether the access token is still valid, without making a network request.
    • Failure handling: If auto-refresh fails, auth state is cleared and an authStateChange event fires with { authenticated: false }.
    • Memory storage: Use tokenStorage: 'memory' for environments where localStorage is unavailable (SSR, tests, private browsing fallback). Tokens are lost on page reload. For production apps, 'memory' is recommended — it keeps refresh tokens out of localStorage, where they could be exfiltrated by XSS. The trade-off is that users must re-authenticate after a page reload.

    All SDK methods throw AuthError instances on failure. Each error has a code property from the AUTH_ERRORS constant, a human-readable message, and optionally a statusCode and details object.

    import { HiyveAuth, AuthError, AUTH_ERRORS } from '@hiyve/identity-client';

    try {
    await auth.login({ email: 'user@example.com', password: 'wrong' });
    } catch (err) {
    if (err instanceof AuthError) {
    switch (err.code) {
    case AUTH_ERRORS.INVALID_CREDENTIALS:
    showError('Invalid email or password');
    break;
    case AUTH_ERRORS.EMAIL_NOT_VERIFIED:
    showError('Please verify your email');
    break;
    case AUTH_ERRORS.RATE_LIMITED:
    showError('Too many attempts. Please wait.');
    break;
    case AUTH_ERRORS.NETWORK_ERROR:
    showError('Network error. Check your connection.');
    break;
    case AUTH_ERRORS.TIMEOUT:
    showError('Request timed out. Try again.');
    break;
    default:
    showError(err.message);
    }
    }
    }
    Code HTTP Status Description
    NETWORK_ERROR -- Network failure (no response received)
    TIMEOUT -- Request exceeded the timeout
    INVALID_CREDENTIALS 401 Wrong email or password
    TOKEN_EXPIRED 401 Access token has expired
    TOKEN_REFRESH_FAILED varies Refresh token is invalid or expired
    UNAUTHORIZED 401 Generic authentication failure
    FORBIDDEN 403 Insufficient permissions
    NOT_FOUND 404 Resource not found
    RATE_LIMITED 429 Too many requests
    VALIDATION_ERROR 400 Invalid input (check err.details)
    EMAIL_NOT_VERIFIED 401 Email address not yet verified
    USER_ALREADY_EXISTS 409 Email already registered
    SERVER_ERROR 5xx Unexpected server error
    INVALID_CONFIG -- Bad SDK configuration or destroyed instance
    INVALID_TOKEN -- Token is malformed or unreadable
    INVALID_GRANT 400 Invalid authorization code or grant type
    INVALID_SCOPE 400 Requested scope is not allowed
    TFA_REQUIRED 200 Login requires two-factor authentication
    TFA_INVALID_CODE 401 OTP code is incorrect
    TFA_CHALLENGE_EXPIRED 401 TFA challenge has expired
    TFA_CHALLENGE_LOCKED 429 Too many failed OTP attempts
    INVALID_STATE -- No pending TFA challenge (call login() first)

    When code is VALIDATION_ERROR, the details property contains field-level errors from the server:

    try {
    await auth.register({ email: 'bad', password: '123' });
    } catch (err) {
    if (err.code === AUTH_ERRORS.VALIDATION_ERROR) {
    console.log(err.details);
    // e.g. { email: '"email" must be a valid email', password: 'minimum 8 characters' }
    }
    }

    AuthError instances have a toJSON() method for logging:

    catch (err) {
    console.log(JSON.stringify(err));
    // { "name": "AuthError", "code": "INVALID_CREDENTIALS", "message": "...", "statusCode": 401, "details": null }
    }

    If you need to make custom requests to additional API endpoints that share the same API key and auth headers:

    import { HttpClient } from '@hiyve/identity-client';

    const client = new HttpClient({
    baseUrl: 'https://your-api-host.com/api',
    apiKey: 'pk_live_your_api_key',
    timeout: 15000,
    getAccessToken: () => auth.getAccessToken(),
    });

    // Unauthenticated request
    const data = await client.get('/public/info');

    // Authenticated request
    const profile = await client.get('/profile', { auth: true });

    // POST with body
    const result = await client.post('/items', { name: 'New Item' }, { auth: true });

    // PUT with body
    await client.put('/items/123', { name: 'Updated Item' }, { auth: true });

    // DELETE request
    await client.delete('/items/123', { auth: true });

    Every request automatically includes the X-Hiyve-Api-Key header. When { auth: true } is passed, the Authorization: Bearer <token> header is included. HTTP errors are mapped to AuthError instances.

    For advanced use cases (e.g., building a custom auth flow or integrating with a state management library):

    import { TokenManager } from '@hiyve/identity-client';

    const tokens = new TokenManager({
    storage: 'localStorage', // or 'memory'
    autoRefresh: true,
    refreshBuffer: 120, // refresh 2 minutes before expiry
    });

    // Store tokens from your own login endpoint
    tokens.setTokens({ accessToken: 'eyJ...', refreshToken: 'eyJ...' });

    // Check state
    tokens.isAuthenticated(); // true if access token is not expired
    tokens.getAccessToken(); // raw JWT string or null
    tokens.getRefreshToken(); // raw refresh token string or null

    // Decode JWT payload (no signature verification)
    const payload = tokens.decodeToken(tokens.getAccessToken());
    console.log(payload.sub, payload.email, payload.exp);

    // Get expiry as Unix timestamp (seconds)
    const exp = tokens.getTokenExpiry(tokens.getAccessToken());
    console.log('Expires at:', new Date(exp * 1000));

    // Clear everything
    tokens.clear();

    If you need to generate PKCE pairs for custom OAuth flows:

    import { PKCEHelper } from '@hiyve/identity-client';

    // Generate a PKCE pair
    const { codeVerifier, codeChallenge } = await PKCEHelper.generatePKCEPair();

    // Or generate components individually
    const verifier = PKCEHelper.generateCodeVerifier();
    const challenge = await PKCEHelper.generateCodeChallenge(verifier);

    Generates cryptographically secure PKCE pairs for OAuth 2.1 flows.

    If you need to access device fingerprinting independently:

    import { DeviceFingerprint } from '@hiyve/identity-client';

    // Collect browser signals
    const fingerprint = DeviceFingerprint.collect();
    console.log(fingerprint);
    // { screenResolution: '1920x1080', timezone: 'America/New_York', language: 'en-US', platform: 'MacIntel' }

    // Get or create a persistent device ID
    const deviceId = DeviceFingerprint.getDeviceId('localStorage', 'myapp_');

    // Encode fingerprint for transport
    const encoded = DeviceFingerprint.encode(fingerprint);
    // Base64-encoded JSON string

    The SDK uses DeviceFingerprint automatically during login() to send device recognition headers.

    Use in-memory storage for server-side rendering, testing, or environments without localStorage:

    const auth = new HiyveAuth({
    apiKey: 'pk_test_your_api_key',
    tokenStorage: 'memory',
    });

    If localStorage is configured but unavailable at runtime (e.g., private browsing restrictions), the SDK automatically falls back to memory storage.

    If your backend mounts the auth routes at a different path:

    const auth = new HiyveAuth({
    apiKey: 'pk_live_your_api_key',
    basePath: '/v2/auth', // overrides the default /identity/auth path
    });

    A typical pattern for React applications:

    import { useEffect, useState, createContext, useContext } from 'react';
    import { HiyveAuth } from '@hiyve/identity-client';
    
    // Create a singleton instance
    const auth = new HiyveAuth({
      apiKey: 'pk_live_your_api_key',
    });
    
    const AuthContext = createContext(null);
    
    export function AuthProvider({ children }) {
      const [state, setState] = useState({
        authenticated: auth.isAuthenticated(),
        user: null,
        loading: true,
      });
    
      useEffect(() => {
        // Listen for auth changes
        const unsubscribe = auth.onAuthStateChange(({ authenticated, user }) => {
          setState({ authenticated, user, loading: false });
        });
    
        // Load user if we have a token
        if (auth.isAuthenticated()) {
          auth.getUser()
            .then(({ user }) => setState({ authenticated: true, user, loading: false }))
            .catch(() => setState({ authenticated: false, user: null, loading: false }));
        } else {
          setState((s) => ({ ...s, loading: false }));
        }
    
        return () => {
          unsubscribe();
          auth.destroy();
        };
      }, []);
    
      return (
        
          {children}
        
      );
    }
    
    export function useAuth() {
      return useContext(AuthContext);
    }
    

    Usage in a component:

    function LoginPage() {
      const { auth } = useAuth();
      const [tfaPending, setTfaPending] = useState(false);
    
      useEffect(() => {
        return auth.onTfaRequired(() => setTfaPending(true));
      }, []);
    
      async function handleLogin(email, password) {
        try {
          const result = await auth.login({ email, password });
          if (result.tfaRequired) {
            // TFA UI will show via onTfaRequired callback
          }
          // If no TFA, AuthProvider updates state via onAuthStateChange
        } catch (err) {
          // Handle error
        }
      }
    
      async function handleVerifyOtp(code) {
        try {
          await auth.verifyTfa({ code });
          setTfaPending(false);
          // AuthProvider updates state via onAuthStateChange
        } catch (err) {
          // Handle TFA_INVALID_CODE, TFA_CHALLENGE_EXPIRED, etc.
        }
      }
    
      if (tfaPending) {
        return ;
      }
    
      // ... render login form
    }
    
    function Dashboard() {
      const { authenticated, user, loading } = useAuth();
    
      if (loading) return 
    Loading...
    ; if (!authenticated) return ; return
    Welcome, {user.name}
    ; }

    The main SDK class. Coordinates authentication flows, token storage, and event emission.

    new HiyveAuth(config: HiyveAuthConfig)
    

    Throws AuthError with code INVALID_CONFIG if neither apiKey nor baseUrl is provided, or if environment is invalid.

    Method Returns Description
    register({ email, password, name?, metadata? }) Promise<{ user }> Register a new user
    login({ email, password }) Promise<LoginResult> Log in (may return tokens or TFA challenge)
    logout() Promise<void> Log out and clear tokens
    refreshTokens() Promise<{ accessToken, refreshToken, expiresIn }> Manually refresh tokens
    getUser() Promise<{ user }> Fetch authenticated user's basic identity
    getProfile() Promise<{ profile }> Fetch the full user profile (name, picture, phone, organization, metadata, roles)
    updateProfile({ name?, picture?, phone?, organization?, metadata? }) Promise<{ profile }> Update the user's own profile
    requestPasswordReset({ email }) Promise<{ message }> Send password reset email
    resetPassword({ token, password }) Promise<{ message }> Reset password with token
    verifyEmail({ token }) Promise<{ message }> Verify email address
    resendVerification({ email }) Promise<{ message }> Resend verification email
    createMCPToken({ name?, permissions?, expiresIn?, ipRestriction? }) Promise<{ rawToken, token }> Create an MCP token
    listMCPTokens() Promise<{ tokens }> List MCP tokens
    revokeMCPToken(tokenId) Promise<{ message }> Revoke an MCP token
    validateMCPToken(token) Promise<{ valid, token? }> Validate a raw MCP token
    createOAuthClient({ clientName, redirectUris, scopes? }) Promise<{ clientId, clientSecret, client }> Create an OAuth client
    listOAuthClients() Promise<{ clients }> List OAuth clients
    getOAuthClient(clientId) Promise<{ client }> Get an OAuth client
    deleteOAuthClient(clientId) Promise<{ message }> Delete an OAuth client
    createAuthorizationCode({ clientId, redirectUri, scope?, state? }) Promise<{ code, codeVerifier, codeChallenge, state }> Create authorization code with auto-PKCE
    exchangeAuthorizationCode({ code, redirectUri, codeVerifier, clientId, clientSecret }) Promise<OAuthTokenResponse> Exchange code for tokens
    refreshOAuthToken({ refreshToken, clientId, clientSecret }) Promise<OAuthTokenResponse> Refresh OAuth tokens
    isAuthenticated() boolean Check if access token exists and is not expired
    getAccessToken() string | null Get the raw access token
    onAuthStateChange(callback) () => void Subscribe to auth state changes; returns unsubscribe function
    verifyTfa({ code, type? }) Promise<{ user, accessToken, refreshToken, expiresIn, deviceId }> Verify OTP code for pending TFA challenge
    resendOtp() Promise<{ message, expiresIn }> Resend OTP email for pending challenge
    onTfaRequired(callback) () => void Subscribe to TFA challenge events; returns unsubscribe function
    listTrustedDevices() Promise<{ devices }> List trusted devices for authenticated user
    revokeTrustedDevice(deviceId) Promise<{ message }> Revoke a specific trusted device
    revokeAllTrustedDevices() Promise<{ message, count }> Revoke all trusted devices
    destroy() void Stop auto-refresh, remove all listeners. Instance is unusable after this.

    Manages token storage, JWT payload decoding, and auto-refresh scheduling.

    new TokenManager(options?: TokenManagerOptions)
    
    Option Type Default Description
    storage string 'localStorage' 'localStorage' or 'memory'
    storagePrefix string -- Optional key prefix for localStorage
    autoRefresh boolean true Enable auto-refresh scheduling
    refreshBuffer number 300 Seconds before expiry to trigger refresh
    Method Returns Description
    setTokens({ accessToken, refreshToken }) void Store both tokens
    getAccessToken() string | null Retrieve the access token
    getRefreshToken() string | null Retrieve the refresh token
    clear() void Remove all tokens and stop auto-refresh
    isAuthenticated() boolean Check if access token exists and is not expired
    getTokenExpiry(token) number | null Decode exp from JWT (seconds since epoch)
    decodeToken(token) object | null Decode full JWT payload (no signature verification)
    startAutoRefresh(refreshFn) void Start auto-refresh with the given async function
    stopAutoRefresh() void Cancel the auto-refresh timer

    Fetch wrapper that adds API key headers, authorization, timeouts, and error mapping.

    new HttpClient(options: HttpClientOptions)
    
    Option Type Default Description
    baseUrl string required Full base URL for requests
    apiKey string required API key sent as X-Hiyve-Api-Key header
    timeout number 30000 Request timeout in milliseconds
    getAccessToken () => string | null () => null Function returning current access token
    Method Returns Description
    request(method, path, body?, options?) Promise<object> Make an HTTP request
    get(path, options?) Promise<object> GET request
    post(path, body?, options?) Promise<object> POST request
    put(path, body?, options?) Promise<object> PUT request
    delete(path, options?) Promise<object> DELETE request

    Request options:

    Option Type Default Description
    auth boolean false Include Authorization: Bearer <token> header
    timeout number -- Override the default timeout for this request
    headers Record<string, string> -- Additional headers to merge into the request

    All requests include credentials: 'include' for cookie-based flows and Content-Type: application/json.

    Static utility class for generating PKCE (Proof Key for Code Exchange) parameters for OAuth 2.1 authorization flows. Uses the Web Crypto API.

    Method Returns Description
    PKCEHelper.generateCodeVerifier() string Generate a cryptographically random code verifier (43+ chars, base64url)
    PKCEHelper.generateCodeChallenge(verifier) Promise<string> Compute S256 code challenge from a verifier (base64url-encoded SHA-256)
    PKCEHelper.generatePKCEPair() Promise<{ codeVerifier, codeChallenge }> Generate both verifier and challenge

    Static utility class for browser device fingerprinting. Used automatically by HiyveAuth.login() to send device recognition headers.

    Method Returns Description
    DeviceFingerprint.collect() DeviceFingerprintData Collect browser signals (screen, timezone, language, platform)
    DeviceFingerprint.getDeviceId(storageType?, prefix?) string | null Get stored device ID from localStorage or memory
    DeviceFingerprint.setDeviceId(storageType?, prefix?, id?) void Store a device ID
    DeviceFingerprint.encode(fingerprint) string Base64-encode fingerprint data for HTTP headers

    Minimal pub/sub event system used internally by HiyveAuth. Also exported for custom use.

    Method Returns Description
    on(event, callback) () => void Subscribe; returns unsubscribe function
    emit(event, data?) void Emit an event to all subscribers
    removeAllListeners(event?) void Remove listeners for one event, or all events
    listenerCount(event) number Count of listeners for an event

    Custom error class extending Error. All SDK errors are AuthError instances.

    Property Type Description
    name string Always 'AuthError'
    code string Error code from AUTH_ERRORS
    message string Human-readable message
    statusCode number | null HTTP status code (if applicable)
    details object | null Additional details (e.g., validation errors)

    Methods: toJSON() returns a plain object with all properties.

    Constant object mapping error names to string codes. Use for switch/if comparisons:

    import { AUTH_ERRORS } from '@hiyve/identity-client';

    AUTH_ERRORS.NETWORK_ERROR // 'NETWORK_ERROR'
    AUTH_ERRORS.TIMEOUT // 'TIMEOUT'
    AUTH_ERRORS.INVALID_CREDENTIALS // 'INVALID_CREDENTIALS'
    AUTH_ERRORS.TOKEN_EXPIRED // 'TOKEN_EXPIRED'
    AUTH_ERRORS.TOKEN_REFRESH_FAILED // 'TOKEN_REFRESH_FAILED'
    AUTH_ERRORS.UNAUTHORIZED // 'UNAUTHORIZED'
    AUTH_ERRORS.FORBIDDEN // 'FORBIDDEN'
    AUTH_ERRORS.NOT_FOUND // 'NOT_FOUND'
    AUTH_ERRORS.RATE_LIMITED // 'RATE_LIMITED'
    AUTH_ERRORS.VALIDATION_ERROR // 'VALIDATION_ERROR'
    AUTH_ERRORS.EMAIL_NOT_VERIFIED // 'EMAIL_NOT_VERIFIED'
    AUTH_ERRORS.USER_ALREADY_EXISTS // 'USER_ALREADY_EXISTS'
    AUTH_ERRORS.SERVER_ERROR // 'SERVER_ERROR'
    AUTH_ERRORS.INVALID_CONFIG // 'INVALID_CONFIG'
    AUTH_ERRORS.INVALID_TOKEN // 'INVALID_TOKEN'
    AUTH_ERRORS.INVALID_GRANT // 'INVALID_GRANT'
    AUTH_ERRORS.INVALID_SCOPE // 'INVALID_SCOPE'
    AUTH_ERRORS.TFA_REQUIRED // 'TFA_REQUIRED'
    AUTH_ERRORS.TFA_INVALID_CODE // 'TFA_INVALID_CODE'
    AUTH_ERRORS.TFA_CHALLENGE_EXPIRED // 'TFA_CHALLENGE_EXPIRED'
    AUTH_ERRORS.TFA_CHALLENGE_LOCKED // 'TFA_CHALLENGE_LOCKED'
    AUTH_ERRORS.INVALID_STATE // 'INVALID_STATE'

    The Hiyve Identity system passes 9 of 10 OWASP Top 10 (2021) categories with full compliance, covering broken access control, cryptographic controls, injection prevention, adaptive TFA, multi-tenant isolation, and layered rate limiting. One category (Security Logging & Monitoring) has partial compliance with structured audit logging on the roadmap.

    Key security highlights:

    • Layered rate limiting — Redis-backed, per-tenant, enforced across clustered deployments
    • Adaptive TFA — device fingerprinting with automatic OTP challenge for unknown devices
    • CSRF protection — HMAC-SHA256 double-submit cookies with timing-safe validation
    • Zero client dependencies — no supply-chain risk from transitive packages
    • Multi-tenant isolation — organization scoping on all queries, tokens, and rate-limit keys

    Token storage advisory: The default tokenStorage: 'localStorage' persists refresh tokens in localStorage, making them accessible to any JavaScript on the same origin. If your site includes third-party scripts (analytics, ads, support widgets), consider using tokenStorage: 'memory' to keep refresh tokens out of reach of XSS. The trade-off is that sessions will not survive page reloads. When using server-side proxy mode (baseUrl), you can implement httpOnly cookie-based refresh on your server for the strongest protection.

    See COMPLIANCE_REPORT.md for the full compliance report including OWASP mapping, cryptographic controls, architecture diagrams, open items, and a consumer security checklist.

    The SDK uses standard web APIs available in all modern browsers:

    • fetch and Response
    • AbortController
    • atob
    • localStorage (with automatic fallback to memory)
    • setTimeout / clearTimeout
    • crypto.subtle and crypto.getRandomValues (for PKCE generation)

    Minimum browser versions: Chrome 66+, Firefox 57+, Safari 12+, Edge 79+

    No polyfills are required for modern browsers. For legacy environments, you may need polyfills for fetch and AbortController.

    Commercial - IWantToPractice, LLC

    Classes

    AuthError
    DeviceFingerprint
    EventEmitter
    HiyveAuth
    HttpClient
    PKCEHelper
    TokenManager

    Interfaces

    AuthStateChange
    AuthUser
    CreateAuthorizationCodeParams
    CreateAuthorizationCodeResponse
    CreateMCPTokenParams
    CreateMCPTokenResponse
    CreateOAuthClientParams
    CreateOAuthClientResponse
    DeviceFingerprintData
    ExchangeAuthorizationCodeParams
    HiyveAuthConfig
    HttpClientOptions
    HttpRequestOptions
    LoginResponse
    MCPToken
    OAuthClient
    OAuthTokenResponse
    PKCEPair
    RefreshOAuthTokenParams
    RegisterResponse
    TfaRequiredEvent
    TfaRequiredResponse
    TfaVerifyResponse
    TokenManagerOptions
    TokenRefreshedEvent
    TrustedDevice
    UpdateProfileParams
    UserProfile
    ValidateMCPTokenResponse

    Type Aliases

    AuthErrorCode
    LoginResult

    Variables

    AUTH_ERRORS