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

@cloudwick/cams-auth-client

v0.0.2

Published

CAMS authentication client library for React applications with Context API and localStorage persistence

Readme

@cloudwick/cams-auth-client — Integration Guide

Comprehensive guide for integrating the CAMS authentication provider into your React application.


Table of Contents

  1. Overview
  2. Installation
  3. Quick Start
  4. Architecture
  5. Authentication Flows
  6. Step-by-Step Integration
  7. API Reference
  8. Types
  9. Configuration
  10. Utility Functions
  11. Features
  12. Security Considerations
  13. Exports Summary
  14. Migration Guide (AWS Amplify)
  15. Troubleshooting

Overview

@cloudwick/cams-auth-client is a self-contained React authentication library for CAMS (Amorphic Central Authentication Management System) that provides:

  • OAuth 2.0 Authorization Code Flow with PKCE support
  • Session Persistence via localStorage with cross-tab synchronization
  • Automatic Token Renewal on 401/403 responses
  • Session Validation at configurable intervals
  • Personal Access Token (PAT) Management with caching
  • Silent SSO via iframe-based auth bridge

Installation

npm install @cloudwick/cams-auth-client
# or
yarn add @cloudwick/cams-auth-client

Quick Start

import { CamsAuthProvider, AuthGuard } from "@cloudwick/cams-auth-client";

function App() {
  return (
    <CamsAuthProvider
      camsApiGateway="https://api.cams.example.com"
      appId="your-app-id"
      redirectUri={window.location.origin + "/auth/callback"}
      camsBridgeUrl="https://cams.example.com/bridge"
      camsLoginUrl="https://cams.example.com/login"
      onLoginSuccess={(user) => console.log("Logged in:", user.email)}
      onSessionExpired={() => console.log("Session expired")}
    >
      <AuthGuard fallback={<div>Authenticating...</div>} loader={<Spinner />}>
        <Dashboard />
      </AuthGuard>
    </CamsAuthProvider>
  );
}

The provider handles everything automatically. See Step-by-Step Integration for detailed setup.


Architecture

┌─────────────────────────────────────────────────────────────────────┐
│                        CamsAuthProvider                            │
│  ┌─────────────────┐    ┌──────────────┐    ┌──────────────────┐   │
│  │  useReducer     │◄──►│ LocalStore   │◄──►│ localStorage     │   │
│  │  (authReducer)  │    │ (persist +   │    │ (cams_auth_     │   │
│  │                 │    │  cross-tab)  │    │  state)          │   │
│  └────────┬────────┘    └──────────────┘    └──────────────────┘   │
│           │                                                         │
│           ▼                                                         │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │                    createHttpClient                          │   │
│  │  • Bearer token injection                                    │   │
│  │  • Auto-renewal on 401/403 + JWT expiry                     │   │
│  │  • Request timeout handling                                  │   │
│  └────────────────────────┬────────────────────────────────────┘   │
│                           │                                         │
│           ┌───────────────┴───────────────┐                        │
│           ▼                               ▼                         │
│  ┌─────────────────┐             ┌─────────────────┐               │
│  │   createAuthApi │             │   createPatApi  │               │
│  │   • signUp      │             │   • createToken │               │
│  │   • prepare     │             │   • listTokens  │               │
│  │   • exchange    │             │   • getDetails  │               │
│  │   • renew       │             │   • rotateToken │               │
│  │   • validate    │             └─────────────────┘               │
│  └─────────────────┘                                               │
└────────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────────┐
│                     CamsAuthContext                                │
│         { state, dispatch, authApi, patApi, config }               │
└─────────────────────────────────────────────────────────────────────┘
                              │
          ┌───────────────────┼───────────────────┐
          ▼                   ▼                   ▼
┌──────────────────┐ ┌────────────────┐ ┌─────────────────┐
│  useCamsAuth    │ │ useSession     │ │  usePatApi      │
│  • startLogin    │ │ Validator      │ │  • createToken  │
│  • startSignUp   │ │ • validateNow  │ │  • listTokens   │
│  • logout        │ │                │ │  (cached)       │
│  • forceRenewal  │ └────────────────┘ └─────────────────┘
└──────────────────┘
          │
          ▼
