@codeswayam/auth
v2.7.2
Published
Plug-and-play auth, subscriptions, and credits for CodeSwayam SaaS products
Maintainers
Readme
@codeswayam/auth
Plug-and-play authentication, SSO, subscriptions, credits, and push notifications for all CodeSwayam apps.
@codeswayam/auth is the root authentication and identity package for every CodeSwayam application. It provides a complete auth stack: SSO login/logout, JWT management, role-based access control, credit wallet management, subscription guards, and Web Push notifications — all as composable React hooks and components that plug into any Next.js app.
Table of Contents
- Overview
- Installation
- Peer Dependencies
- Quick Start
- API Reference
- Re-exports
- TypeScript Interfaces
- Code Examples
- Integration Guide (Next.js)
- Changelog
Overview
@codeswayam/auth covers the full identity and monetisation surface of a CodeSwayam app:
| Domain | What it provides |
|---|---|
| Authentication | SSO redirect, JWT storage, auto token refresh, callback handling |
| User identity | useCSWUser() for current user data |
| Roles | useCSWRole() with isAdmin, isModerator, hasRole() |
| Subscriptions | useCSWSubscriptions(), SubscriptionGuard |
| Credits | useCSWCredits(), CreditGuard, CreditBadge, BuyCreditsModal |
| Push notifications | useCSWNotifications(), NotificationBell |
| Middleware | withCSWAuth() — Next.js Edge middleware factory |
| Entitlements | Re-exports all of @codeswayam/access |
Installation
npm install @codeswayam/auth
# or
yarn add @codeswayam/auth
# or
pnpm add @codeswayam/authPeer Dependencies
| Package | Version |
|---|---|
| react | ^18.0.0 |
| react-dom | ^18.0.0 |
| next | ^13.0.0 \|\| ^14.0.0 \|\| ^15.0.0 |
| swr | ^2.0.0 |
| @codeswayam/api-client | ^1.3.0 |
| @codeswayam/access | ^1.0.0 |
npm install react react-dom next swr @codeswayam/api-client @codeswayam/accessQuick Start
1. Add middleware
// middleware.ts (project root)
import { withCSWAuth } from '@codeswayam/auth';
export default withCSWAuth({
ssoUrl: process.env.NEXT_PUBLIC_SSO_URL!,
callbackPath: '/auth/callback',
publicPaths: ['/', '/pricing', '/about'],
});
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};2. Add CSWProvider to your root layout
// app/layout.tsx
import { CSWProvider } from '@codeswayam/auth';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<CSWProvider apiUrl={process.env.NEXT_PUBLIC_API_URL!}>
{children}
</CSWProvider>
</body>
</html>
);
}3. Guard your pages
// app/dashboard/layout.tsx
import { AuthGuard } from '@codeswayam/auth';
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
return <AuthGuard>{children}</AuthGuard>;
}API Reference
withCSWAuth — Next.js Middleware
Factory function that returns a Next.js Edge middleware. It verifies the user's JWT on every request and redirects unauthenticated users to the SSO login page.
function withCSWAuth(config: CSWAuthMiddlewareConfig): NextMiddlewareConfig options:
| Option | Type | Required | Description |
|---|---|---|---|
| ssoUrl | string | ✅ | Base URL of the CodeSwayam auth service |
| callbackPath | string | ✅ | Path in your app that handles the SSO callback (e.g. /auth/callback) |
| publicPaths | string[] | ✅ | Array of paths that are accessible without authentication |
// middleware.ts
import { withCSWAuth } from '@codeswayam/auth';
export default withCSWAuth({
ssoUrl: 'https://auth.codeswayam.com',
callbackPath: '/auth/callback',
publicPaths: ['/', '/pricing', '/blog', '/about'],
});
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico|api/public).*)'],
};CSWProvider — Root Provider
The root React context provider for the entire @codeswayam/auth stack. Must be placed above all auth hooks and guard components.
import { CSWProvider } from '@codeswayam/auth';
<CSWProvider apiUrl="https://api.codeswayam.com">
{children}
</CSWProvider>Props:
| Prop | Type | Required | Description |
|---|---|---|---|
| apiUrl | string | ✅ | Base URL for the CodeSwayam API |
| children | React.ReactNode | ✅ | Child components |
CSWProvider internally mounts AccessProvider (from @codeswayam/access), so you only need one provider at the root.
useCSWUser
Returns the currently authenticated user's data.
function useCSWUser(): {
user: CSWUser | null;
isLoaded: boolean;
isSignedIn: boolean;
}Return values:
| Property | Type | Description |
|---|---|---|
| user | CSWUser \| null | User object when signed in, null otherwise |
| isLoaded | boolean | true once the initial user fetch completes |
| isSignedIn | boolean | true when a valid session exists |
CSWUser shape:
interface CSWUser {
id: string;
email: string;
name: string;
avatarUrl?: string;
role: string;
createdAt: string;
}useCSWRole
Provides role-based permission helpers for the current user.
function useCSWRole(): {
role: string | null;
isAdmin: boolean;
isModerator: boolean;
hasRole: (role: string) => boolean;
}Return values:
| Property | Type | Description |
|---|---|---|
| role | string \| null | Raw role string for the current user |
| isAdmin | boolean | Shorthand for hasRole('admin') |
| isModerator | boolean | Shorthand for hasRole('moderator') |
| hasRole(r) | (role: string) => boolean | Generic role check |
useCSWCredits
Provides access to the user's credit wallet, feature costs, and credit operations.
function useCSWCredits(): {
balance: number;
featureCosts: Record<string, number>;
useCredits: (feature: string) => Promise<void>;
purchase: (packId: string) => Promise<void>;
}Return values:
| Property | Type | Description |
|---|---|---|
| balance | number | Current credit balance |
| featureCosts | Record<string, number> | Per-feature credit costs (e.g. { ai_chat: 5 }) |
| useCredits(feature) | (feature: string) => Promise<void> | Deduct credits for a feature use |
| purchase(packId) | (packId: string) => Promise<void> | Purchase a credit pack by ID |
useCSWSubscriptions
Provides the user's active subscriptions and helpers for checking plan status.
function useCSWSubscriptions(): {
subscriptions: UserSubscription[];
hasActivePlan: boolean;
getActivePlan: (appId: string) => UserSubscription | null;
}Return values:
| Property | Type | Description |
|---|---|---|
| subscriptions | UserSubscription[] | All subscriptions for the user |
| hasActivePlan | boolean | true if any subscription is currently active |
| getActivePlan(appId) | (appId: string) => UserSubscription \| null | Returns the active plan for a specific app |
AuthGuard
Redirects unauthenticated users to the SSO login page. Renders children only when the user is signed in.
import { AuthGuard } from '@codeswayam/auth';
<AuthGuard>
<ProtectedPage />
</AuthGuard>
// With a custom loading state
<AuthGuard fallback={<FullPageSpinner />}>
<ProtectedPage />
</AuthGuard>Props:
| Prop | Type | Description |
|---|---|---|
| children | React.ReactNode | Content to render when authenticated |
| fallback | React.ReactNode | Content to render while loading (defaults to null) |
SubscriptionGuard
Renders children only when the user has an active subscription meeting the minimum plan requirement for a given app.
import { SubscriptionGuard } from '@codeswayam/auth';
<SubscriptionGuard
appId="auraflow"
minPlan="pro"
fallback={<UpgradePage />}
>
<ProFeatureContent />
</SubscriptionGuard>Props:
| Prop | Type | Required | Description |
|---|---|---|---|
| appId | string | ✅ | The CodeSwayam app identifier |
| minPlan | string | ✅ | Minimum plan type required |
| fallback | React.ReactNode | — | Content rendered when guard fails |
| children | React.ReactNode | ✅ | Protected content |
CreditGuard
Renders children only when the user has sufficient credits for a given feature. Shows fallback (e.g. purchase prompt) otherwise.
import { CreditGuard } from '@codeswayam/auth';
<CreditGuard
feature="smart_ai"
cost={10}
fallback={<BuyCreditsPrompt />}
>
<AiFeatureComponent />
</CreditGuard>Props:
| Prop | Type | Required | Description |
|---|---|---|---|
| feature | string | ✅ | Feature key to check credits for |
| cost | number | ✅ | Credit cost required to use this feature |
| fallback | React.ReactNode | — | Rendered when insufficient credits |
| children | React.ReactNode | ✅ | Protected content |
CreditBadge
Renders a pill showing the user's current credit balance. Intended for placement in navigation or header areas.
import { CreditBadge } from '@codeswayam/auth';
// In your navbar:
<nav>
<CreditBadge />
<UserMenu />
</nav>Props: None. Reads balance from useCSWCredits() internally.
BuyCreditsModal
A complete modal for browsing and purchasing credit packs. Uses Razorpay under the hood for payment processing.
import { BuyCreditsModal } from '@codeswayam/auth';
import { useState } from 'react';
export function Header() {
const [open, setOpen] = useState(false);
return (
<>
<button onClick={() => setOpen(true)}>Buy Credits</button>
<BuyCreditsModal open={open} onClose={() => setOpen(false)} />
</>
);
}Props:
| Prop | Type | Required | Description |
|---|---|---|---|
| open | boolean | ✅ | Controls modal visibility |
| onClose | () => void | ✅ | Called when the modal is dismissed |
SSO Utilities
Low-level SSO functions exported from sso.ts. Typically you won't use these directly — withCSWAuth and AuthGuard handle the auth flow. Use these for custom auth flows.
import {
redirectToSSO,
useSSOCallback,
getStoredToken,
saveToken,
clearToken,
isTokenValid,
decodeJwtPayload,
CSW_TOKEN_KEY,
} from '@codeswayam/auth';| Export | Signature | Description |
|---|---|---|
| redirectToSSO | (authUrl: string, currentUrl: string) => void | Redirects to the SSO login page with a return URL |
| useSSOCallback | () => { isProcessing, error } | Hook for the SSO callback page — exchanges the ticket for a JWT |
| getStoredToken | () => string \| null | Reads the JWT from localStorage |
| saveToken | (token: string) => void | Persists a JWT to localStorage |
| clearToken | () => void | Removes the JWT from localStorage |
| isTokenValid | (token: string) => boolean | Checks if a JWT is non-expired |
| decodeJwtPayload | <T>(token: string) => T | Decodes the JWT payload without verification |
| CSW_TOKEN_KEY | string | The localStorage key used to store the token |
Notifications
Web Push notification management with a ready-made bell UI component.
import { useCSWNotifications, NotificationBell } from '@codeswayam/auth';useCSWNotifications
function useCSWNotifications(): {
isSupported: boolean;
permission: NotificationPermission;
isSubscribed: boolean;
subscribe: () => Promise<void>;
unsubscribe: () => Promise<void>;
}| Property | Type | Description |
|---|---|---|
| isSupported | boolean | Whether the browser supports Web Push |
| permission | NotificationPermission | 'default', 'granted', or 'denied' |
| isSubscribed | boolean | Whether the user has an active push subscription |
| subscribe() | () => Promise<void> | Request permission and register push subscription |
| unsubscribe() | () => Promise<void> | Unregister push subscription |
NotificationBell
import { NotificationBell } from '@codeswayam/auth';
// Drop into your navigation bar
<NotificationBell />Props: None. Handles permission requests and subscription state internally.
Re-exports
From @codeswayam/access
Everything from the access package is re-exported for convenience — you don't need to install it separately when using @codeswayam/auth.
import {
AccessProvider,
FeatureGate,
TierBadge,
UsageMeter,
useAppAccess,
useFeatureGate,
useUsageTracker,
} from '@codeswayam/auth';From @codeswayam/api-client
All API client functions for subscriptions, credits, referrals, and notifications are re-exported:
import {
// Subscriptions
fetchUserSubscriptions,
fetchSubscriptionById,
fetchPublicPlans,
changeSubscriptionPlan,
cancelUserSubscription,
// Credits
fetchMyWallet,
useCredits,
fetchCreditPacks,
fetchFeatureCosts,
createCreditPurchaseOrder,
verifyCreditPurchase,
// Referrals
fetchReferralStats,
redeemReferralCode,
redeemCouponCode,
// Notifications
registerPushSubscription,
unregisterPushSubscription,
fetchVapidPublicKey,
} from '@codeswayam/auth';TypeScript Interfaces
interface CSWUser {
id: string;
email: string;
name: string;
avatarUrl?: string;
role: string;
createdAt: string;
}
interface CSWAuthMiddlewareConfig {
ssoUrl: string;
callbackPath: string;
publicPaths: string[];
}
interface CSWProviderProps {
apiUrl: string;
children: React.ReactNode;
}
interface AuthGuardProps {
children: React.ReactNode;
fallback?: React.ReactNode;
}
interface SubscriptionGuardProps {
appId: string;
minPlan: string;
fallback?: React.ReactNode;
children: React.ReactNode;
}
interface CreditGuardProps {
feature: string;
cost: number;
fallback?: React.ReactNode;
children: React.ReactNode;
}
interface BuyCreditsModalProps {
open: boolean;
onClose: () => void;
}Code Examples
1. Next.js App Setup with CSWProvider
// app/layout.tsx
import { CSWProvider } from '@codeswayam/auth';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{/*
CSWProvider initializes the entire auth + access stack.
It internally includes AccessProvider — no need to add it separately.
*/}
<CSWProvider apiUrl={process.env.NEXT_PUBLIC_API_URL!}>
{children}
</CSWProvider>
</body>
</html>
);
}2. withCSWAuth Middleware
// middleware.ts (place at the project root, next to package.json)
import { withCSWAuth } from '@codeswayam/auth';
export default withCSWAuth({
ssoUrl: process.env.NEXT_PUBLIC_SSO_URL!,
callbackPath: '/auth/callback',
publicPaths: [
'/',
'/pricing',
'/blog',
'/about',
'/auth/callback', // must always be public
],
});
// Run on all routes except static assets
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};3. AuthGuard Usage
// app/dashboard/layout.tsx
import { AuthGuard } from '@codeswayam/auth';
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
return (
// Unauthenticated users are automatically redirected to SSO
<AuthGuard fallback={<div>Loading...</div>}>
{children}
</AuthGuard>
);
}4. useCSWUser Hook
'use client';
import { useCSWUser } from '@codeswayam/auth';
export function UserGreeting() {
const { user, isLoaded, isSignedIn } = useCSWUser();
if (!isLoaded) return <span>Loading...</span>;
if (!isSignedIn) return <a href="/login">Sign in</a>;
return (
<div className="flex items-center gap-3">
{user?.avatarUrl && (
<img src={user.avatarUrl} alt={user.name} className="w-8 h-8 rounded-full" />
)}
<span>Hello, {user?.name}</span>
</div>
);
}5. useCSWRole — Admin Check
'use client';
import { useCSWRole } from '@codeswayam/auth';
export function AdminPanel() {
const { isAdmin, isModerator, hasRole } = useCSWRole();
if (!isAdmin) {
return <p>Access denied. Admins only.</p>;
}
return (
<div>
<h1>Admin Panel</h1>
{isModerator && <ModerationTools />}
{hasRole('billing_admin') && <BillingSettings />}
</div>
);
}6. useCSWCredits — Deducting Credits
'use client';
import { useCSWCredits } from '@codeswayam/auth';
import { useState } from 'react';
export function AiGenerateButton() {
const { balance, featureCosts, useCredits } = useCSWCredits();
const [loading, setLoading] = useState(false);
const cost = featureCosts['ai_generate'] ?? 10;
const canAfford = balance >= cost;
async function handleGenerate() {
if (!canAfford) return;
setLoading(true);
try {
// Deduct credits first
await useCredits('ai_generate');
// Then run the AI feature
await runAiGeneration();
} finally {
setLoading(false);
}
}
return (
<button
onClick={handleGenerate}
disabled={!canAfford || loading}
className="btn-primary"
>
{loading ? 'Generating...' : `Generate (${cost} credits)`}
</button>
);
}7. BuyCreditsModal
'use client';
import { BuyCreditsModal, CreditBadge } from '@codeswayam/auth';
import { useState } from 'react';
export function Navbar() {
const [buyOpen, setBuyOpen] = useState(false);
return (
<header className="navbar">
<Logo />
<nav>
<CreditBadge />
<button
onClick={() => setBuyOpen(true)}
className="btn-outline text-sm"
>
+ Buy Credits
</button>
</nav>
<BuyCreditsModal
open={buyOpen}
onClose={() => setBuyOpen(false)}
/>
</header>
);
}8. useSSOCallback on the Callback Page
// app/auth/callback/page.tsx
'use client';
import { useSSOCallback } from '@codeswayam/auth';
import { useRouter } from 'next/navigation';
import { useEffect } from 'react';
export default function AuthCallbackPage() {
const router = useRouter();
const { isProcessing, error } = useSSOCallback();
useEffect(() => {
if (!isProcessing && !error) {
// Token saved — redirect to the app
router.replace('/dashboard');
}
}, [isProcessing, error, router]);
if (error) {
return (
<div className="flex flex-col items-center justify-center min-h-screen">
<p className="text-red-500">Authentication failed: {error}</p>
<a href="/">Return home</a>
</div>
);
}
return (
<div className="flex items-center justify-center min-h-screen">
<p>Signing you in...</p>
</div>
);
}9. NotificationBell
'use client';
import { NotificationBell } from '@codeswayam/auth';
// Drop this anywhere in your navigation
export function AppHeader() {
return (
<header>
<Logo />
<div className="flex items-center gap-4">
<NotificationBell />
<UserMenu />
</div>
</header>
);
}Using the underlying hook for custom UIs:
'use client';
import { useCSWNotifications } from '@codeswayam/auth';
export function NotificationSettings() {
const { isSupported, permission, isSubscribed, subscribe, unsubscribe } =
useCSWNotifications();
if (!isSupported) {
return <p>Your browser does not support push notifications.</p>;
}
return (
<div>
<p>Status: {permission}</p>
{isSubscribed ? (
<button onClick={unsubscribe}>Disable Notifications</button>
) : (
<button onClick={subscribe}>Enable Notifications</button>
)}
</div>
);
}Integration Guide (Next.js)
Recommended File Structure
src/
├── app/
│ ├── layout.tsx ← CSWProvider here
│ ├── middleware.ts ← withCSWAuth here (project root)
│ ├── auth/
│ │ └── callback/
│ │ └── page.tsx ← useSSOCallback here
│ ├── dashboard/
│ │ ├── layout.tsx ← AuthGuard here
│ │ └── page.tsx
│ └── admin/
│ ├── layout.tsx ← AuthGuard + role check here
│ └── page.tsx
└── components/
└── Navbar.tsx ← CreditBadge + NotificationBellEnvironment Variables
# .env.local
NEXT_PUBLIC_API_URL=https://api.codeswayam.com
NEXT_PUBLIC_SSO_URL=https://auth.codeswayam.comComplete App Router Setup
middleware.ts (project root)
import { withCSWAuth } from '@codeswayam/auth';
export default withCSWAuth({
ssoUrl: process.env.NEXT_PUBLIC_SSO_URL!,
callbackPath: '/auth/callback',
publicPaths: ['/', '/pricing', '/about', '/auth/callback'],
});
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};app/layout.tsx
import { CSWProvider } from '@codeswayam/auth';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<CSWProvider apiUrl={process.env.NEXT_PUBLIC_API_URL!}>
{children}
</CSWProvider>
</body>
</html>
);
}app/dashboard/layout.tsx
import { AuthGuard, SubscriptionGuard } from '@codeswayam/auth';
export default function DashboardLayout({ children }: { children: React.ReactNode }) {
return (
<AuthGuard>
<SubscriptionGuard appId="auraflow" minPlan="free" fallback={<NoSubscriptionPage />}>
{children}
</SubscriptionGuard>
</AuthGuard>
);
}app/auth/callback/page.tsx
'use client';
import { useSSOCallback } from '@codeswayam/auth';
import { useRouter } from 'next/navigation';
import { useEffect } from 'react';
export default function CallbackPage() {
const router = useRouter();
const { isProcessing, error } = useSSOCallback();
useEffect(() => {
if (!isProcessing && !error) router.replace('/dashboard');
}, [isProcessing, error, router]);
if (error) return <div>Auth error: {error}</div>;
return <div>Signing in...</div>;
}Source Structure
packages/auth-client/src/
├── index.ts — Main barrel exports
├── sso.ts — SSO utilities and JWT helpers
├── middleware.ts — withCSWAuth() Next.js middleware factory
├── CSWProvider.tsx — Root React provider
├── users.ts — User utility functions
├── hooks/
│ ├── useCSWUser.ts — Current user data hook
│ ├── useCSWRole.ts — Role checking hook
│ ├── useCSWCredits.ts — Credit wallet hook
│ ├── useCSWSubscriptions.ts — Subscriptions hook
│ └── useCSWTokenRefresh.ts — Auto token refresh hook
├── components/
│ ├── AuthGuard.tsx — Authentication guard component
│ ├── SubscriptionGuard.tsx — Subscription-based guard component
│ ├── CreditGuard.tsx — Credit-based guard component
│ ├── CreditBadge.tsx — Credit balance display
│ └── BuyCreditsModal.tsx — Credit purchase modal (Razorpay)
└── notifications/
├── useCSWNotifications.ts — Web Push management hook
├── NotificationBell.tsx — Bell UI component
└── index.ts — Notifications barrel exportsChangelog
v2.7.2
- SSO Performance: Dynamic origin detection and fast-path redirect eliminating domain verification flash latency.
- Cross-App Grants: Unified cross-app grant resolution exported via
@codeswayam/access. - Dependencies: Bumped
@codeswayam/api-clientto^1.3.4and@codeswayam/accessto^1.0.3.
v2.7.1
- Domain-wide cookie synchronisation for seamless cross-subdomain sessions.
v2.7.0
- Bugfix: Fixed
data.find is not a functioninuseCSWFeatureCostwhen backend returns dictionary objects (Record<string, number>), arrays, or nested data wrappers. - SSO Context Retention: Added
appNameoption towithCSWAuthandredirectToSSOto preserve calling app identity and return trail during central login. - URL Normalization: Automatic
/ssopath resolution inresolveSsoUrlso passing root base URLs (e.g.https://auth.codeswayam.com) seamlessly hits the SSO ticket endpoint.
v2.6.0
- Multi-currency support (INR/USD) in subscription helpers
- Added domain-wide cookie retention for cross-app currency preferences
- Fixed token refresh race condition on concurrent requests
- Improved
BuyCreditsModalerror state handling
v2.5.0
- Added
NotificationBellcomponent - Added
useCSWNotificationshook with full Web Push support CSWProvidernow internally mountsAccessProvider
v2.4.0
- Added
CreditGuardcomponent - Added
SubscriptionGuardcomponent useCSWCreditsnow exposesfeatureCosts
v2.3.0
- Added re-exports from
@codeswayam/access - Added re-exports from
@codeswayam/api-client
v2.0.0
- Replaced cookie-only auth with JWT + cookie hybrid approach
withCSWAuthrewritten as an Edge-compatible middleware factory- Added
useCSWTokenRefreshfor background token renewal
Part of the CodeSwayam platform. For support, open an issue in the monorepo.
