signifyid-client is a production-ready React SDK that provides seamless integration with Signify iD's authentication platform. It supports both CommonJS and ES Module builds with full TypeScript support, making it compatible with modern React applications, Next.js, and beyond.
- Single Sign-On (SSO) - Unified authentication across multiple applications
- Session Management - Secure, token-based authentication with automatic refresh
- Protected Routes - Built-in component for authentication-gated routes
- Multi-Factor Authentication - Support for enhanced security options
- TypeScript First - Complete type definitions included
- Framework Agnostic - Works with React, Next.js (App & Pages Router), and standard React SPAs
- SSR Safe - Proper handling of server-side rendering concerns
- Zero External Dependencies - Minimal bundle size impact
Install the package using your preferred package manager:
npm install signifyid-clientyarn add signifyid-clientpnpm add signifyid-client- React 17.0.0 or higher
- React DOM 17.0.0 or higher
Add the required configuration to your environment file:
# .env.local (Next.js) or .env
NEXT_PUBLIC_SIGNIFY_API_URL=https://signifyid-api.vercel.app
NEXT_PUBLIC_SIGNIFY_LOGIN_URL=https://signifyid.vercel.app/client/loginWrap your application with SignifyProvider:
// app/layout.tsx (Next.js App Router)
import { SignifyProvider } from "signifyid-client";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html>
<body>
<SignifyProvider
config={{
apiUrl: process.env.NEXT_PUBLIC_SIGNIFY_API_URL!,
loginUrl: process.env.NEXT_PUBLIC_SIGNIFY_LOGIN_URL!,
env: "production", // 'production' or 'development'
}}
>
{children}
</SignifyProvider>
</body>
</html>
);
}Use ProtectedRoute to guard components requiring authentication:
// app/dashboard/page.tsx
import { ProtectedRoute } from "signifyid-client";
import Dashboard from "./Dashboard";
export default function DashboardPage() {
return (
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
);
}Use the useSignifyAuth hook to interact with authentication:
"use client";
import { useSignifyAuth } from "signifyid-client";
export function Navbar() {
const { isAuthenticated, isLoading, user, login, logout } = useSignifyAuth();
if (isLoading) {
return <nav>Loading authentication state...</nav>;
}
return (
<nav>
{isAuthenticated ? (
<>
<span>Welcome, {user?.name}</span>
<button onClick={logout}>Sign Out</button>
</>
) : (
<button onClick={login}>Sign In with Signify iD</button>
)}
</nav>
);
}Root provider component that establishes the authentication context throughout your application.
Props:
config(required) - Configuration object with:apiUrl(string) - Backend API endpoint URLloginUrl(string) - Signify iD login page URLcookieName?(string) - Session cookie name (default:"clientSession")cookieMaxAge?(number) - Cookie expiration in seconds (default:86400)tokenParam?(string) - URL parameter for token extraction (default:"token")debug?(boolean) - Enable debug logging (default:false)env?(string) - Environment setting:"production"or"development". When set to"development", API calls and login redirects are disabled for security and cost-prevention (default:"development")
onAuthStateChange?- Callback function invoked on authentication state changes
Example:
<SignifyProvider
config={{
apiUrl: "https://signifyid-api.vercel.app",
loginUrl: "https://signifyid.vercel.app/client/login",
env: "production",
debug: process.env.NODE_ENV === "development",
}}
onAuthStateChange={(state) => {
if (state.isAuthenticated) {
// Handle authenticated state
}
}}
>
{children}
</SignifyProvider>Hook to access authentication state and methods within a SignifyProvider context.
Returns:
| Property | Type | Description |
|---|---|---|
isAuthenticated |
boolean |
User authentication status |
isLoading |
boolean |
Session validation in progress |
session |
SignifySession | null |
Complete session object |
user |
SignifyUser | null |
Authenticated user data |
login() |
() => void |
Redirect to Signify iD login page |
logout() |
() => Promise<void> |
Clear session and sign out user |
validateSession() |
() => Promise<void> |
Manually validate session |
Example:
"use client";
import { useSignifyAuth } from "signifyid-client";
export function Profile() {
const { user, isLoading, logout } = useSignifyAuth();
if (isLoading) return <div>Loading...</div>;
return (
<div>
<h1>{user?.name}</h1>
<button onClick={logout}>Sign Out</button>
</div>
);
}Component that conditionally renders children only when user is authenticated.
Props:
children(required) - Component to render when authenticatedloadingComponent?- Loading UI displayed during session validationredirectUrl?- Custom redirect URL (default: login URL)onRedirect?- Callback before redirect to login
Example:
<ProtectedRoute loadingComponent={<LoadingSpinner />} redirectUrl="/auth/login">
<Dashboard />
</ProtectedRoute>Hook to access resolved configuration values.
Returns: Configuration object containing all provider settings
const config = useSignifyConfig();
console.log(config.apiUrl);Exported utility functions for advanced use cases:
import {
setCookie, // Set HTTP cookie
getCookie, // Retrieve cookie value
deleteCookie, // Remove cookie
getTokenFromUrl, // Extract token from URL parameters
cleanUrlParams, // Remove parameters from URL
isBrowser, // Check if running in browser environment
} from "signifyid-client";The SDK follows a redirect-based OAuth-style authentication pattern:
┌──────────────────┐ ┌─────────────────┐ ┌────────────────┐
│ Your App │ │ Signify iD │ │ Backend API │
│ │ │ Login Portal │ │ │
└────────┬─────────┘ └────────┬────────┘ └────────┬───────┘
│ │ │
│ 1. User accesses │ │
│ protected route │ │
│ │ │
│ 2. Redirect ───────────>│ │
│ ?redirect=app.com │ │
│ │ │
│ 3. User signs in │
│ │ │
│ 4. Redirect <───────────│ │
│ ?token=JWT_TOKEN │ │
│ │ │
│ 5. Extract & store token in secure cookie │
│ │ │
│ 6. Validate session ───────────────────────────────>│
│ │ │
│ 7. Session data <──────────────────────────────────│
│ │ │
│ 8. User authenticated ✓ │ │
Root Layout:
// app/layout.tsx
import { SignifyProvider } from "signifyid-client";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<SignifyProvider
config={{
apiUrl: process.env.NEXT_PUBLIC_SIGNIFY_API_URL!,
loginUrl: process.env.NEXT_PUBLIC_SIGNIFY_LOGIN_URL!,
env: "production",
debug: process.env.NODE_ENV === "development",
}}
>
{children}
</SignifyProvider>
</body>
</html>
);
}Protected Layout:
// app/dashboard/layout.tsx
import { ProtectedRoute } from "signifyid-client";
export default function DashboardLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<ProtectedRoute
loadingComponent={
<div className="flex items-center justify-center min-h-screen">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500" />
</div>
}
>
{children}
</ProtectedRoute>
);
}Application Entry:
// pages/_app.tsx
import type { AppProps } from "next/app";
import { SignifyProvider } from "signifyid-client";
export default function App({ Component, pageProps }: AppProps) {
return (
<SignifyProvider
config={{
apiUrl: process.env.NEXT_PUBLIC_SIGNIFY_API_URL!,
loginUrl: process.env.NEXT_PUBLIC_SIGNIFY_LOGIN_URL!,
}}
>
<Component {...pageProps} />
</SignifyProvider>
);
}Protected Page:
// pages/dashboard.tsx
import { ProtectedRoute, useSignifyAuth } from "signifyid-client";
function DashboardContent() {
const { user, logout } = useSignifyAuth();
return (
<div>
<h1>Welcome, {user?.name}</h1>
<p>Email: {user?.email}</p>
<button onClick={logout}>Sign Out</button>
</div>
);
}
export default function DashboardPage() {
return (
<ProtectedRoute>
<DashboardContent />
</ProtectedRoute>
);
}// src/App.tsx
import {
SignifyProvider,
ProtectedRoute,
useSignifyAuth,
} from "signifyid-client";
function Dashboard() {
const { user, logout } = useSignifyAuth();
return (
<div>
<h1>Welcome, {user?.name}</h1>
<button onClick={logout}>Sign Out</button>
</div>
);
}
export function App() {
return (
<SignifyProvider
config={{
apiUrl: process.env.REACT_APP_SIGNIFY_API_URL!,
loginUrl: process.env.REACT_APP_SIGNIFY_LOGIN_URL!,
env: "production",
}}
>
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
</SignifyProvider>
);
}Full TypeScript support is included out of the box. The package exports comprehensive type definitions for all components and hooks.
Exported Types:
import type {
SignifyConfig,
SignifySession,
SignifyUser,
SignifyAuthState,
SignifyProviderProps,
ProtectedRouteProps,
} from "signifyid-client";Type Safety Example:
interface ExtendedUser extends SignifyUser {
organizationId: string;
role: "admin" | "user" | "viewer";
}
export function AdminPanel() {
const { user } = useSignifyAuth();
const adminUser = user as ExtendedUser | null;
if (adminUser?.role !== "admin") {
return <div>Access Denied</div>;
}
return <div>Admin Content</div>;
}Listen to authentication state changes at the provider level:
<SignifyProvider
config={config}
onAuthStateChange={(state) => {
if (state.isAuthenticated && state.user) {
// Track user authentication
analytics.identify(state.user.id);
} else {
// Clear user-specific data
analytics.reset();
}
}}
>
{children}
</SignifyProvider><ProtectedRoute
redirectUrl="/auth/login"
onRedirect={() => {
// Custom logic before redirect
console.log("User will be redirected to login");
}}
>
<Dashboard />
</ProtectedRoute>Re-validate session on demand:
import { useSignifyAuth } from "signifyid-client";
export function AuthStatus() {
const { validateSession, isAuthenticated } = useSignifyAuth();
const handleRefresh = async () => {
await validateSession();
};
return (
<div>
<p>Status: {isAuthenticated ? "Authenticated" : "Not authenticated"}</p>
<button onClick={handleRefresh}>Refresh Session</button>
</div>
);
}<SignifyProvider
config={{
apiUrl: "https://signifyid-api.vercel.app",
loginUrl: "https://signifyid.vercel.app/client/login",
cookieName: "my_session_token",
cookieMaxAge: 604800, // 7 days in seconds
tokenParam: "auth_token", // Custom URL parameter name
debug: true, // Enable verbose logging
}}
>
{children}
</SignifyProvider>The SDK implements security best practices throughout:
- Server-Side Rendering Safe - All browser APIs are protected with
isBrowser()checks - Cross-Domain Credentials - Uses
credentials: 'include'for proper cookie handling - Automatic Token Cleanup - Token parameters are removed from URL after extraction
- CSRF Protection - Cookies are set with
SameSite=Lax - No Client-Side Token Storage - Authentication tokens are stored exclusively in secure HTTP-only cookies
- Session Validation - Regular server-side session validation prevents token tampering
Ensure your component is a child of <SignifyProvider>:
// ❌ Incorrect - Component outside provider
<SignifyProvider>...</SignifyProvider>
<MyComponent />
// ✅ Correct - Component inside provider
<SignifyProvider>
<MyComponent />
</SignifyProvider>Verify the following:
apiUrlandloginUrlare correctly configured- CORS is enabled on your backend server
- Cookies are being set (check DevTools → Application → Cookies)
- Session validation endpoint is accessible at
POST /api/client-auth/session/validate
This typically indicates session validation is failing. Check:
- Backend service is running and accessible
- Session validation endpoint is properly configured
- Request credentials are being sent and processed correctly
- Server is returning valid session data in response
Enable debug logging for troubleshooting:
<SignifyProvider
config={{
apiUrl: process.env.NEXT_PUBLIC_SIGNIFY_API_URL!,
loginUrl: process.env.NEXT_PUBLIC_SIGNIFY_LOGIN_URL!,
debug: true,
}}
>
{children}
</SignifyProvider>We welcome contributions to improve the SDK. Please review our contributing guidelines before submitting pull requests.
For bug reports or feature requests, please open an issue on our GitHub repository.
This package is built with tsup and supports multiple output formats:
- CommonJS:
dist/index.cjs(for Node.js and bundlers) - ES Modules:
dist/index.js(for modern browsers and build tools) - TypeScript Definitions:
dist/index.d.tsanddist/index.d.cts
Build the package:
npm run buildWatch mode for development:
npm run devType checking:
npm run typecheckMIT License © Signify iD
See the LICENSE file for details.
For questions, issues, or support:
- 📧 Email: sanjaychili838@gmail.com
- 🌐 Website: https://signifyid.vercel.app
- 📚 Documentation: https://docs.signifyid.com