┌──────────────────┐
│   AuthGuard      │
│   (conditional   │
│    rendering)    │
└──────────────────┘

Directory Structure

cams-auth-provider/
├── src/
│   ├── index.ts                    # Public API barrel export
│   ├── CamsAuthProvider.tsx       # Root React provider
│   ├── CamsAuthContext.ts         # Context definition + hook
│   │
│   ├── api/
│   │   ├── httpClient.ts           # Fetch wrapper with auth
│   │   ├── authApi.ts              # Auth endpoints
│   │   ├── patApi.ts               # PAT CRUD endpoints
│   │   └── index.ts
│   │
│   ├── components/
│   │   ├── AuthGuard.tsx           # Conditional render component
│   │   └── index.ts
│   │
│   ├── hooks/
│   │   ├── useCamsAuth.ts         # Primary consumer hook
│   │   ├── useSessionValidator.ts  # Manual validation trigger
│   │   ├── usePatApi.ts            # PAT operations hook
│   │   └── index.ts
│   │
│   ├── services/
│   │   ├── loginService.ts         # Login flow orchestration
│   │   └── index.ts                # Iframe auth bridge
│   │
│   ├── store/
│   │   ├── authReducer.ts          # State reducer
│   │   ├── localStore.ts           # Persistence layer
│   │   └── index.ts
│   │
│   ├── types/
│   │   ├── auth.ts                 # Core auth types
│   │   ├── api.ts                  # API DTOs
│   │   ├── config.ts               # Configuration types
│   │   └── index.ts
│   │
│   ├── utils/
│   │   ├── tokenDecoder.ts         # JWT decode/expiry
│   │   ├── urlBuilder.ts           # URL construction
│   │   ├── errorExtractor.ts       # Error normalization
│   │   ├── patCache.ts             # PAT list caching
│   │   └── index.ts
│   │
│   └── constants/
│       └── index.ts                # Origin, login URL helpers
│
└── tests/                          # Unit tests

Authentication Flows

High-Level Flow

┌─────────────────────────────────────────────────────────────────────────────┐
│ App Loads                                                                   │
└─────────────────────────────────────────────────────────────────────────────┘
         │
         ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ Provider hydrates state from localStorage                                   │
│ - If valid tokens exist → isAuthenticated = true                            │
└─────────────────────────────────────────────────────────────────────────────┘
         │
         ▼ (no tokens)
┌─────────────────────────────────────────────────────────────────────────────┐
│ Check iframe bridge for existing CAMS session                              │
│ - Bridge responds → startSilentAuth() → redirect to CAMS                   │
│ - Bridge timeout → redirect to camsLoginUrl                                │
└─────────────────────────────────────────────────────────────────────────────┘
         │
         ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ CAMS redirects back with ?code=...&state=...                               │
│ Provider auto-detects and exchanges for tokens                              │
└─────────────────────────────────────────────────────────────────────────────┘
         │
         ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ User is authenticated                                                       │
│ - Tokens stored in localStorage                                             │
│ - Session auto-validates every 10 minutes (configurable)                    │
└─────────────────────────────────────────────────────────────────────────────┘

Standard Login Flow (Sequence Diagram)

