@auth31/auth-service
v1.0.16
Published
Reusable authentication package with login, forgot password, and reset password flows using React, TypeScript, and MUI
Readme
@auth31/auth-service
A comprehensive and reusable authentication service for React and Next.js applications, featuring Login, MFA (OTP Verification), Forgot Password, Reset Password, Change Password, and token refresh flows with Material UI (MUI) and dynamic branding support.
🚀 Features
- Pre-built Auth Pages: Highly optimized Login, MFA (Verify OTP), Forgot Password, Reset Password, and Change Password pages.
- Token Refresh: Built-in
POST /auth/refreshsupport with token rotation, cookie storage, and automatic retry on401. - Backend-Driven Error Messaging: Displays precise backend API error responses (
detail,message,error) seamlessly across all authentication flows without static fallback error strings. - Enhanced OTP Verification (MFA):
- Privacy & Security: Built-in email masking with interactive show/hide toggle.
- Persistent Cooldown: Session-backed OTP resend timer that maintains countdown across browser refreshes.
- Seamless Inputs: Mobile-friendly 6-digit input boxes with auto-focus and clipboard paste support.
- Dynamic Branding: Inject logos, titles, colors, and backgrounds dynamically via
BrandingProvideror remote config. - Flexible UI: Seamlessly integrates with Material UI (v5/v6) and custom themes.
- Multiple Frameworks: Complete integration support for React (Vite/CRA) and Next.js (App Router).
- Callback Driven: Handle authentication events (success/failure) and navigation overrides with custom callbacks.
🆕 What's New in Recent Updates
🔄 Token Refresh (/api/v1/auth/refresh)
- Exchange a valid refresh token for a new access + refresh token pair.
- Token rotation: the submitted refresh token is revoked; the client must replace both stored tokens.
- Public-Key header is sent automatically from cached branding (must match the app that issued the token).
- Single session: if another device already consumed the refresh token, the API returns
401and the package clears tokens and redirects to login. - Authenticated package API calls that receive
401automatically attempt one refresh, then retry.
⚡ Backend-Driven Error Toast Notifications
- Replaced hardcoded static error strings with direct backend error response parsing (
detail,message,error). - Clean, single-source toast notifications for failed API calls (e.g.
{"detail": "Invalid OTP code"}).
🔐 MFA & OTP Improvements
- Email Privacy Control: Automatically masks recipient email (
a*****@domain.com) with a toggle button to reveal the full email address. - Resend OTP Cooldown: Persisted timer storage (
otpResendStorage) ensuring cooldown timers remain accurate even if the page is refreshed. - Dynamic Text Templates: Support for template interpolation in
verifyOtpbranding configuration ({{email}}and{{expiryTime}}).
📦 Installation
To get started, install the package and its peer dependencies:
npm install @auth31/auth-service @mui/material @emotion/react @emotion/styled @mui/icons-material react-router-dom axios notistack js-cookie[!NOTE] Ensure you have
reactandreact-dom(>= 18) installed in your project.
🛠️ Usage: React (Vite/CRA)
Follow these steps to integrate the auth service into your React application.
1. Configure the Auth Provider
In your entry point (usually main.tsx or App.tsx), wrap your application with the hierarchical providers.
[!IMPORTANT]
appIdandbackend_urlare essential configuration properties. Other properties likeendpoints,callbacks, andnavigationare optional. SetonNavigateToLoginso expired sessions redirect correctly after a failed refresh.
import {
AuthConfigProvider,
BrandingProvider,
ThemeProvider,
NotistackProvider,
LoginPage,
ForgotPasswordPage,
ResetPasswordPage,
VerifyOtpPage
} from "@auth31/auth-service";
import { BrowserRouter, Routes, Route } from "react-router-dom";
const authConfig = {
appId: "12345", // Required
apiBaseUrl: "http://localhost:3000", // Frontend URL (Testing)
backend_url: "https://your-api-url.com/api/v1", // Backend API URL
navigation: {
onNavigateToLogin: () => {
window.location.assign("/");
}
},
callbacks: {
onTokenRefreshSuccess: (data) => {
console.log("Tokens refreshed", data);
},
onTokenRefreshError: (error) => {
console.error("Token refresh failed", error);
},
onSessionExpired: () => {
console.log("Session expired — user redirected to login");
}
}
};
function App() {
return (
<AuthConfigProvider config={authConfig}>
<BrandingProvider>
{/* ThemeProvider is optional. Use yours or ours! */}
<ThemeProvider>
<NotistackProvider>
<BrowserRouter>
<Routes>
<Route path="/" element={<LoginPage />} />
<Route path="/verify-otp" element={<VerifyOtpPage />} />
<Route
path="/forgot-password"
element={<ForgotPasswordPage />}
/>
<Route path="/reset-password" element={<ResetPasswordPage />} />
</Routes>
</BrowserRouter>
</NotistackProvider>
</ThemeProvider>
</BrandingProvider>
</AuthConfigProvider>
);
}
export default App;🛠️ Usage: Next.js (App Router)
Since this package uses react-router-dom for internal navigation, it requires a client-side wrapper to function within Next.js.
Step 1: Create a Client-Side Auth Wrapper
Create components/AuthServiceWrapper.tsx:
"use client";
import {
AuthConfigProvider,
BrandingProvider,
ThemeProvider,
NotistackProvider,
LoginPage,
ForgotPasswordPage,
ResetPasswordPage,
VerifyOtpPage
} from "@auth31/auth-service";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { useRouter } from "next/navigation";
export default function AuthServiceWrapper() {
const router = useRouter();
const authConfig = {
appId: process.env.NEXT_PUBLIC_APP_ID!,
backend_url: process.env.NEXT_PUBLIC_API_URL!,
apiBaseUrl: "http://localhost:3000", // Frontend URL for testing
navigation: {
onNavigateToLogin: () => router.push("/auth/login"),
onNavigateToVerifyOtp: (data) =>
router.push(
`/auth/verify-otp?request_id=${data.request_id}&email=${data.email}`
)
},
callbacks: {
onLoginSuccess: (data) => {
console.log("Login success, proceed to MFA", data);
},
onVerifyOtpSuccess: () => router.push("/dashboard"),
onTokenRefreshSuccess: (data) => {
console.log("Tokens refreshed", data);
},
onSessionExpired: () => router.push("/auth/login")
}
};
return (
<AuthConfigProvider config={authConfig}>
<BrandingProvider>
<ThemeProvider>
<NotistackProvider>
<BrowserRouter basename="/auth">
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route path="/verify-otp" element={<VerifyOtpPage />} />
<Route
path="/forgot-password"
element={<ForgotPasswordPage />}
/>
<Route path="/reset-password" element={<ResetPasswordPage />} />
</Routes>
</BrowserRouter>
</NotistackProvider>
</ThemeProvider>
</BrandingProvider>
</AuthConfigProvider>
);
}Step 2: Create a Catch-all Route
Create app/auth/[...slug]/page.tsx to handle all authentication routes:
import dynamic from "next/dynamic";
// Disable SSR for the auth flow because it depends on browser routing
const AuthServiceWrapper = dynamic(
() => import("@/components/AuthServiceWrapper"),
{ ssr: false }
);
export default function AuthPage() {
return <AuthServiceWrapper />;
}🔑 Token Storage & Refresh (Host App Requirements)
Installing the package and wrapping providers is not enough for refresh. The MFA flow returns an authorization code, not tokens. Your host application must store tokens after it receives them (for example after exchanging the code with your backend).
1. Store tokens (required)
import { tokenStorage } from "@auth31/auth-service";
// After your app obtains access_token + refresh_token:
tokenStorage.set(access_token, refresh_token);Tokens are stored in cookies (access_token, refresh_token).
2. Refresh tokens
import { refreshSession, refreshTokenApi, tokenStorage } from "@auth31/auth-service";
// Recommended: rotates cookies, handles 401 → login
const data = await refreshSession();
// data.access_token, data.refresh_token, data.user, ...
// Or call the API directly (you must store the new pair yourself):
const response = await refreshTokenApi({
refresh_token: tokenStorage.getRefreshToken()!
});
tokenStorage.set(response.access_token, response.refresh_token);3. What the package does automatically
| Behavior | Details |
| -------- | ------- |
| Public-Key header | Injected from branding cache on every API call (including refresh) |
| Token rotation | refreshSession() replaces both cookies with the new pair |
| Auto-refresh on 401 | Package API calls that send Authorization retry once after refresh |
| Failed refresh / 401 | Clears cookies and redirects via navigation.onNavigateToLogin (fallback: /) |
[!IMPORTANT] Auto-refresh only applies to the package API client. If your host app uses its own Axios/fetch client, call
refreshSession()yourself on401, or usecreateApiClientfrom this package.
4. Clear tokens on logout
import { tokenStorage } from "@auth31/auth-service";
tokenStorage.clear();⚙️ Configuration Reference
AuthConfig
| Property | Type | Requirement | Description |
| ------------- | -------- | ------------ | ----------- |
| backend_url | string | Required | The base URL for your authentication backend API (e.g. https://…/api/v1). |
| apiBaseUrl | string | Optional | Frontend Testing URL. If provided, it takes precedence over the branding redirect_url and is used to construct forgot password links. Ideal for localhost testing. |
| appId | string | Required | ID used to fetch branding and for Public-Key scoped API calls (including refresh). |
| endpoints | object | Optional | Override default paths for login, forgotPassword, resetPassword, changePassword, and refresh. |
| callbacks | object | Optional | Success/Error hooks for auth actions, MFA, token refresh (onTokenRefreshSuccess, onTokenRefreshError), and onSessionExpired. |
| navigation | object | Optional | Override internal routing (crucial for Next.js). Includes onNavigateToLogin (used after failed refresh) and onNavigateToVerifyOtp. |
BrandingConfig
| Property | Description |
| ---------------- | -------------------------------------------------------------- |
| appName | The name of your application. |
| logoUrl | URL to your brand logo. |
| primaryColor | Hex code for primary buttons and accents. |
| secondaryColor | Hex code for success indicators. |
| background | Configuration for "gradient", "color", or "image" backgrounds. |
📦 Exported Components & Hooks
- Pages:
LoginPage,VerifyOtpPage,ForgotPasswordPage,ResetPasswordPage,ChangePasswordPage. - Providers:
AuthConfigProvider,BrandingProvider,ThemeProvider,NotistackProvider. - Hooks:
useAuthConfig(): Access current configuration.useBranding(): Access active branding and theme settings.
- Token / session:
tokenStorage:set,getAccessToken,getRefreshToken,clear.refreshSession(): Exchange refresh token, rotate cookies, redirect on failure.refreshTokenApi(): Low-levelPOST /auth/refreshcall.
- API helpers:
loginApi,verifyOtpApi,forgotPasswordApi,changePasswordApi,createApiClient.
📄 License
MIT © auth31
