@induseuro-labs/auth-ui
v2.3.10
Published
A beautiful, reusable authentication UI template with RBAC support for Next.js and React applications
Readme
@induseuro-labs/auth-ui
A beautiful, reusable authentication UI template with RBAC (Role-Based Access Control) support for Next.js and React applications. Perfect for quickly adding professional login, signup, and password reset flows to any project.
Features
Authentication UI (MUI)
- Forms:
LoginForm,SignupForm,ForgotPasswordForm,ResetPasswordForm— email/password, Google entry point, OTP tab on login, forgot-password email request, and token-based “set new password” (?token=from email links). - Full pages:
LoginPage,SignupPage,ForgotPasswordPage,ResetPasswordPage— same forms insideAuthPageLayoutwith left image or slider and right form. - Layout primitive:
AuthPageLayoutfor custom compositions.
Session and state
AuthProvider+useAuth()— login, signup, Google, OTP login, refresh, logout, send reset email, complete password reset (token+newPassword).- Persisted session in
localStorage(default keyauth_session) withjwtToken,refreshToken,username,id,roles. - Cross-tab sync when the session key changes in another tab.
Built-in routing (no React Router required)
AuthRoutes— serves/login,/signup,/forgot-password,/reset-password,/dashboard,/unauthorizedusing the browser history API.lockToAuthRoutes— unknown paths redirect to/loginor/dashboarddepending on auth;allowedPathsfor extra public routes.
RBAC
- Roles: admin, user, moderator (
UserRoleenum). ProtectedRoute— gate by authentication and optionalrequiredRoles.useRole— convenience helpers (isAdmin, etc.).
Theming
createMuiTheme/defaultTheme— dark-friendly defaults; works with your own MUIThemeProvider.
Backend integration (no UI)
createAuthApiHandlers— buildsAuthProvidercallbacks from abaseURL+ default REST paths (JSONfetch). Override paths or merge with customonLogin, etc.- HTTP constants —
AUTH_TOKEN_STORAGE_KEY,AUTH_HEADER_KEYS(X-tenant,Authorization, …),AUTH_CONTENT_TYPESfor consistent headers. createAuthAxiosClient— Axios instance factory with tenant/accept headers, bearer fromauth_tokensstorage (separate fromauth_session), and 401 → refresh → retry. Paths default to/api/v1/auth/*(signup, login,loginOtp, forgot/reset password, Google, refresh).axiosis a peer dependency — install it alongside this package.
Utilities
useRouter/createDefaultRouter— small history-based router for SPAs not using Next.js.
Installation
npm install @induseuro-labs/auth-ui @mui/material @mui/icons-material @emotion/react @emotion/styled
# or
yarn add @induseuro-labs/auth-ui @mui/material @mui/icons-material @emotion/react @emotion/styled
# or
pnpm add @induseuro-labs/auth-ui @mui/material @mui/icons-material @emotion/react @emotion/styledPeer dependency: install axios in your app (required for createAuthAxiosClient):
npm install axiosNote: Next.js is optional. The package works with both Next.js and regular React applications.
Quick Start
1. Setup MUI Theme Provider
Wrap your app with MUI's ThemeProvider:
// app/layout.tsx (Next.js) or App.tsx (React)
import { ThemeProvider } from '@mui/material/styles'
import CssBaseline from '@mui/material/CssBaseline'
import { createMuiTheme } from '@induseuro-labs/auth-ui'
const theme = createMuiTheme()
export default function RootLayout({ children }) {
return (
<html>
<body>
<ThemeProvider theme={theme}>
<CssBaseline />
{children}
</ThemeProvider>
</body>
</html>
)
}2. Wrap your app with AuthProvider
// app/layout.tsx (Next.js) or App.tsx (React)
import { AuthProvider } from '@induseuro-labs/auth-ui'
export default function RootLayout({ children }) {
return (
<html>
<body>
<AuthProvider
onLogin={async (credentials) => {
// Your login API call
const response = await fetch('/api/login', {
method: 'POST',
body: JSON.stringify(credentials),
})
return response.json()
}}
onSignup={async (data) => {
// Your signup API call
const response = await fetch('/api/signup', {
method: 'POST',
body: JSON.stringify(data),
})
return response.json()
}}
onGoogleLogin={async () => {
// Your Google OAuth implementation
// ...
}}
>
{children}
</AuthProvider>
</body>
</html>
)
}3. Create Login Page
// app/login/page.tsx
'use client'
import { LoginPage } from '@induseuro-labs/auth-ui'
export default function LoginPage() {
return (
<LoginPage
redirectTo="/dashboard"
media="/images/auth-left.jpg" // single image mode
/>
)
}3A. Use Built-in Routes (No app routes required)
If you do not want to define /login, /signup, /forgot-password, /reset-password in each project, use AuthRoutes directly:
import { AuthProvider, AuthRoutes } from '@induseuro-labs/auth-ui'
export default function App() {
return (
<AuthProvider>
<AuthRoutes
// default paths:
// /login, /signup, /forgot-password, /reset-password, /dashboard, /unauthorized
lockToAuthRoutes
media={['/images/slide-1.jpg', '/images/slide-2.jpg']}
mediaMode="auto"
dashboardComponent={<div>Dashboard content</div>}
/>
</AuthProvider>
)
}With lockToAuthRoutes={true} (default), unknown routes are redirected:
- Not logged in ->
/login - Logged in ->
/dashboard
Full-page layout with image/slider
You can use ready-made full pages for login, signup, and forgot password.
import { LoginPage, SignupPage, ForgotPasswordPage, ResetPasswordPage } from '@induseuro-labs/auth-ui'
// Single image
<LoginPage media="/images/auth.jpg" mediaMode="single" />
// Multiple images -> slider (auto when mediaMode="auto")
<SignupPage
media={[
'/images/slide-1.jpg',
'/images/slide-2.jpg',
'/images/slide-3.jpg',
]}
mediaMode="auto"
sliderIntervalMs={5000}
/>
// With rich slide content
<ForgotPasswordPage
media={[
{
src: '/images/recover.jpg',
title: 'Recover Account Access',
description: 'Reset password and continue securely.',
},
{
src: '/images/security.jpg',
title: 'Your Security Matters',
description: 'Use strong passwords and keep your account protected.',
},
]}
/>
<ResetPasswordPage />4. Create Signup Page
// app/signup/page.tsx (Next.js) or pages/Signup.tsx (React)
'use client' // Only needed in Next.js
import { SignupForm } from '@induseuro-labs/auth-ui'
export default function SignupPage() {
return (
<div className="min-h-screen flex items-center justify-center bg-slate-900 p-8">
<SignupForm redirectTo="/dashboard" />
</div>
)
}5. Protect Routes
// app/dashboard/page.tsx (Next.js) or pages/Dashboard.tsx (React)
'use client' // Only needed in Next.js
import { ProtectedRoute, UserRole } from '@induseuro-labs/auth-ui'
export default function DashboardPage() {
return (
<ProtectedRoute>
<div>Your protected content</div>
</ProtectedRoute>
)
}
// Admin only page
export default function AdminPage() {
return (
<ProtectedRoute requiredRoles={[UserRole.ADMIN]}>
<div>Admin only content</div>
</ProtectedRoute>
)
}Backend integration
Fetch-based handlers for AuthProvider
If your backend matches the default JSON routes (see createAuthApiHandlers defaults), you can avoid repeating fetch boilerplate. createAuthApiHandlers does not call a logout URL — logout() only clears localStorage and session state. Redirect users to login when they have no session by wrapping protected pages with ProtectedRoute (or your own guard): when there is no token/session, isAuthenticated is false and redirectTo (default /login) runs.
import { AuthProvider, createAuthApiHandlers } from '@induseuro-labs/auth-ui'
const api = createAuthApiHandlers({
baseURL: 'https://your-api.example.com',
tenant: 'dev',
paths: {
// optional overrides, e.g. login: '/api/v1/auth/login'
},
})
export function Root({ children }: { children: React.ReactNode }) {
return (
<AuthProvider
{...api}
// merge or override any single handler:
// onLogin={customLogin}
storageKey="auth_session"
>
{children}
</AuthProvider>
)
}Axios client + shared header constants
Use this when you want a shared Axios instance for app APIs (not only auth forms), with 401 refresh retry and tokens in localStorage under auth_tokens by default (independent from AuthProvider’s auth_session unless you sync them):
import {
AUTH_TOKEN_STORAGE_KEY,
AUTH_HEADER_KEYS,
AUTH_CONTENT_TYPES,
createAuthAxiosClient,
} from '@induseuro-labs/auth-ui'
const authHttp = createAuthAxiosClient({
baseURL: process.env.NEXT_PUBLIC_API_URL ?? '',
tenant: 'dev',
tokenStorageKey: AUTH_TOKEN_STORAGE_KEY,
paths: {
// optional: override defaults under /api/v1/auth/*
},
})
// Use returned methods or the underlying instance:
const api = authHttp.getAxios()
// After login via this client, tokens are stored; interceptors attach:
// AUTH_HEADER_KEYS.authorization, AUTH_HEADER_KEYS.tenant, etc.Exported TypeScript types include AuthTokens, LoginResponse, AuthUserResponse, AuthAxiosClient, CreateAuthAxiosClientConfig, and AuthAxiosPaths.
API Reference
AuthProvider Props
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| onLogin | (credentials: LoginCredentials) => Promise<AuthResponse \| User> | No | Custom login handler |
| onSignup | (data: SignupData) => Promise<AuthResponse \| User> | No | Custom signup handler |
| onGoogleLogin | () => Promise<AuthResponse \| User> | No | Google OAuth handler |
| onOTPLogin | (otpData: OTPData) => Promise<AuthResponse \| User> | No | OTP login handler |
| onRefreshToken | (refreshToken: string) => Promise<AuthResponse> | No | Refresh token handler |
| onLogout | (session: AuthResponse \| null) => Promise<void> \| void | No | Optional server revoke; default logout is client-only (clears storageKey + auth_tokens) |
| additionalLogoutStorageKeys | string[] | No | Extra keys removed on logout (default: ['auth_tokens']; pass [] to only clear storageKey) |
| onSendOTP | (email: string) => Promise<void> | No | Send OTP handler |
| onResetPassword | (email: string) => Promise<void> | No | Password reset handler |
| onCompletePasswordReset | (payload: { token: string; newPassword: string }) => Promise<void> | No | Complete reset with token + new password |
| storageKey | string | No | localStorage key (default: 'auth_session') |
LoginForm Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| onSuccess | () => void | - | Callback on successful login |
| redirectTo | string | '/dashboard' | Redirect path after login |
| showBackButton | boolean | true | Show back button |
| showGoogleLogin | boolean | true | Show Google login button |
| showOTPTab | boolean | true | Show OTP tab |
| title | string | 'Log in' | Form title |
| subtitle | string | "Choose how you'd like to sign in." | Form subtitle |
| className | string | '' | Additional CSS classes |
SignupForm Props
Similar to LoginForm with signup-specific defaults.
ProtectedRoute Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| requiredRoles | UserRole[] | - | Required roles to access |
| redirectTo | string | '/login' | Redirect if not authenticated |
| unauthorizedRedirectTo | string | '/unauthorized' | Redirect if wrong role |
AuthRoutes Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| loginPath | string | '/login' | Login route path |
| signupPath | string | '/signup' | Signup route path |
| forgotPasswordPath | string | '/forgot-password' | Forgot password route path |
| resetPasswordPath | string | '/reset-password' | Reset password route path |
| dashboardPath | string | '/dashboard' | Post-login route path |
| unauthorizedPath | string | '/unauthorized' | Unauthorized route path |
| lockToAuthRoutes | boolean | true | Redirect unknown paths to auth/dashboard routes |
| allowedPaths | string[] | [] | Extra paths allowed when route lock is enabled |
| dashboardComponent | React.ReactNode | null | Component rendered for dashboard path |
| unauthorizedComponent | React.ReactNode | null | Component rendered for unauthorized path |
| notFoundComponent | React.ReactNode | null | Component rendered for unknown path when route lock is disabled |
Hooks
useAuth
import { useAuth } from '@induseuro-labs/auth-ui'
function MyComponent() {
const { user, session, isAuthenticated, refreshSession, logout, hasRole } = useAuth()
// Check if user has specific role
if (hasRole(UserRole.ADMIN)) {
// Admin content
}
// Refresh access token when needed
const handleRefresh = async () => {
await refreshSession()
}
}useRole
import { useRole } from '@induseuro-labs/auth-ui'
function MyComponent() {
const { isAdmin, isUser, isModerator } = useRole()
if (isAdmin()) {
// Admin content
}
}Auth Response Shape
The package supports and persists this API response shape in localStorage:
{
"data": {
"jwtToken": "string",
"refreshToken": "string",
"username": "string",
"id": 0,
"roles": ["string"]
},
"message": "string",
"success": true
}Types
import { User, UserRole, LoginCredentials, SignupData, AuthResponse, AuthData } from '@induseuro-labs/auth-ui'
enum UserRole {
ADMIN = 'admin',
USER = 'user',
MODERATOR = 'moderator',
}Using in React Apps (without Next.js)
This package works seamlessly with regular React applications. See REACT_APP_USAGE.md for detailed instructions.
Quick Example:
// App.tsx
import React from 'react'
import { BrowserRouter, Routes, Route } from 'react-router-dom'
import { AuthProvider, LoginForm } from '@induseuro-labs/auth-ui'
import { ThemeProvider } from '@mui/material/styles'
import { createMuiTheme } from '@induseuro-labs/auth-ui'
import CssBaseline from '@mui/material/CssBaseline'
const theme = createMuiTheme()
function App() {
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<BrowserRouter>
<AuthProvider>
<Routes>
<Route path="/login" element={
<div style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<LoginForm onSuccess={() => window.location.href = '/dashboard'} />
</div>
} />
</Routes>
</AuthProvider>
</BrowserRouter>
</ThemeProvider>
)
}
export default AppNote: For React apps, use the onSuccess callback with your router's navigate function for SPA navigation, or let the component use window.location for full page navigation.
Customization
Custom MUI Theme
All components use Material-UI. You can customize the theme:
import { createTheme } from '@mui/material/styles'
import { createMuiTheme } from '@induseuro-labs/auth-ui'
// Use the default theme
const theme = createMuiTheme()
// Or create your own
const customTheme = createTheme({
palette: {
mode: 'dark',
primary: {
main: '#9333ea',
},
},
})Custom Callbacks
<AuthProvider
onLogin={async (credentials) => {
// Your API integration
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credentials),
})
if (!response.ok) throw new Error('Login failed')
const authResponse = await response.json()
return authResponse
}}
onRefreshToken={async (refreshToken) => {
const response = await fetch('/api/auth/refresh', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refreshToken }),
})
if (!response.ok) throw new Error('Refresh failed')
const authResponse = await response.json()
return authResponse
}}
>
{children}
</AuthProvider>Examples
Full Page Layout with Sidebar
// app/login/page.tsx
'use client'
import { LoginForm } from '@induseuro-labs/auth-ui'
export default function LoginPage() {
return (
<div className="min-h-screen flex">
{/* Left Section - Image/Content */}
<div className="hidden lg:flex lg:w-1/2 relative bg-gradient-to-br from-slate-800 to-slate-900">
<div className="absolute inset-0 bg-[url('/your-image.jpg')] bg-cover bg-center opacity-20"></div>
<div className="relative z-10 flex flex-col justify-between p-12 text-white">
<div className="space-y-4">
<h2 className="text-4xl font-bold">Welcome Back</h2>
<p className="text-lg text-slate-300">
Sign in to continue to your account.
</p>
</div>
</div>
</div>
{/* Right Section - Login Form */}
<div className="flex-1 flex items-center justify-center p-8 bg-slate-900">
<LoginForm />
</div>
</div>
)
}Publishing to npm
- Update version in
package.json - Build the library:
npm run build:lib - Publish:
npm publish --access public
License
MIT
Support
For issues and feature requests, please open an issue on GitHub.