┌─────────┐     ┌─────────────┐     ┌─────────────┐     ┌────────────┐
│  User   │     │    App      │     │  AuthBridge │     │   CAMS    │
│         │     │             │     │  (iframe)   │     │   Server   │
└────┬────┘     └──────┬──────┘     └──────┬──────┘     └─────┬──────┘
     │                 │                   │                   │
     │ Click Login     │                   │                   │
     │────────────────►│                   │                   │
     │                 │                   │                   │
     │                 │ createAuthBridge  │                   │
     │                 │──────────────────►│                   │
     │                 │                   │                   │
     │                 │    postMessage    │                   │
     │                 │◄──────────────────│                   │
     │                 │  { idp_hint }     │                   │
     │                 │                   │                   │
     │                 │ POST /auth/prepare                    │
     │                 │──────────────────────────────────────►│
     │                 │                   │                   │
     │                 │   { authorization_url }               │
     │                 │◄──────────────────────────────────────│
     │                 │                   │                   │
     │  Redirect to CAMS login            │                   │
     │◄────────────────│                   │                   │
     │                 │                   │                   │
     │  User authenticates with CAMS      │                   │
     │────────────────────────────────────────────────────────►│
     │                 │                   │                   │
     │  Redirect back with code + state    │                   │
     │◄────────────────────────────────────────────────────────│
     │                 │                   │                   │
     │  App receives callback              │                   │
     │────────────────►│                   │                   │
     │                 │                   │                   │
     │                 │ POST /auth/callback                   │
     │                 │──────────────────────────────────────►│
     │                 │                   │                   │
     │                 │   { tokens }                          │
     │                 │◄──────────────────────────────────────│
     │                 │                   │                   │
     │                 │ GET /me (user info)                   │
     │                 │──────────────────────────────────────►│
     │                 │                   │                   │
     │                 │   { user }                            │
     │                 │◄──────────────────────────────────────│
     │                 │                   │                   │
     │  Authenticated  │                   │                   │
     │◄────────────────│                   │                   │

Auto-Login Flow (?auto_auth=true)

When the app loads with ?auto_auth=true query param:

  1. Provider detects query param
  2. Initiates startLoginFlow automatically
  3. Auth bridge attempts silent SSO
  4. If SSO succeeds → user authenticated
  5. If SSO fails → redirects to CAMS login

Token Renewal Flow

┌──────────────┐     ┌──────────────┐     ┌────────────┐
│  HttpClient  │     │   AuthApi    │     │   CAMS    │
└──────┬───────┘     └──────┬───────┘     └─────┬──────┘
       │                    │                   │
       │ API Request (401/403)                  │
       │◄──────────────────────────────────────►│
       │                    │                   │
       │ Check: isTokenExpired(opaqueToken)?    │
       │────────────────────┤                   │
       │                    │                   │
       │ POST /auth/renew   │                   │
       │───────────────────►│──────────────────►│
       │                    │                   │
       │   { new tokens }   │◄──────────────────│
       │◄───────────────────│                   │
       │                    │                   │
       │ Retry original request                 │
       │───────────────────────────────────────►│
       │                    │                   │
       │   { response }                         │
       │◄──────────────────────────────────────│

Session Validation Flow

Periodic validation ensures session is still active server-side:

  1. Provider schedules interval (default: 10 minutes)
  2. Calls authApi.validateSession(appId, idToken)
  3. If invalid → dispatches INVALIDATE_SESSION, clears storage, fires onSessionExpired
  4. If valid → updates lastValidatedAt, fires onSessionValidated

Step-by-Step Integration

Step 1: Install Package

npm install @cloudwick/cams-auth-client
# or
yarn add @cloudwick/cams-auth-client

Step 2: Configure Provider

// src/config/auth.ts
import { CamsAuthConfig } from '@cloudwick/cams-auth-client';

export const authConfig: CamsAuthConfig = {
  // Required
  camsApiGateway: 'https://api.cams.example.com',
  appId: 'your-application-id',
  redirectUri: 'https://your-app.com/auth/callback',
  camsLoginUrl: 'https://login.cams.example.com',

  // Optional
  validationInterval: 600000,      // 10 minutes (default)
  enableAutoValidation: true,      // default: true

  // Lifecycle callbacks
  onLoginSuccess: (user) => {
    console.log('Login success:', user);
    analytics.track('login', { userId: user.id });
  },
  onLoginError: (error) => {
    console.error('Login failed:', error);
  },
  onSessionExpired: () => {
    toast.error('Session expired. Please log in again.');
  },
  onTokenRenewed: (tokens) => {
    console.log('Tokens renewed');
  },
  onStateChange: (state) => {
    // State changed (useful for debugging)
  },
};

Step 3: Wrap Application

// src/App.tsx
import { CamsAuthProvider } from '@cloudwick/cams-auth-client';
import { authConfig } from './config/auth';

