Authentication

Integrate Authentication

This guide provides practical implementation patterns for integrating CashIn's Authentication endpoints. These endpoints handle user signup, signin, verification, and session management while maintaining seamless user experiences and proper instance tracking.

Authentication Endpoints

Endpoint

Method

Purpose

Key Parameters

Response Data

/partner/v1/auth/signup

POST

Register new user with tracking

email, name, handle, campaignId, existing_instance_id

user object, instance_id, instance_migrated

/partner/v1/auth/signin

POST

Initiate signin process

identifier, identifier_type, instance_id

user_found, verification_sent, verification_methods

/partner/v1/auth/signin/verify

POST

Complete signin with verification

instance_id, verification_type, verification_code

user object, authToken, insider, partnerRules

/partner/v1/auth/signin/resend

POST

Resend verification codes

instance_id, methods

sent_methods

/partner/v1/auth/signout

POST

Sign out user and invalidate session

instance_id

Success confirmation

/partner/v1/auth/context

POST

Check auto-authentication eligibility

url, integration_type, fingerprint

instance_id, is_authenticated, match_strength

Authentication Setup

All Authentication endpoints require Bearer token authentication using your partner secret key.

const headers = {
  'Authorization': 'Bearer sk_live_abc123def456...',
  'Content-Type': 'application/json',
  'X-Request-ID': generateUniqueId() // Recommended for debugging
};

Integration Patterns

Pattern 1: User Signup Flow

Handle new user registration with automatic instance migration and campaign attribution.

async function handleUserSignup(userData, instanceContext = {}) {
  const signupData = {
    email: userData.email,
    name: userData.name,
    handle: userData.handle || generateHandle(userData.name),
    social_media_handles: userData.socialHandles || {},
    campaignId: instanceContext.campaignId,
    fingerprint_id: instanceContext.fingerprintId,
    referrer_id: instanceContext.referrerId,
    existing_instance_id: localStorage.getItem('cashin_instance_id')
  };

  try {
    const response = await fetch('/partner/v1/auth/signup', {
      method: 'POST',
      headers,
      body: JSON.stringify(signupData)
    });

    const result = await response.json();

    if (result.success) {
      // Store new user data and instance information
      const { user, instance_id, instance_migrated } = result.data;
      
      // Update stored instance if migration occurred
      if (instance_migrated) {
        localStorage.setItem('cashin_instance_id', instance_id);
        console.log('Instance successfully migrated to authenticated user');
      }

      // Store user session data
      localStorage.setItem('cashin_user', JSON.stringify(user));
      localStorage.setItem('cashin_auth_token', user.id);

      return {
        success: true,
        user,
        instanceMigrated: instance_migrated
      };
    } else {
      return { success: false, error: result.message };
    }
  } catch (error) {
    console.error('Signup failed:', error);
    return { success: false, error: 'Network error during signup' };
  }
}

// Usage example
async function onSignupFormSubmit(formData) {
  const instanceContext = {
    campaignId: getUrlParameter('campaign_id'),
    fingerprintId: await generateFingerprint(),
    referrerId: getUrlParameter('ref')
  };

  const result = await handleUserSignup(formData, instanceContext);
  
  if (result.success) {
    showSuccessMessage('Account created successfully!');
    redirectToUserDashboard();
  } else {
    showErrorMessage(result.error);
  }
}

Pattern 2: Multi-Step Signin Flow

Implement the complete signin process with identifier validation and code verification.

class SigninFlowManager {
  constructor() {
    this.instanceId = localStorage.getItem('cashin_instance_id');
    this.signinState = {};
  }

  async initiateSignin(identifier, identifierType = 'email') {
    try {
      const response = await fetch('/partner/v1/auth/signin', {
        method: 'POST',
        headers,
        body: JSON.stringify({
          identifier,
          identifier_type: identifierType,
          instance_id: this.instanceId
        })
      });

      const result = await response.json();

      if (result.success) {
        this.signinState = {
          userFound: result.data.user_found,
          partnerRelationship: result.data.partner_relationship,
          verificationMethods: result.data.verification_methods,
          user: result.data.user
        };

        return {
          success: true,
          userFound: result.data.user_found,
          verificationSent: result.data.verification_sent,
          methods: result.data.verification_methods
        };
      }

      return { success: false, error: result.message };
    } catch (error) {
      console.error('Signin initiation failed:', error);
      return { success: false, error: 'Network error' };
    }
  }

