Authentication

The Asterisms JS SDK provides a comprehensive authentication system that supports multiple authentication providers and flows.

Overview

Authentication in the Asterisms JS SDK is handled through the Auth Resource, which provides:

  • Multiple authentication provider support (SRP6, OAuth, etc.)
  • Secure token management with automatic refresh
  • Session persistence across browser sessions
  • Real-time authentication state monitoring

Basic Authentication Flow

1. Initialize Authentication

import { createAsterismsSDK } from '@asterisms/sdk';
import { SRP6AuthProvider } from '@asterisms/auth-provider-srp6';

const sdk = createAsterismsSDK({
  bundleId: 'your-app-bundle-id',
  rootDomain: 'your-domain.com',
  navigationAdapter: yourNavigationAdapter
});

// Configure authentication provider
const authProvider = new SRP6AuthProvider({
  serviceUrl: 'https://auth.yourapp.com'
});

sdk.auth.registerProvider('srp6', authProvider);

2. User Login

try {
  const result = await sdk.auth.login({
    provider: 'srp6',
    credentials: {
      username: 'user@example.com',
      password: 'userPassword'
    }
  });

  console.log('Login successful:', result.user);
} catch (error) {
  if (error instanceof InvalidTokenError) {
    console.error('Invalid credentials');
  } else {
    console.error('Login failed:', error.message);
  }
}

3. Check Authentication State

// Check if user is currently authenticated
const isAuthenticated = sdk.auth.isAuthenticated();

// Get current user information
if (isAuthenticated) {
  const user = sdk.auth.getCurrentUser();
  console.log('Current user:', user);
}

4. User Logout

await sdk.auth.logout();
console.log('User logged out successfully');

Authentication Providers

SRP6 Authentication Provider

The SRP6 (Secure Remote Password) provider offers password-based authentication without sending passwords over the network:

import { SRP6AuthProvider } from '@asterisms/auth-provider-srp6';

const srpProvider = new SRP6AuthProvider({
  serviceUrl: 'https://auth.yourapp.com/srp6',
  timeout: 30000,
  retryAttempts: 3
});

sdk.auth.registerProvider('srp6', srpProvider);

Custom Authentication Providers

You can create custom authentication providers by implementing the AuthProviderInterface:

import { AuthProviderInterface, AuthResult } from '@asterisms/sdk';

class CustomAuthProvider implements AuthProviderInterface {
  async authenticate(credentials: any): Promise<AuthResult> {
    // Implement your custom authentication logic
    const response = await fetch('/api/custom-auth', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(credentials)
    });

    if (!response.ok) {
      throw new Error('Authentication failed');
    }

    const data = await response.json();
    return {
      token: data.accessToken,
      refreshToken: data.refreshToken,
      user: data.user,
      expiresIn: data.expiresIn
    };
  }

  async refresh(refreshToken: string): Promise<AuthResult> {
    // Implement token refresh logic
  }

  async logout(): Promise<void> {
    // Implement logout logic
  }
}

// Register the custom provider
sdk.auth.registerProvider('custom', new CustomAuthProvider());

Authentication Events

Monitor authentication state changes in real-time:

// Listen for authentication state changes
sdk.auth.onAuthStateChanged((state) => {
  if (state.isAuthenticated) {
    console.log('User authenticated:', state.user);
    // Redirect to dashboard or update UI
  } else {
    console.log('User not authenticated');
    // Redirect to login or show login form
  }
});

// Listen for token refresh events
sdk.auth.onTokenRefresh((newToken) => {
  console.log('Token refreshed successfully');
});

// Listen for authentication errors
sdk.auth.onAuthError((error) => {
  console.error('Authentication error:', error);
  // Handle authentication failures
});

Token Management

The SDK automatically handles token storage and refresh:

Token Storage

// Tokens are automatically stored securely
// You can configure storage options
const sdk = createAsterismsSDK({
  bundleId: 'your-app',
  rootDomain: 'yourapp.com',
  navigationAdapter: yourAdapter,
  auth: {
    tokenStorage: 'localStorage', // or 'sessionStorage', 'memory'
    autoRefresh: true,
    refreshThreshold: 300 // Refresh 5 minutes before expiry
  }
});

Manual Token Operations

// Get current token
const token = sdk.auth.getToken();

// Check if token is valid
const isValid = sdk.auth.isTokenValid();

// Manually refresh token
try {
  await sdk.auth.refreshToken();
  console.log('Token refreshed successfully');
} catch (error) {
  console.error('Token refresh failed:', error);
  // Redirect to login
}

Authentication Guards

Protect routes and components with authentication guards:

// Simple authentication check
function requireAuth() {
  if (!sdk.auth.isAuthenticated()) {
    throw new Error('Authentication required');
  }
}

// Async authentication guard with automatic redirect
async function authGuard(redirectTo = '/login') {
  if (!sdk.auth.isAuthenticated()) {
    // Try to refresh token first
    try {
      await sdk.auth.refreshToken();
    } catch {
      // Redirect to login if refresh fails
      window.location.href = redirectTo;
      return false;
    }
  }
  return true;
}

// Usage in route handler
async function handleProtectedRoute() {
  if (!(await authGuard())) return;

  // Route is protected, user is authenticated
  // Continue with route logic
}

Framework Integration

SvelteKit Integration

// src/hooks.server.ts
import { authGuard } from '$lib/auth';

export async function handle({ event, resolve }) {
  if (event.url.pathname.startsWith('/protected')) {
    const isAuthenticated = await authGuard(event);
    if (!isAuthenticated) {
      return new Response('Unauthorized', { status: 401 });
    }
  }

  return resolve(event);
}

React Integration

// AuthProvider.tsx
import React, { createContext, useContext, useEffect, useState } from 'react';
import { sdk } from './sdk';

const AuthContext = createContext(null);

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

  useEffect(() => {
    const unsubscribe = sdk.auth.onAuthStateChanged((state) => {
      setUser(state.user);
      setLoading(false);
    });

    return unsubscribe;
  }, []);

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

export const useAuth = () => useContext(AuthContext);

Security Best Practices

  1. Use HTTPS: Always use HTTPS in production
  2. Secure Storage: Use secure token storage options
  3. Token Expiry: Set appropriate token expiration times
  4. Refresh Tokens: Implement secure refresh token rotation
  5. Error Handling: Don't expose sensitive information in error messages
// Example secure configuration
const sdk = createAsterismsSDK({
  bundleId: 'your-app',
  rootDomain: 'yourapp.com',
  navigationAdapter: yourAdapter,
  auth: {
    tokenStorage: 'localStorage',
    autoRefresh: true,
    refreshThreshold: 300,
    maxRetryAttempts: 3,
    secureTransport: true // Enforce HTTPS
  }
});

Troubleshooting

Common Issues

  1. Token Expired: Implement proper token refresh handling
  2. Network Errors: Add retry logic for network failures
  3. Storage Issues: Handle storage quota and permissions
  4. CORS Errors: Configure proper CORS headers on your backend

Debug Authentication

// Enable authentication debugging
const sdk = createAsterismsSDK({
  bundleId: 'your-app',
  rootDomain: 'yourapp.com',
  navigationAdapter: yourAdapter,
  logLevel: 'debug', // Enable debug logging
  auth: {
    debug: true // Enable authentication-specific debugging
  }
});

// Monitor authentication events for debugging
sdk.auth.onAuthStateChanged((state) => {
  console.log('Auth State:', state);
});

sdk.auth.onAuthError((error) => {
  console.error('Auth Error:', error);
});

Next Steps