function App() {
  return (
    <CamsAuthProvider config={authConfig}>
      <Router>
        <Routes />
      </Router>
    </CamsAuthProvider>
  );
}

Step 4: Protect Routes

// src/components/ProtectedRoute.tsx
import { AuthGuard } from '@cloudwick/cams-auth-client';

function ProtectedRoute({ children }) {
  return (
    <AuthGuard
      fallback={<Navigate to="/login" />}
      loader={<LoadingSpinner />}
    >
      {children}
    </AuthGuard>
  );
}

Step 5: Use Auth Hook

// src/components/Header.tsx
import { useCamsAuth } from '@cloudwick/cams-auth-client';

function Header() {
  const { isAuthenticated, user, startLogin, logout } = useCamsAuth();

  return (
    <header>
      {isAuthenticated ? (
        <>
          <span>Welcome, {user?.name}</span>
          <button onClick={logout}>Logout</button>
        </>
      ) : (
        <button onClick={startLogin}>Login</button>
      )}
    </header>
  );
}

Step 6: Manual Session Validation (Optional)

import { useSessionValidator } from '@cloudwick/cams-auth-client';

function Dashboard() {
  const { validateNow, lastValidatedAt } = useSessionValidator();

  return (
    <div>
      <p>Last validated: {lastValidatedAt}</p>
      <button onClick={validateNow}>Validate Now</button>
    </div>
  );
}

Step 7: PAT Management (Optional)

// src/components/TokenManager.tsx
import { usePatApi } from '@cloudwick/cams-auth-client';

function TokenManager() {
  const { createToken, listTokens, invalidateCache } = usePatApi();

  const handleCreate = async () => {
    const result = await createToken({
      name: 'My API Token',
      expiry: '2025-12-31',
    });
    invalidateCache(); // Clear cache after mutation
  };

  const tokens = await listTokens(); // Cached for 15 minutes

  return (/* ... */);
}

API Reference

CamsAuthProvider Props

| Prop | Type | Required | Default | Description | |------|------|----------|---------|-------------| | camsApiGateway | string | Yes | - | Base URL of the CAMS API gateway | | appId | string | Yes | - | Your application ID registered with CAMS | | redirectUri | string | Yes | - | OAuth callback URL | | camsBridgeUrl | string | No | - | URL of CAMS iframe bridge for session detection | | camsLoginUrl | string | No | - | URL to redirect when no session exists | | bridgeTimeout | number | No | 3000 | Max wait time for bridge response (ms) | | validationInterval | number | No | 600000 | Session validation interval (ms) | | enableAutoValidation | boolean | No | true | Auto-validate session periodically | | onLoginStart | (email) => void | No | - | Fired when auth flow begins | | onLoginSuccess | (user, tokens) => void | No | - | Fired on successful authentication | | onLoginError | (error) => void | No | - | Fired on auth failure | | onLogout | () => void | No | - | Fired after logout | | onTokenRenewed | (tokens) => void | No | - | Fired when tokens refreshed | | onTokenRenewalError | (error) => void | No | - | Fired on token refresh failure | | onSessionExpired | () => void | No | - | Fired when session expires | | onSessionValidated | (isValid) => void | No | - | Fired after validation check | | onStateHydrated | (state) => void | No | - | Fired after localStorage hydration | | onStateChange | (state, prev) => void | No | - | Fired on any state change | | onError | (error, context) => void | No | - | General error handler | | onAuthBridgeFailed | () => void | No | - | Fired when bridge times out |

AuthApi Methods

| Method | Endpoint | Description | |--------|----------|-------------| | signUp(params) | POST /auth/signup | Register new user | | prepare(params) | POST /auth/prepare | Get authorization URL | | exchangeAuthCode(code, state) | POST /auth/callback | Exchange code for tokens | | renewToken(params) | POST /auth/renew | Refresh expired tokens | | validateSession(appId, token) | GET /auth/validate | Check session validity |

PatApi Methods

| Method | Endpoint | Description | |--------|----------|-------------| | createToken(params) | POST /me/pats | Create new PAT | | listTokens(params?) | GET /me/pats | List all PATs |

useCamsAuth Hook