  async verifySignin(verificationType, verificationData) {
    const requestBody = {
      instance_id: this.instanceId,
      verification_type: verificationType,
      ...verificationData
    };

    try {
      const response = await fetch('/partner/v1/auth/signin/verify', {
        method: 'POST',
        headers,
        body: JSON.stringify(requestBody)
      });

      const result = await response.json();

      if (result.success && result.data.verified) {
        // Store authenticated user data
        const { user, authToken, insider, identifiers, partnerRules } = result.data;
        
        localStorage.setItem('cashin_user', JSON.stringify(user));
        localStorage.setItem('cashin_auth_token', authToken);
        localStorage.setItem('cashin_insider_data', JSON.stringify(insider));
        localStorage.setItem('cashin_partner_rules', JSON.stringify(partnerRules));

        return {
          success: true,
          user,
          insider,
          partnerRules,
          instanceUpgraded: result.data.instance_upgraded
        };
      }

      return { success: false, error: result.message };
    } catch (error) {
      console.error('Signin verification failed:', error);
      return { success: false, error: 'Verification failed' };
    }
  }

  async resendVerificationCode(methods = ['email']) {
    try {
      const response = await fetch('/partner/v1/auth/signin/resend', {
        method: 'POST',
        headers,
        body: JSON.stringify({
          instance_id: this.instanceId,
          methods
        })
      });

      const result = await response.json();
      return {
        success: result.success,
        sentMethods: result.data?.sent_methods || []
      };
    } catch (error) {
      console.error('Resend failed:', error);
      return { success: false, error: 'Failed to resend codes' };
    }
  }
}

// Implementation example
async function handleSigninProcess() {
  const signinManager = new SigninFlowManager();
  const email = document.getElementById('email').value;

  // Step 1: Initiate signin
  const initResult = await signinManager.initiateSignin(email, 'email');
  
  if (!initResult.success) {
    showError(initResult.error);
    return;
  }

  if (!initResult.userFound) {
    // Redirect to signup flow
    redirectToSignup({ email });
    return;
  }

  // Step 2: Show verification UI
  showVerificationForm(initResult.methods);

  // Step 3: Handle verification
  document.getElementById('verify-btn').onclick = async () => {
    const code = document.getElementById('verification-code').value;
    
    const verifyResult = await signinManager.verifySignin('code', {
      verification_code: code,
      verification_method: 'email'
    });

    if (verifyResult.success) {
      showSuccess('Signed in successfully!');
      applyPartnerRules(verifyResult.partnerRules);
      redirectToUserDashboard();
    } else {
      showError(verifyResult.error);
    }
  };
}

Pattern 3: Authentication Context Detection

Implement smart authentication based on device fingerprints and URL context.

class AuthContextManager {
  async checkAuthenticationContext(url, integrationType = 'web_link') {
    const fingerprint = await this.generateDetailedFingerprint();

    try {
      const response = await fetch('/partner/v1/auth/context', {
        method: 'POST',
        headers,
        body: JSON.stringify({
          url,
          integration_type: integrationType,
          fingerprint
        })
      });

      const result = await response.json();

      if (result.success) {
        const { 
          instance_id, 
          is_authenticated, 
          match_strength, 
          discount_id, 
          referrer_id 
        } = result.data;

        return {
          shouldAutoAuth: is_authenticated && match_strength === 'high',
          instanceId: instance_id,
          discountId: discount_id,
          referrerId: referrer_id,
          matchStrength: match_strength
        };
      }

      return { shouldAutoAuth: false };
    } catch (error) {
      console.error('Auth context check failed:', error);
      return { shouldAutoAuth: false };
    }
  }

  async generateDetailedFingerprint() {
    return {
      id: await this.generateFingerprintId(),
      userAgent: navigator.userAgent,
      screen: {
        width: screen.width,
        height: screen.height,
        colorDepth: screen.colorDepth
      },
      timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
      language: navigator.language,
      platform: navigator.platform,
      cookieEnabled: navigator.cookieEnabled,
      doNotTrack: navigator.doNotTrack
    };
  }

