@djangocfg/api
v2.1.231
Published
Auto-generated TypeScript API client with React hooks, SWR integration, and Zod validation for Django REST Framework backends
Downloads
7,071
Maintainers
Readme
@djangocfg/api
Core authentication API client and auth system for DjangoCFG applications.
Part of DjangoCFG — modern Django framework for production-ready SaaS applications.
Install
pnpm add @djangocfg/apiWhat's Inside
This package provides everything needed for authentication and user management:
- API Client - Type-safe client for Django
cfg_accountsAPI - Auth System - Complete authentication module with contexts, hooks, and utilities
- JWT Management - Automatic token refresh and storage
- OAuth Integration - GitHub OAuth with callback handling
- Two-Factor Authentication - TOTP-based 2FA setup and verification
- Server Middleware - Proxy middleware for Next.js
- Shared Storage - Authentication storage used by all extensions
Package Structure
src/
├── generated/
│ ├── cfg_accounts/ # Generated API client (accounts, profiles, OAuth)
│ │ ├── api/ # API class with all endpoints
│ │ ├── schemas/ # Zod validation schemas
│ │ └── types/ # TypeScript types
│ └── cfg_totp/ # Generated TOTP/2FA client
│ ├── api/ # TOTP device, setup, verification endpoints
│ ├── schemas/ # TOTP-specific schemas
│ └── types/ # TOTP types
└── auth/
├── context/ # AuthProvider, AccountsProvider
├── hooks/ # useAuth, useAuthGuard, useGithubAuth, useTwoFactor, etc.
├── middlewares/ # Next.js proxy middleware
└── utils/ # Validation, errors, logger, analyticsEntry Points
| Entry | Import | Description |
|-------|--------|-------------|
| Main | @djangocfg/api | Server-safe exports (API client, fetchers, schemas, types) |
| Hooks | @djangocfg/api/hooks | Client-only SWR hooks for data fetching |
| Auth | @djangocfg/api/auth | Client-only auth system (providers, hooks) |
| Auth Server | @djangocfg/api/auth/server | Server-safe auth utilities (middleware) |
API Client
Pre-configured API instance with automatic JWT token management:
import { api } from '@djangocfg/api';
// Authentication
await api.accounts.login({ email, password });
await api.accounts.logout();
await api.accounts.register({ email, password, username });
// Profile
const profile = await api.accounts.profileRetrieve();
await api.accounts.profileUpdate({ first_name: 'John' });
// Password
await api.accounts.passwordChange({ old_password, new_password });
await api.accounts.passwordReset({ email });
await api.accounts.passwordResetConfirm({ token, password });
// OAuth
await api.accounts.oauthCallback({ provider: 'github', code });Shared Authentication Storage
All DjangoCFG extensions use shared authentication storage from this package:
// Extensions automatically access the same auth tokens
import { apiSupport } from '@djangocfg/ext-support';
import { apiPayments } from '@djangocfg/ext-payments';
// All API clients share the same authentication state
// Login once, authenticated everywhereThis is handled automatically by createExtensionAPI() from @djangocfg/ext-base.
Auth Module
Complete authentication system with OTP, OAuth, and Two-Factor Authentication (2FA) support.
Authentication Flow
1. User enters email → OTP sent
2. User enters OTP code → Verify
├── If 2FA enabled → Show TOTP verification
│ └── User enters 6-digit TOTP → Verify
└── If no 2FA → Continue
3. Success screen with logo → Auto-redirect to dashboardAll authentication methods (OTP, OAuth, 2FA) show a success screen with your logo before redirecting.
Setup
// app/layout.tsx
import { AuthProvider } from '@djangocfg/api/auth';
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<AuthProvider
config={{
routes: {
auth: '/auth',
defaultCallback: '/dashboard'
}
}}
>
{children}
</AuthProvider>
</body>
</html>
);
}Available Hooks
import {
useAuth, // Main auth context (user, isAuthenticated, OTP methods)
useAuthGuard, // Protect routes (redirect if not authenticated)
useAuthForm, // Auth form state management (OTP + 2FA steps)
useGithubAuth, // GitHub OAuth integration
useTwoFactor, // 2FA verification (verify TOTP code)
useTwoFactorSetup, // 2FA setup flow (generate QR, verify, enable)
useTwoFactorStatus, // 2FA status check and disable
useAutoAuth, // Auto-authentication on mount
useLocalStorage, // localStorage helper
useSessionStorage, // sessionStorage helper
} from '@djangocfg/api/auth';useAuth
Main authentication hook with OTP support:
'use client';
import { useAuth } from '@djangocfg/api/auth';
export function UserProfile() {
const {
user, // UserProfile | null
isAuthenticated, // boolean
isLoading, // boolean
requestOTP, // (identifier, sourceUrl?) => Promise<{ success, message }>
verifyOTP, // (identifier, otp, sourceUrl?, redirectUrl?) => Promise<{ success, message, user? }>
logout, // () => void
} = useAuth();
if (isLoading) return <div>Loading...</div>;
if (!isAuthenticated) {
return <div>Please sign in</div>;
}
return (
<div>
<p>Welcome, {user?.email}!</p>
<button onClick={logout}>Logout</button>
</div>
);
}useAuthGuard
Protect routes from unauthenticated access:
'use client';
import { useAuthGuard } from '@djangocfg/api/auth';
export default function DashboardPage() {
const { isAllowed } = useAuthGuard({
redirectTo: '/auth',
requireAuth: true,
});
if (!isAllowed) return null;
return <div>Protected Dashboard Content</div>;
}useAuthForm
Manage authentication form state with OTP and 2FA support:
'use client';
import { useAuthForm } from '@djangocfg/api/auth';
export function OTPLoginForm() {
const {
// State
identifier, // Email address
otp, // 6-digit OTP code
step, // 'identifier' | 'otp' | '2fa' | '2fa-setup' | 'success'
isLoading,
error,
acceptedTerms,
isRateLimited, // boolean - true when rate limited
rateLimitLabel, // string - formatted countdown e.g. "1:30" or "45s"
// Setters
setIdentifier,
setOtp,
setAcceptedTerms,
// Handlers
handleIdentifierSubmit, // Request OTP
handleOTPSubmit, // Verify OTP
handleResendOTP, // Resend OTP
handleBackToIdentifier, // Go back to step 1
// Utilities
validateIdentifier,
} = useAuthForm({
sourceUrl: window.location.origin,
requireTermsAcceptance: false, // Set true if terms/privacy links provided
onIdentifierSuccess: (identifier) => {
console.log('OTP sent to', identifier);
},
onOTPSuccess: () => {
console.log('Login successful');
},
});
if (step === 'identifier') {
return (
<form onSubmit={handleIdentifierSubmit}>
<input
type="email"
value={identifier}
onChange={(e) => setIdentifier(e.target.value)}
placeholder="Enter email"
/>
{error && <p className="text-red-500">{error}</p>}
<button type="submit" disabled={isLoading}>
{isLoading ? 'Sending...' : 'Send verification code'}
</button>
</form>
);
}
return (
<form onSubmit={handleOTPSubmit}>
<p>Enter the code sent to {identifier}</p>
<input
type="text"
value={otp}
onChange={(e) => setOtp(e.target.value)}
placeholder="000000"
maxLength={6}
/>
{error && <p className="text-red-500">{error}</p>}
<button type="submit" disabled={isLoading || otp.length < 6}>
{isLoading ? 'Verifying...' : 'Verify'}
</button>
<button type="button" onClick={handleResendOTP}>Resend</button>
<button type="button" onClick={handleBackToIdentifier}>Back</button>
</form>
);
}useAuthForm Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| sourceUrl | string | required | Application URL for OTP emails |
| redirectUrl | string | - | URL to redirect after successful OTP verification |
| requireTermsAcceptance | boolean | false | Require terms acceptance before submit |
| onIdentifierSuccess | function | - | Callback after OTP sent |
| onOTPSuccess | function | - | Callback after successful verification |
| onError | function | - | Callback on error |
useGithubAuth
GitHub OAuth integration:
'use client';
import { useGithubAuth } from '@djangocfg/api/auth';
export function GithubLoginButton() {
const {
startGithubAuth, // () => void - redirects to GitHub
isLoading, // boolean
error, // string | null
} = useGithubAuth({
sourceUrl: window.location.origin,
});
return (
<button onClick={startGithubAuth} disabled={isLoading}>
{isLoading ? 'Connecting...' : 'Continue with GitHub'}
</button>
);
}OAuth callback is handled automatically by @djangocfg/layouts AuthLayout component.
useTwoFactor
Verify 2FA code during authentication:
'use client';
import { useTwoFactor } from '@djangocfg/api/auth';
export function TwoFactorVerify({ sessionId }: { sessionId: string }) {
const {
verify, // (code: string) => Promise<void>
isLoading, // boolean
error, // string | null
} = useTwoFactor({
sessionId,
onSuccess: (user) => console.log('2FA verified:', user),
onError: (error) => console.error('2FA failed:', error),
redirectUrl: '/dashboard',
skipRedirect: false, // Set true if handling navigation manually
});
const [code, setCode] = useState('');
return (
<form onSubmit={(e) => { e.preventDefault(); verify(code); }}>
<input
type="text"
value={code}
onChange={(e) => setCode(e.target.value)}
placeholder="000000"
maxLength={6}
/>
<button type="submit" disabled={isLoading || code.length < 6}>
{isLoading ? 'Verifying...' : 'Verify'}
</button>
</form>
);
}useTwoFactorSetup
Setup 2FA for authenticated users:
'use client';
import { useTwoFactorSetup } from '@djangocfg/api/auth';
export function TwoFactorSetup() {
const {
// State
qrCode, // Base64 QR code image
secret, // Manual entry secret
isEnabled, // Current 2FA status
isLoading,
error,
// Actions
startSetup, // () => Promise<void> - generates QR code
verifySetup, // (code: string) => Promise<void> - enables 2FA
disable, // () => Promise<void> - disables 2FA
checkStatus, // () => Promise<boolean> - refresh status
} = useTwoFactorSetup({
onSetupComplete: () => console.log('2FA enabled successfully'),
onDisabled: () => console.log('2FA disabled'),
onError: (error) => console.error('2FA setup error:', error),
});
if (isEnabled) {
return (
<div>
<p>Two-factor authentication is enabled</p>
<button onClick={disable}>Disable 2FA</button>
</div>
);
}
if (qrCode) {
return (
<div>
<img src={qrCode} alt="2FA QR Code" />
<p>Secret: {secret}</p>
<input placeholder="Enter code from app" />
<button onClick={() => verifySetup('123456')}>Verify & Enable</button>
</div>
);
}
return <button onClick={startSetup}>Set up 2FA</button>;
}useTwoFactorStatus
Check 2FA status and disable 2FA for authenticated users:
'use client';
import { useTwoFactorStatus } from '@djangocfg/api/auth';
export function TwoFactorStatus() {
const {
// State
has2FAEnabled, // boolean | null - current 2FA status
devices, // TwoFactorDevice[] - list of TOTP devices
isLoading,
error,
// Actions
fetchStatus, // () => Promise<void> - refresh status
disable2FA, // (code: string) => Promise<boolean> - disable with TOTP code
clearError, // () => void
} = useTwoFactorStatus();
useEffect(() => {
fetchStatus();
}, [fetchStatus]);
if (has2FAEnabled) {
return (
<div>
<p>2FA is enabled</p>
<p>Devices: {devices.length}</p>
<button onClick={() => disable2FA('123456')}>Disable 2FA</button>
</div>
);
}
return <p>2FA is not enabled</p>;
}Server Components & API Routes
Use fetchers for server-side data fetching:
import { Fetchers, api } from '@djangocfg/api';
// Server Component
export default async function ProfilePage() {
const profile = await Fetchers.getAccountsProfileRetrieve(api);
return <div>{profile.email}</div>;
}
// API Route
export async function GET() {
try {
const profile = await Fetchers.getAccountsProfileRetrieve(api);
return Response.json(profile);
} catch (error) {
return Response.json({ error: 'Failed to fetch profile' }, { status: 500 });
}
}Server Middleware
Proxy middleware for Django backend:
// middleware.ts
import { proxyMiddleware } from '@djangocfg/api/auth/server';
import { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
// Proxies /media/* and /api/* to Django backend
// Automatically forwards cookies and headers
return proxyMiddleware(request);
}
export const config = {
matcher: ['/media/:path*', '/api/:path*'],
};Client Hooks (SWR)
Use SWR hooks for reactive data fetching in client components:
'use client';
import { useAccountsProfileRetrieve, useAccountsProfileUpdate } from '@djangocfg/api/hooks';
export function Profile() {
const { data: profile, isLoading, error, mutate } = useAccountsProfileRetrieve();
const { trigger: updateProfile } = useAccountsProfileUpdate();
const handleUpdate = async () => {
await updateProfile({ first_name: 'John' });
mutate(); // Revalidate data
};
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<p>Email: {profile?.email}</p>
<button onClick={handleUpdate}>Update Profile</button>
</div>
);
}Validation Schemas
Zod schemas for runtime validation:
import { Schemas } from '@djangocfg/api';
// Validate API responses
const profile = Schemas.UserProfileSchema.parse(data);
// Validate form data
const loginData = Schemas.LoginRequestSchema.parse({
email: '[email protected]',
password: 'password123',
});TypeScript Types
Full type safety for all API operations:
import type {
UserProfile,
LoginRequest,
RegisterRequest,
OAuthProvider,
OAuthCallbackRequest,
} from '@djangocfg/api';
// Or use namespace
import type { CfgAccountsTypes } from '@djangocfg/api';
type Profile = CfgAccountsTypes.UserProfile;
type LoginReq = CfgAccountsTypes.LoginRequest;Extension Packages
Extensions are now in separate packages with their own API clients that share authentication:
| Extension | Package | Description |
|-----------|---------|-------------|
| Newsletter | @djangocfg/ext-newsletter | Newsletter subscription and campaigns |
| Knowledge Base | @djangocfg/ext-knowbase | Documentation, chat, RAG-powered AI |
| Leads | @djangocfg/ext-leads | Lead capture and contact forms |
| Payments | @djangocfg/ext-payments | Payment processing and subscriptions |
| Support | @djangocfg/ext-support | Support tickets and helpdesk |
Extension Usage
// Extensions automatically use shared auth from @djangocfg/api
import { apiSupport } from '@djangocfg/ext-support';
import { apiPayments } from '@djangocfg/ext-payments';
import { apiLeads } from '@djangocfg/ext-leads';
// All authenticated automatically if user is logged in
const tickets = await apiSupport.tickets.list();
const payments = await apiPayments.payments.list();
const leads = await apiLeads.leads.list();See individual extension documentation for details.
Error Handling
import { handleError, formatError } from '@djangocfg/api/auth';
try {
await api.accounts.login({ email, password });
} catch (error) {
handleError(error, (formattedError) => {
console.error('Login failed:', formattedError);
// formattedError has consistent structure:
// { message: string, code?: string, details?: any }
});
}Analytics Integration
Auth events are automatically tracked when analytics is enabled:
// Automatic tracking for:
// - AUTH_LOGIN_SUCCESS
// - AUTH_LOGOUT
// - AUTH_SESSION_EXPIRED
// - AUTH_TOKEN_REFRESH
// - AUTH_OAUTH_START
// - AUTH_OAUTH_SUCCESS
// - AUTH_OAUTH_FAIL
// - AUTH_2FA_SETUP_START
// - AUTH_2FA_SETUP_COMPLETE
// - AUTH_2FA_VERIFY_SUCCESS
// - AUTH_2FA_VERIFY_FAILAnalytics setup is handled by @djangocfg/layouts package.
Environment Variables
# Required
NEXT_PUBLIC_API_URL=http://localhost:8000
# OAuth (optional)
NEXT_PUBLIC_GITHUB_CLIENT_ID=your_github_client_id
# Static build (optional)
NEXT_PUBLIC_STATIC_BUILD=falseDevelopment
# Generate API client from Django OpenAPI schema
make generate
# Build package
make build
# Watch mode
pnpm devRequirements
- Next.js >= 15
- React >= 19
- SWR (peer dependency)
- Zod (bundled for validation)
License
MIT