const {
  isAuthenticated,  // boolean - true if user has valid session
  isLoading,        // boolean - true during auth operations
  validSession,     // boolean - session validity flag
  sessionActive,    // boolean - session active state
  user,             // UserInfo - { userId, email, sessionId }
  tokens,           // TokenSet | null - { idToken, opaqueToken, accessToken }
  startLogin,       // (email: string, idpHint?: string) => Promise<PrepareAuthResponse>
  startSignUp,      // () => void
  startSilentLogin, // (email?: string) => Promise<void>
  handleCallback,   // (code: string, state: string) => Promise<void>
  logout,           // () => void
  forceTokenRenewal // () => Promise<boolean>
} = useCamsAuth();

useSessionValidator Hook

const {
  validateNow,      // () => Promise<boolean> - manually validate session
  lastValidatedAt,  // string | null - ISO timestamp of last validation
  isValidating      // boolean - true during validation
} = useSessionValidator();

usePatApi Hook

const {
  createToken,      // (params) => Promise<CreateTokenResponse>
  listTokens,       // (params?) => Promise<TokenList> - Cached (15 min TTL)
  invalidateCache,  // () => void - Clear PAT cache
} = usePatApi();

AuthGuard Component

<AuthGuard
  fallback={<LoginPage />}    // Shown when not authenticated
  loader={<LoadingSpinner />} // Shown while loading (optional)
>
  <ProtectedContent />
</AuthGuard>

| Prop | Type | Description | |------|------|-------------| | children | ReactNode | Rendered when authenticated | | fallback | ReactNode | Rendered when not authenticated | | loader | ReactNode? | Rendered during loading state |


Types

AuthState

interface AuthState {
  isAuthenticated: boolean;
  isLoading: boolean;
  validSession: boolean;
  sessionActive: boolean;
  user: UserInfo | null;
  tokens: TokenSet | null;
  lastValidatedAt: number | null;
  error: string | null;
}

TokenSet

interface TokenSet {
  idToken: string;
  opaqueToken: string;
  accessToken?: string;
  refreshToken?: string;
  expiresAt?: number;
}

UserInfo

interface UserInfo {
  userId: string | null;
  email: string | null;
}

Reducer Actions

| Action | Payload | Effect | |--------|---------|--------| | SET_TOKENS | TokenSet | Store tokens, set authenticated | | SET_SESSION | boolean | Update session validity | | SET_USER_INFO | UserInfo | Store user data | | SET_LOADING | boolean | Toggle loading state | | INVALIDATE_SESSION | — | Clear auth state | | UPDATE_VALIDATION | number | Update lastValidatedAt | | HYDRATE | AuthState | Restore from storage | | RESET | — | Full state reset |


Configuration

CamsAuthConfig Interface

interface CamsAuthConfig {
  // Required
  camsApiGateway: string;      // API base URL
  appId: string;                // Application identifier
  redirectUri: string;          // OAuth callback URL
  camsLoginUrl: string;        // CAMS login page URL

  // Optional
  validationInterval?: number;      // Session check interval (ms), default: 600000
  enableAutoValidation?: boolean;   // Auto-validate session, default: true

  // Lifecycle Callbacks
  onLoginStart?: () => void;
  onLoginSuccess?: (user: UserInfo) => void;
  onLoginError?: (error: Error) => void;
  onSignUpStart?: () => void;
  onSignUpError?: (error: Error) => void;
  onLogout?: () => void;
  onTokenRenewed?: (tokens: TokenSet) => void;
  onTokenRenewalError?: (error: Error) => void;
  onSessionExpired?: () => void;
  onSessionValidated?: () => void;
  onStateHydrated?: (state: AuthState) => void;
  onStateChange?: (state: AuthState) => void;
  onError?: (error: Error) => void;
}

Defaults

const DEFAULT_VALIDATION_INTERVAL = 600000;  // 10 minutes
const DEFAULT_REQUEST_TIMEOUT = 35000;       // 35 seconds
const DEFAULT_PAT_CACHE_TTL = 900000;        // 15 minutes

Utility Functions

Token Utilities

// Decode JWT payload
decodeToken<T>(token: string): T | null;