  async generateFingerprintId() {
    const components = [
      navigator.userAgent,
      navigator.language,
      screen.width + 'x' + screen.height,
      new Date().getTimezoneOffset(),
      navigator.platform
    ];

    const fingerprint = components.join('|');
    const encoder = new TextEncoder();
    const data = encoder.encode(fingerprint);
    const hashBuffer = await crypto.subtle.digest('SHA-256', data);
    const hashArray = Array.from(new Uint8Array(hashBuffer));
    return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
  }
}

// Usage on page load
async function initializeAuthentication() {
  const authContext = new AuthContextManager();
  const currentUrl = window.location.href;
  
  const context = await authContext.checkAuthenticationContext(currentUrl);
  
  if (context.shouldAutoAuth) {
    // User should be automatically authenticated
    localStorage.setItem('cashin_instance_id', context.instanceId);
    
    if (context.discountId) {
      applyDiscount(context.discountId);
    }
    
    showAutoAuthSuccess();
  } else {
    // Show normal signin/signup options
    showAuthenticationOptions();
  }
}

Pattern 4: Session Management and Signout

Handle user signout and session cleanup properly.

async function handleUserSignout() {
  const instanceId = localStorage.getItem('cashin_instance_id');
  
  if (!instanceId) {
    // No active session to sign out
    clearLocalAuthData();
    return { success: true };
  }

  try {
    const response = await fetch('/partner/v1/auth/signout', {
      method: 'POST',
      headers,
      body: JSON.stringify({
        instance_id: instanceId
      })
    });

    const result = await response.json();
    
    if (result.success) {
      console.log('User signed out successfully');
    } else {
      console.warn('Signout API failed, but cleaning local data:', result.message);
    }
  } catch (error) {
    console.error('Signout request failed:', error);
  } finally {
    // Always clean up local storage regardless of API response
    clearLocalAuthData();
  }

  return { success: true };
}

function clearLocalAuthData() {
  const keysToRemove = [
    'cashin_instance_id',
    'cashin_user',
    'cashin_auth_token',
    'cashin_insider_data',
    'cashin_partner_rules',
    'cashin_expires_at'
  ];

  keysToRemove.forEach(key => localStorage.removeItem(key));
}

// Auto-signout on page unload
window.addEventListener('beforeunload', handleUserSignout);

// Manual signout button handler
document.getElementById('signout-btn')?.addEventListener('click', async () => {
  await handleUserSignout();
  showMessage('Signed out successfully');
  redirectToHomePage();
});

Complete Authentication Integration

Unified Authentication Manager

