@benbraide/auth-service-nextjs
v1.1.1
Published
Next.js/React component for integrating iframe-based account switcher with authentication service
Maintainers
Readme
@benbraide/auth-service-nextjs
Next.js/React component library for integrating iframe-based account switcher with authentication service.
Features
✅ Full TypeScript Support - Complete type definitions for all components and hooks ✅ SSR Compatible - Works seamlessly with Next.js server-side rendering ✅ Interactive Dialogs - Built-in support for confirm, prompt, and alert dialogs ✅ Auto-Resize - Iframe automatically adjusts height based on content ✅ Tree-Shakeable - Modern ESM and CJS builds with optimized bundle size ✅ Zero Dependencies - Only peer dependencies on React 18+
Installation
npm install @benbraide/auth-service-nextjsQuick Start
1. Wrap your app with AccountSwitcherProvider
// app/layout.tsx
import { AccountSwitcherProvider } from '@benbraide/auth-service-nextjs';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<AccountSwitcherProvider
backendUrl={process.env.NEXT_PUBLIC_API_URL!}
authServiceUrl="https://auth.yourdomain.com"
onSessionChange={(session) => {
console.log('Session changed:', session);
}}
>
{children}
</AccountSwitcherProvider>
</body>
</html>
);
}2. Use the AccountSwitcher component
// components/Header.tsx
'use client';
import { AccountSwitcher, useSession } from '@benbraide/auth-service-nextjs';
export function Header() {
const { currentUser, isAuthenticated } = useSession();
return (
<header className="flex items-center justify-between p-4">
<h1>My App</h1>
{isAuthenticated && (
<div className="flex items-center gap-4">
<span>Welcome, {currentUser?.name}!</span>
<AccountSwitcher />
</div>
)}
</header>
);
}3. Create API route for token generation
The package expects a /api/account-switcher/token endpoint that returns the widget token:
// app/api/account-switcher/token/route.ts
import { NextResponse } from 'next/server';
export async function GET() {
try {
// Call your backend (Laravel/Node/etc.) to get the widget token
const response = await fetch(`${process.env.BACKEND_URL}/api/account-switcher/token`, {
headers: {
'Accept': 'application/json',
},
credentials: 'include',
});
const data = await response.json();
return NextResponse.json(data);
} catch (error) {
return NextResponse.json(
{ error: 'Failed to fetch token' },
{ status: 500 }
);
}
}Components
AccountSwitcherProvider
Context provider that manages session state and iframe communication.
Props:
interface AccountSwitcherConfig {
backendUrl: string; // Required: Your backend API URL
authServiceUrl?: string; // Optional: Auth service URL
onSessionChange?: (session: Session) => void; // Optional: Session change callback
onAccountChange?: (user: User) => void; // Optional: Account change callback
onError?: (error: Error) => void; // Optional: Error callback
debug?: boolean; // Optional: Enable debug logging
// Dialog configuration
dialogs?: {
enabled?: boolean; // Default: true
closeOnOverlayClick?: boolean; // Default: true
closeOnEscape?: boolean; // Default: true
};
// Custom dialog handlers
onConfirmRequest?: (payload: ConfirmPayload) => Promise<boolean>;
onPromptRequest?: (payload: PromptPayload) => Promise<string | null>;
onAlertRequest?: (payload: AlertPayload) => Promise<void>;
// Auto-resize configuration
autoResize?: boolean; // Default: true
minHeight?: number; // Default: 200
maxHeight?: number | null; // Default: null (unlimited)
onResize?: (height: number) => void; // Optional: Resize callback
// Navigation handlers (optional - default: window.location.href)
onNavigateToAddAccount?: (url: string) => void; // Handle add account navigation
onNavigateToManageAccount?: (url: string) => void; // Handle account management navigation
onNavigateToAvatarUpdate?: (url: string) => void; // Handle avatar update navigation
}AccountAvatar
Compact, clickable avatar component for NAV bars that displays the current user's avatar and toggles the account switcher visibility.
Props:
interface AccountAvatarProps {
size?: number; // Avatar diameter in pixels (default: 40)
className?: string; // Additional CSS classes
style?: React.CSSProperties; // Custom inline styles
targetId?: string; // ID of element to toggle (IFRAME container)
onClick?: () => void; // Custom click handler
onAvatarClick?: (user: User | null) => void; // Callback with user data on click
}Features:
- Displays user avatar (image or initials) when logged in
- Shows generic profile icon when logged out
- Automatically syncs with session changes
- Toggles target element visibility (typically IFRAME container)
- Outside click and ESC key support for closing popup
- Smooth hover and active animations
Example:
// In your NAV component
'use client';
import { AccountAvatar } from '@benbraide/auth-service-nextjs';
export function Navbar() {
return (
<nav className="navbar">
<div className="logo">My App</div>
<div className="nav-items">
{/* Avatar that toggles the dropdown */}
<AccountAvatar
size={40}
targetId="account-switcher-dropdown"
onAvatarClick={(user) => console.log('Clicked:', user?.name)}
/>
</div>
</nav>
{/* Hidden dropdown with AccountSwitcher */}
<div
id="account-switcher-dropdown"
style={{
display: 'none',
position: 'absolute',
top: '60px',
right: '20px',
zIndex: 1000,
}}
>
<AccountSwitcher width={320} height={400} />
</div>
);
}AccountSwitcher
Iframe component that displays the account switcher widget.
Props:
interface AccountSwitcherProps {
className?: string; // Optional: CSS class
position?: 'top-left' | 'top-right' | // Optional: Position
'bottom-left' | 'bottom-right';
width?: number; // Optional: Width in pixels
height?: number; // Optional: Height in pixels
}Hooks
useSession()
Returns current session state.
const {
isAuthenticated,
currentUser,
accounts,
isLoading,
error
} = useSession();useUser()
Returns current user object.
const user = useUser();useAccountSwitcher()
Returns account switcher methods.
const {
switchAccount,
addAccount,
manageAccount,
updateAvatar,
logoutAccount,
logoutAll,
refresh
} = useAccountSwitcher();
// Switch to a different account
await switchAccount('user-uuid-here');
// Add new account
await addAccount();
// Navigate to account management page
await manageAccount();
// Navigate to avatar update page
await updateAvatar();
// Logout specific account
await logoutAccount('user-uuid');
// Logout all accounts
await logoutAll();
// Refresh session
await refresh();Advanced Usage
Custom Dialog Handlers
Use your own UI library (e.g., SweetAlert2, Radix UI):
<AccountSwitcherProvider
backendUrl={process.env.NEXT_PUBLIC_API_URL!}
onConfirmRequest={async (payload) => {
const result = await Swal.fire({
title: payload.title,
text: payload.message,
icon: payload.variant === 'danger' ? 'warning' : 'question',
showCancelButton: true,
confirmButtonText: payload.confirmText || 'OK',
});
return result.isConfirmed;
}}
>
{children}
</AccountSwitcherProvider>Event Handlers
<AccountSwitcherProvider
backendUrl={process.env.NEXT_PUBLIC_API_URL!}
onSessionChange={(session) => {
if (session.isAuthenticated) {
// Load user data
fetchUserData(session.currentUser.uuid);
}
}}
onAccountChange={(user) => {
// Reload page or refresh data
window.location.reload();
}}
onError={(error) => {
toast.error(error.message);
}}
>
{children}
</AccountSwitcherProvider>Custom Navigation Handlers
Override default navigation behavior for account flows:
<AccountSwitcherProvider
backendUrl={process.env.NEXT_PUBLIC_API_URL!}
onNavigateToAddAccount={(url) => {
// Use Next.js router instead of window.location
router.push(url);
}}
onNavigateToManageAccount={(url) => {
// Open in new tab
window.open(url, '_blank');
}}
onNavigateToAvatarUpdate={(url) => {
// Custom modal implementation
openAvatarModal(url);
}}
>
{children}
</AccountSwitcherProvider>Debug Mode
Enable verbose logging:
<AccountSwitcherProvider
backendUrl={process.env.NEXT_PUBLIC_API_URL!}
debug={process.env.NODE_ENV === 'development'}
>
{children}
</AccountSwitcherProvider>TypeScript Types
interface User {
uuid: string;
name: string;
email: string;
avatar_url?: string;
created_at?: string;
}
interface Session {
isAuthenticated: boolean;
currentUser: User | null;
accounts: User[];
}Backend Integration
Your backend should implement these endpoints:
GET /api/account-switcher/token
Generate widget embed token.
Response:
{
"token": "eyJ...",
"embed_url": "https://auth.example.com/widgets/embed?token=eyJ...",
"expires_at": "2025-10-02 01:30:00"
}POST /api/account-switcher/sync-session (Optional)
Sync session after account switch.
Request:
{
"auth_token": "eyJ...",
"user_uuid": "user-uuid"
}License
MIT
Author
Ben Braide