// Check if JWT is expired
isTokenExpired(token: string): boolean;

// Get expiry timestamp from JWT
getTokenExpiry(token: string): number | null;

URL Utilities

// Build URL with path segments
urlBuilder(base: string, ...paths: string[]): string;

// Format query parameters
formatQueryParams(params: Record<string, string>): string;

Error Utilities

// Extract error message from various error shapes
extractMessage(error: unknown): string;

// Extract structured error object
extractError(error: unknown): { message: string; code?: string };

Constants

// Current window origin
currentOrigin: string;

// Build unauthenticated redirect URL
unAuthenticatedCamsUri(baseUrl: string): string;
// Returns: baseUrl + ?auto_auth=true

Features

Auto OAuth Callback Handling

Provider automatically detects code and state URL params and exchanges them for tokens. No need to create a separate callback route.

Cross-Tab Sync

Auth state syncs across browser tabs via localStorage events. Login/logout in one tab updates all others instantly.

Token Renewal

HTTP client automatically renews tokens on 401/403 responses. Failed renewal triggers onSessionExpired.

Storage

Auth state persists in localStorage under key {domain_origin}:cams_auth_state.

Cross-Tab Sync: The storage event listener ensures state changes in one tab propagate to others.

// localStore.ts
const STORAGE_KEY = `${window.location.hostname.split(".").join("_")}:cams_auth_state`;

interface LocalStore {
  getState(): AuthState | null;
  setState(state: AuthState): void;
  subscribe(listener: (state: AuthState) => void): () => void;
  clear(): void;
  hydrate(): AuthState | null;
}

Security Considerations

Token Storage

  • Tokens stored in localStorage (accessible to JavaScript)
  • For enhanced security, consider:
    • HttpOnly cookies for refresh tokens
    • Shorter access token lifetimes
    • Secure/SameSite cookie attributes

Cross-Tab Communication

  • Uses native storage events (no broadcast channels)
  • State sync may have slight delay between tabs
  • Logout in one tab triggers logout across all tabs

Session Validation

  • Validates against server at regular intervals
  • Uses idToken (not access token) for validation
  • Clears local state if server reports invalid session

Auth Bridge (iframe SSO)

  • Hidden iframe communicates via postMessage
  • Targets CAMS /authBridge.html endpoint
  • Returns idp_hint for silent authentication
  • Falls back to redirect flow if bridge fails

Error Handling

  • All API errors normalized via extractError
  • Token renewal failures trigger onTokenRenewalError
  • Session invalidation clears all local state
  • Network errors surface through onError callback

Exports Summary

// Provider & Context
export { CamsAuthProvider, CamsAuthProviderProps };
export { CamsAuthContext, useCamsAuthContext, CamsAuthContextValue };

// Hooks
export { useCamsAuth, useSessionValidator, usePatApi };

// Components
export { AuthGuard, AuthGuardProps };

// Services
export { createAuthBridgeWithCams, startLoginFlow, StartLoginParams };

// Store
export { createLocalStore, LocalStore, authReducer, AuthAction };

// Types
export type {
  AuthState, TokenSet, UserInfo,
  SessionInvalidError, TokenRenewalError,
  CamsAuthConfig, HttpClientConfig, RequestOptions,
  // ... all API DTOs
};

// Utils
export { decodeToken, isTokenExpired, getTokenExpiry };
export { urlBuilder, formatQueryParams };
export { extractError, extractMessage };

// Constants
export { currentOrigin, unAuthenticatedCamsUri };
export { DEFAULT_VALIDATION_INTERVAL, DEFAULT_REQUEST_TIMEOUT };

Migration Guide (AWS Amplify)

This section covers migrating from aws-amplify SDK to @cloudwick/cams-auth-client.

Package Changes

Remove:

npm uninstall aws-amplify @aws-amplify/auth @aws-amplify/core
# or
yarn remove aws-amplify @aws-amplify/auth @aws-amplify/core

Add:

npm install @cloudwick/cams-auth-client
# or
yarn add @cloudwick/cams-auth-client

Code Migration Patterns