class CashInAuthManager {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.headers = {
      'Authorization': `Bearer ${apiKey}`,
      'Content-Type': 'application/json'
    };
    this.contextManager = new AuthContextManager();
    this.signinManager = new SigninFlowManager();
  }

  async initialize() {
    // Check for existing authentication context
    const context = await this.contextManager.checkAuthenticationContext(
      window.location.href
    );

    if (context.shouldAutoAuth) {
      return this.handleAutoAuthentication(context);
    }

    // Check for existing local session
    const existingUser = this.getCurrentUser();
    if (existingUser) {
      return this.validateExistingSession();
    }

    return { authenticated: false, method: 'none' };
  }

  async handleAutoAuthentication(context) {
    localStorage.setItem('cashin_instance_id', context.instanceId);
    
    if (context.discountId) {
      this.applyDiscount(context.discountId);
    }

    return {
      authenticated: true,
      method: 'auto',
      instanceId: context.instanceId,
      matchStrength: context.matchStrength
    };
  }

  async validateExistingSession() {
    const instanceId = localStorage.getItem('cashin_instance_id');
    
    if (!instanceId) {
      this.clearAuthData();
      return { authenticated: false, method: 'expired' };
    }

    // Validate instance is still active (using instance validation endpoint)
    try {
      const response = await fetch('/partner/v1/instance/validate', {
        method: 'POST',
        headers: this.headers,
        body: JSON.stringify({ instance_id: instanceId })
      });

      const result = await response.json();
      
      if (result.success && result.data.valid) {
        return {
          authenticated: true,
          method: 'existing',
          instance: result.data.instance
        };
      } else {
        this.clearAuthData();
        return { authenticated: false, method: 'expired' };
      }
    } catch (error) {
      console.error('Session validation failed:', error);
      return { authenticated: false, method: 'error' };
    }
  }

  getCurrentUser() {
    const userStr = localStorage.getItem('cashin_user');
    return userStr ? JSON.parse(userStr) : null;
  }

  getInsiderData() {
    const insiderStr = localStorage.getItem('cashin_insider_data');
    return insiderStr ? JSON.parse(insiderStr) : null;
  }

  getPartnerRules() {
    const rulesStr = localStorage.getItem('cashin_partner_rules');
    return rulesStr ? JSON.parse(rulesStr) : [];
  }

  applyDiscount(discountId) {
    // Implement your discount application logic
    console.log('Applying discount:', discountId);
    
    // Example: Add discount indicator to UI
    const discountBanner = document.createElement('div');
    discountBanner.className = 'cashin-discount-banner';
    discountBanner.textContent = 'Special CashIn discount applied!';
    document.body.prepend(discountBanner);
  }

  clearAuthData() {
    const keysToRemove = [
      'cashin_instance_id',
      'cashin_user',
      'cashin_auth_token',
      'cashin_insider_data',
      'cashin_partner_rules',
      'cashin_expires_at'
    ];

    keysToRemove.forEach(key => localStorage.removeItem(key));
  }

  async signout() {
    const instanceId = localStorage.getItem('cashin_instance_id');
    
    if (instanceId) {
      try {
        await fetch('/partner/v1/auth/signout', {
          method: 'POST',
          headers: this.headers,
          body: JSON.stringify({ instance_id: instanceId })
        });
      } catch (error) {
        console.error('Signout API call failed:', error);
      }
    }

    this.clearAuthData();
    return { success: true };
  }
}

Page Load Integration

// Initialize authentication on every page load
document.addEventListener('DOMContentLoaded', async () => {
  const authManager = new CashInAuthManager('sk_live_your_api_key');
  
  try {
    const authResult = await authManager.initialize();
    
    switch (authResult.method) {
      case 'auto':
        showAutoAuthMessage();
        setupAuthenticatedUI(authResult);
        break;
        
      case 'existing':
        console.log('Existing session restored');
        setupAuthenticatedUI(authResult);
        break;
        
      case 'expired':
      case 'none':
      default:
        setupUnauthenticatedUI();
        break;
    }
  } catch (error) {
    console.error('Authentication initialization failed:', error);
    setupUnauthenticatedUI();
  }
});

function setupAuthenticatedUI(authResult) {
  const authManager = new CashInAuthManager('sk_live_your_api_key');
  const user = authManager.getCurrentUser();
  const insider = authManager.getInsiderData();
  const rules = authManager.getPartnerRules();
  
  // Show user-specific content
  document.getElementById('user-name').textContent = user?.name || 'User';
  
  // Apply partner rules (discounts, special offers, etc.)
  rules.forEach(rule => {
    if (rule.isActive) {
      applyPartnerRule(rule);
    }
  });
  
  // Show insider-specific features
  if (insider?.verifiedStatus === 'verified') {
    showVerifiedInsiderBenefits();
  }
  
  // Setup signout functionality
  document.getElementById('signout-btn').onclick = async () => {
    await authManager.signout();
    location.reload();
  };
}

function setupUnauthenticatedUI() {
  // Show signin/signup options
  document.getElementById('auth-buttons').style.display = 'block';
  
  // Setup signin flow
  document.getElementById('signin-btn').onclick = () => {
    showSigninModal();
  };
  
  // Setup signup flow
  document.getElementById('signup-btn').onclick = () => {
    showSignupModal();
  };
}

Error Handling and Edge Cases

Comprehensive Error Handler