| AWS Amplify | CAMS Auth Client | |-------------|-------------------| | Amplify.configure({ Auth: {...} }) | <CamsAuthProvider config={...}> | | Auth.signIn(email, password) | startLogin(email) | | Auth.signOut() | logout() | | Auth.currentAuthenticatedUser() | useCamsAuth().user | | Auth.currentSession() | useCamsAuth().tokens | | Auth.fetchAuthSession() | useCamsAuth().tokens | | Hub.listen('auth', callback) | Provider callbacks: onLoginSuccess, onSessionExpired | | Auth.forgotPassword(email) | Redirect to CAMS forgot password URL | | Auth.completeNewPassword() | Handled by CAMS login flow |

Step-by-Step Migration

1. Remove Amplify Configuration

Before (Amplify):

// src/main.tsx or src/App.tsx
import { Amplify } from 'aws-amplify';

Amplify.configure({
  Auth: {
    Cognito: {
      userPoolId: 'us-west-2_xxxxx',
      userPoolClientId: 'xxxxxx',
      signUpVerificationMethod: 'code',
    }
  }
});

function App() {
  return <YourApp />;
}

After (CAMS):

// src/main.tsx or src/App.tsx
import { CamsAuthProvider } from '@cloudwick/cams-auth-client';

function App() {
  return (
    <CamsAuthProvider
      camsApiGateway={import.meta.env.VITE_CAMS_API_URL}
      appId={import.meta.env.VITE_CAMS_APP_ID}
      redirectUri={`${window.location.origin}/auth/callback`}
      camsLoginUrl={import.meta.env.VITE_CAMS_LOGIN_URL}
      onLoginSuccess={(user) => console.log('Logged in:', user)}
      onSessionExpired={() => console.log('Session expired')}
    >
      <YourApp />
    </CamsAuthProvider>
  );
}

2. Replace Auth Imports

Before (Amplify):

import { signIn, signOut, getCurrentUser, fetchAuthSession } from 'aws-amplify/auth';
import { Hub } from 'aws-amplify/utils';

After (CAMS):

import { useCamsAuth } from '@cloudwick/cams-auth-client';

3. Replace Auth Calls

Before (Amplify):

function LoginButton() {
  const handleLogin = async () => {
    try {
      await signIn({ username: email, password });
      const user = await getCurrentUser();
      console.log('Logged in:', user);
    } catch (error) {
      console.error('Login failed:', error);
    }
  };

  const handleLogout = async () => {
    await signOut();
  };

  return (
    <>
      <button onClick={handleLogin}>Login</button>
      <button onClick={handleLogout}>Logout</button>
    </>
  );
}

After (CAMS):

function LoginButton() {
  const { startLogin, logout, isAuthenticated, user } = useCamsAuth();

  return (
    <>
      {isAuthenticated ? (
        <>
          <span>Welcome, {user?.email}</span>
          <button onClick={logout}>Logout</button>
        </>
      ) : (
        <button onClick={() => startLogin(email)}>Login</button>
      )}
    </>
  );
}

4. Replace Hub Listeners

Before (Amplify):

useEffect(() => {
  const unsubscribe = Hub.listen('auth', ({ payload }) => {
    switch (payload.event) {
      case 'signedIn':
        console.log('User signed in');
        break;
      case 'signedOut':
        console.log('User signed out');
        break;
      case 'tokenRefresh':
        console.log('Token refreshed');
        break;
    }
  });
  return unsubscribe;
}, []);

After (CAMS):

// Configure callbacks in provider props
<CamsAuthProvider
  onLoginSuccess={(user) => console.log('User signed in:', user)}
  onLogout={() => console.log('User signed out')}
  onTokenRenewed={(tokens) => console.log('Token refreshed')}
  onSessionExpired={() => console.log('Session expired')}
>

5. Replace Protected Routes

Before (Amplify with custom hook):

function ProtectedRoute({ children }) {
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    getCurrentUser()
      .then(() => setIsAuthenticated(true))
      .catch(() => setIsAuthenticated(false))
      .finally(() => setLoading(false));
  }, []);

  if (loading) return <Spinner />;
  if (!isAuthenticated) return <Navigate to="/login" />;
  return children;
}

After (CAMS):

import { AuthGuard } from '@cloudwick/cams-auth-client';

function ProtectedRoute({ children }) {
  return (
    <AuthGuard
      fallback={<Navigate to="/login" />}
      loader={<Spinner />}
    >
      {children}
    </AuthGuard>
  );
}

6. Replace Token Access

Before (Amplify):

const session = await fetchAuthSession();
const idToken = session.tokens?.idToken?.toString();
const accessToken = session.tokens?.accessToken?.toString();

// In API calls
fetch('/api/data', {
  headers: {
    Authorization: `Bearer ${accessToken}`
  }
});

After (CAMS):

const { tokens } = useCamsAuth();
const { idToken, accessToken, opaqueToken } = tokens || {};

// Token injection handled automatically by httpClient
// Or use tokens directly if needed

Files to Check During Migration

  • App entry point (main.tsx, App.tsx) - Remove Amplify.configure()
  • Auth configuration files - Delete Amplify config files
  • Components using auth - Replace signIn/signOut calls
  • Protected route wrappers - Use AuthGuard component
  • API interceptors - Remove manual token injection (handled by provider)
  • Event listeners - Replace Hub.listen with provider callbacks

Environment Variables

Before (Amplify):

VITE_COGNITO_USER_POOL_ID=us-west-2_xxxxx
VITE_COGNITO_CLIENT_ID=xxxxxx
VITE_COGNITO_REGION=us-west-2

After (CAMS):

VITE_CAMS_API_URL=https://api.cams.example.com
VITE_CAMS_APP_ID=your-app-id
VITE_CAMS_LOGIN_URL=https://cams.example.com/login
VITE_CAMS_BRIDGE_URL=https://cams.example.com/bridge

Troubleshooting

Common Issues

1. "useCamsAuthContext must be used within CamsAuthProvider"

  • Ensure CamsAuthProvider wraps your component tree

2. Infinite redirect loop on callback

  • Verify redirectUri matches registered OAuth callback
  • Check state parameter consistency

3. Session expires immediately after login

  • Verify appId matches server configuration
  • Check server-side session timeout settings

4. Cross-tab logout not working

  • Ensure localStorage access isn't blocked
  • Verify same origin policy compliance

5. Token renewal fails

  • Check refreshToken presence in token set
  • Verify /auth/renew endpoint availability

6. Auth bridge timeout

  • Verify camsBridgeUrl is correct
  • Check CORS configuration on bridge endpoint
  • Increase bridgeTimeout if network is slow

7. Silent SSO not working

  • Ensure cookies are enabled
  • Check third-party cookie policies
  • Verify CAMS session exists in parent domain

Complete Example

// src/main.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import { CamsAuthProvider, AuthGuard, useCamsAuth } from "@cloudwick/cams-auth-client";

function Dashboard() {
  const { user, logout } = useCamsAuth();
  return (
    <div>
      <h1>Welcome, {user?.email}</h1>
      <button onClick={logout}>Logout</button>
    </div>
  );
}

function LoadingScreen() {
  return <div>Authenticating with CAMS...</div>;
}

function App() {
  return (
    <CamsAuthProvider
      camsApiGateway={import.meta.env.VITE_CAMS_API_URL}
      appId={import.meta.env.VITE_CAMS_APP_ID}
      redirectUri={`${window.location.origin}/auth/callback`}
      camsBridgeUrl={import.meta.env.VITE_CAMS_BRIDGE_URL}
      camsLoginUrl={import.meta.env.VITE_CAMS_LOGIN_URL}
      onLoginSuccess={(user) => console.log("Welcome:", user.email)}
      onSessionExpired={() => console.log("Session expired")}
      onError={(err, ctx) => console.error(`[${ctx}]`, err)}
    >
      <AuthGuard fallback={<LoadingScreen />}>
        <Dashboard />
      </AuthGuard>
    </CamsAuthProvider>
  );
}

ReactDOM.createRoot(document.getElementById("root")!).render(<App />);

Created with ❤️ by @UXCOE @CLOUDWICK