class AuthErrorHandler {
  static handleAuthError(error, context = {}) {
    console.error('Authentication error:', error, context);
    
    switch (error.type) {
      case 'network':
        return {
          userMessage: 'Connection error. Please check your internet and try again.',
          retry: true
        };
        
      case 'validation':
        return {
          userMessage: error.fields?.map(f => f.message).join(', ') || 'Invalid input',
          retry: false
        };
        
      case 'conflict':
        return {
          userMessage: 'Account already exists. Try signing in instead.',
          retry: false,
          redirect: 'signin'
        };
        
      case 'rate_limit':
        return {
          userMessage: 'Too many attempts. Please wait a moment and try again.',
          retry: true,
          delay: 30000
        };
        
      default:
        return {
          userMessage: 'Something went wrong. Please try again.',
          retry: true
        };
    }
  }

  static async handleWithRetry(apiCall, maxRetries = 3) {
    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      try {
        return await apiCall();
      } catch (error) {
        const handling = this.handleAuthError(error);
        
        if (!handling.retry || attempt === maxRetries) {
          throw error;
        }
        
        const delay = handling.delay || Math.pow(2, attempt) * 1000;
        await new Promise(resolve => setTimeout(resolve, delay));
      }
    }
  }
}

Testing Your Integration

Authentication Flow Tests

async function testAuthenticationFlow() {
  const authManager = new CashInAuthManager('sk_test_your_test_key');
  
  console.log('Testing authentication initialization...');
  const initResult = await authManager.initialize();
  console.log('Init result:', initResult);
  
  console.log('Testing signup flow...');
  const signupResult = await handleUserSignup({
    email: 'test@example.com',
    name: 'Test User',
    handle: 'testuser123'
  });
  console.log('Signup result:', signupResult);
  
  console.log('Testing signin flow...');
  const signinManager = new SigninFlowManager();
  const signinResult = await signinManager.initiateSignin('test@example.com');
  console.log('Signin result:', signinResult);
  
  console.log('Testing signout...');
  const signoutResult = await authManager.signout();
  console.log('Signout result:', signoutResult);
}

// Run tests in development environment
if (process.env.NODE_ENV === 'development') {
  window.testAuth = testAuthenticationFlow;
}

Best Practices

Security Considerations

  • Always validate user input before sending to APIs

  • Store minimal user data in localStorage

  • Clear sensitive data on signout or page close

  • Use HTTPS for all authentication requests

  • Implement proper CSRF protection

  • Validate instance states before critical operations

Performance Optimization

  • Cache authentication context checks for short periods

  • Debounce verification code resend requests

  • Preload authentication UI components

  • Use session storage for temporary verification states

  • Implement proper loading states for all auth operations

User Experience Guidelines

  • Minimize required fields during signup

  • Provide clear error messages with actionable steps

  • Show progress indicators during multi-step flows

  • Offer multiple verification methods when available

  • Remember user preferences (email vs phone verification)

  • Implement graceful degradation for failed auth operations

This integration guide provides comprehensive patterns for implementing CashIn's Authentication endpoints while maintaining excellent user experience and robust error handling.

Authentication Endpoint Summary

User Signup

  • Use Case: New user registration with campaign attribution

  • Returns: Complete user profile, migrated instance ID

  • Instance Effect: Upgrades anonymous instance to authenticated

  • Handles: Email conflicts, validation errors

Signin Initiation

  • Use Case: Start login process for existing users

  • Returns: User found status, available verification methods

  • Methods: Email, SMS, push notifications

  • Handles: Non-existent users, partner relationship validation

Signin Verification

  • Use Case: Complete authentication with code/password

  • Returns: Full user data, insider status, partner-specific rules

  • Types: Verification code, password authentication

  • Instance Effect: Upgrades instance to authenticated state

Resend Verification

  • Use Case: Re-send codes during signin process

  • Returns: Confirmation of sent methods

  • Methods: Can specify email, SMS, or both

  • Limitation: Tied to active signin session

User Signout

  • Use Case: Clean session termination

  • Returns: Success confirmation

  • Instance Effect: Revokes and invalidates instance

  • Security: Clears all session data

Authentication Context

  • Use Case: Smart auto-authentication for returning users

  • Returns: Authentication eligibility, discount/campaign data

  • Factors: Device fingerprint, URL context, match strength

  • Integration Types: cashin_app, mobile_app, web_link, external_link