@mounaji_npm/auth
v0.4.0
Published
Authentication provider, hooks, guards, and login components for Mounaji applications
Downloads
727
Maintainers
Readme
@mounaji_npm/auth
Authentication layer for Mounaji applications. Adapter-driven auth context, React hooks, role-based guards, and a complete ready-made auth UI — sign in, sign up, and forgot password — all in one drop-in package.
Supports Firebase Auth (Google, GitHub, Facebook, Twitter/X, Microsoft, Apple, email/password), NextAuth.js, and JWT/REST backends. New providers can be added by implementing the MounajiAdapter interface.
Interactive Setup (recommended)
# In your project directory — asks which provider, installs deps, writes boilerplate
npx @mounaji_npm/auth setupThe wizard detects your package manager, framework, and TypeScript usage, then installs everything and generates the adapter, provider wrapper, and optional login page.
Install
npm install @mounaji_npm/auth
# Firebase:
npm install firebase
# NextAuth.js:
npm install next-auth
# JWT/REST: no extra depsQuick Start
import { AuthProvider, useAuth } from '@mounaji_npm/auth';
import { createFirebaseAdapter } from '@mounaji_npm/auth/firebase';
import { initializeApp } from 'firebase/app';
import { getAuth, GoogleAuthProvider, GithubAuthProvider } from 'firebase/auth';
const auth = getAuth(initializeApp(firebaseConfig));
const adapter = createFirebaseAdapter({
auth,
providers: {
google: new GoogleAuthProvider(),
github: new GithubAuthProvider(),
},
});
export default function Root() {
return (
<AuthProvider adapter={adapter}>
<App />
</AuthProvider>
);
}
function App() {
const { user, login, logout, loading } = useAuth();
if (loading) return <p>Loading…</p>;
if (!user) return <button onClick={() => login({ method: 'google' })}>Sign in</button>;
return <button onClick={logout}>Sign out ({user.name})</button>;
}Architecture
@mounaji_npm/auth
├── Adapters
│ ├── createFirebaseAdapter (Google, GitHub, Facebook, Twitter/X, Microsoft, Apple + email)
│ └── createJwtAdapter (REST API — login/me/refresh)
├── Context
│ └── AuthProvider — wraps your app, exposes auth state
├── Hooks
│ ├── useAuth — full auth context
│ └── useRole — role-check helpers
├── Guards
│ ├── RoleGate — declarative render guard
│ ├── GuestGate — render only when logged out
│ └── AuthGuard — redirect guard + HOC
└── Components
├── LoginButton — avatar dropdown / sign-in button
├── LoginPage — full auth UI (sign in, sign up, forgot password)
└── PublicNavbar — sticky top nav for marketing pagesNormalized user shape
Regardless of adapter, user is always:
{
id: string,
name: string | null,
email: string | null,
avatar: string | null,
roles: Record<string, boolean>, // { admin: true, editor: true }
raw: any, // original provider object
}Adapters
Firebase (@mounaji_npm/auth/firebase)
Pass a providers map with any combination of Firebase auth providers:
import { createFirebaseAdapter } from '@mounaji_npm/auth/firebase';
import { initializeApp } from 'firebase/app';
import {
getAuth,
GoogleAuthProvider,
GithubAuthProvider,
FacebookAuthProvider,
TwitterAuthProvider,
OAuthProvider,
} from 'firebase/auth';
const auth = getAuth(initializeApp(firebaseConfig));
const adapter = createFirebaseAdapter({
auth,
providers: {
google: new GoogleAuthProvider(),
github: new GithubAuthProvider(),
facebook: new FacebookAuthProvider(),
twitter: new TwitterAuthProvider(),
microsoft: new OAuthProvider('microsoft.com'),
apple: new OAuthProvider('apple.com'),
},
});Registration and password reset are built in when you use the Firebase adapter:
const { register, resetPassword } = useAuth();
// Create a new account
await register({ name: 'Jane Smith', email: '[email protected]', password: 'secret123' });
// Send a reset email
await resetPassword('[email protected]');Roles from Firebase custom claims — set server-side:
// Node.js — Firebase Admin SDK
await admin.auth().setCustomUserClaims(uid, { admin: true, editor: true });They appear on user.roles after the next token refresh.
JWT / REST (createJwtAdapter)
import { createJwtAdapter } from '@mounaji_npm/auth';
const adapter = createJwtAdapter({
loginUrl: '/api/auth/login', // POST { method, email?, password? } → { token, user }
logoutUrl: '/api/auth/logout', // POST
meUrl: '/api/auth/me', // GET → { user, roles }
refreshUrl: '/api/auth/refresh', // POST → { token }
storage: 'localStorage', // 'localStorage' | 'sessionStorage' | 'memory'
tokenKey: 'mn_token',
});NextAuth.js (createNextAuthAdapter)
Full OAuth support for Next.js. See DOCS.md for the complete setup guide.
AuthProvider Props
<AuthProvider
adapter={adapter}
onLogin={(user) => console.log('Logged in', user)}
onLogout={() => console.log('Logged out')}
onAuthReady={(user) => console.log('Auth ready', user)}
>
{children}
</AuthProvider>| Prop | Type | Description |
|---|---|---|
| adapter | MounajiAdapter | Required. Auth provider adapter. |
| onLogin | (user) => void | Called after a successful sign-in or registration. |
| onLogout | () => void | Called after logout. |
| onAuthReady | (user\|null) => void | Called when the initial auth state resolves. |
useAuth
const {
user, // MounajiUser | null
token, // string | null
loading, // boolean
error, // string | null
isAuthenticated, // boolean
login, // async ({ method, email?, password? }) => void
logout, // async () => void
register, // async ({ name?, email, password }) => void — undefined if not supported
resetPassword, // async (email) => void — undefined if not supported
getToken, // async (forceRefresh?) => string | null
hasRole, // (role: string) => boolean
hasAnyRole, // (roles: string[]) => boolean
} = useAuth();register and resetPassword are undefined when the current adapter does not implement them (e.g. JWT adapter, NextAuth). Gate UI on their presence:
const { register, resetPassword } = useAuth();
{register && <a href="/register">Sign up</a>}
{resetPassword && <a href="/forgot">Forgot password?</a>}useRole
const {
isAdmin, // hasRole('admin')
isEditor, // hasRole('editor')
isViewer, // hasRole('viewer')
can, // (role) => boolean
canAny, // (roles[]) => boolean
roles, // Record<string, boolean>
} = useRole();Guards
RoleGate
import { RoleGate, GuestGate } from '@mounaji_npm/auth';
<RoleGate roles="admin"> <AdminPanel /> </RoleGate>
<RoleGate roles={['admin','editor']}> <EditTools /> </RoleGate>
<RoleGate authenticated> <Dashboard /> </RoleGate>
<GuestGate> <MarketingBanner /></GuestGate>AuthGuard
import { AuthGuard, withAuthGuard } from '@mounaji_npm/auth';
<AuthGuard redirectTo="/login" onRedirect={router.push}>
<ProtectedPage />
</AuthGuard>
// HOC variant
export default withAuthGuard(AdminPage, {
redirectTo: '/login',
roles: ['admin'],
onRedirect: (path) => router.push(path),
});Components
LoginPage
A complete, drop-in auth UI. Handles sign-in, sign-up, and forgot-password — all in one component with no routing required.
import { LoginPage } from '@mounaji_npm/auth';
<LoginPage
appName="My App"
tagline="Build something great"
logo={<img src="/logo.svg" />}
isDark={true}
methods={['google', 'github', 'facebook', 'email']}
allowRegister={true}
allowForgotPassword={true}
initialMode="login"
redirectTo="/dashboard"
onRedirect={router.push}
companyName="Acme Inc"
termsUrl="/terms"
privacyUrl="/privacy"
/>| Prop | Type | Default | Description |
|---|---|---|---|
| methods | string[] | ['google'] | Auth methods to show: 'google' 'github' 'facebook' 'twitter' 'microsoft' 'apple' 'email' |
| allowRegister | boolean | true | Show "Sign up" link and registration form. Hidden automatically if adapter has no register(). |
| allowForgotPassword | boolean | true | Show "Forgot password?" link. Hidden automatically if adapter has no resetPassword(). |
| initialMode | string | 'login' | Starting mode: 'login' 'register' 'forgot' |
| isDark | boolean | true | Dark or light theme |
| appName | string | 'Mounaji' | Brand name |
| tagline | string | 'AI Platform' | Tagline below brand name |
| subheadline | string | (per mode) | Override the per-mode subheadline |
| logo | ReactNode | gradient "M" | Logo element |
| redirectTo | string | '/dashboard' | Path passed to onRedirect after login |
| onRedirect | (path) => void | — | Navigation handler |
| onSuccess | (user) => void | — | Called after successful login/register |
| termsUrl | string | '/terms' | Terms of service link |
| privacyUrl | string | '/privacy' | Privacy policy link |
| companyName | string | — | Footer copyright name |
OAuth layout — when ≤ 2 OAuth providers are listed they render full-width ("Continue with Google"). With 3+ providers they collapse into a 2-column compact grid automatically.
LoginButton
Drop-in sign-in button / avatar dropdown. Shows a button when logged out; a user menu when logged in.
import { LoginButton } from '@mounaji_npm/auth';
<LoginButton isDark={true} onNavigate={router.push} />
<LoginButton compact isDark={true} onNavigate={router.push} />| Prop | Default | Description |
|---|---|---|
| isDark | true | Dark/light theme |
| compact | false | Icon-only mode |
| onNavigate | — | Navigation handler |
| loginLabel | 'Sign in' | Button label text |
| dashboardPath | '/dashboard' | Dropdown dashboard link |
| settingsPath | '/settings' | Dropdown settings link |
PublicNavbar
Sticky top nav for public/marketing pages.
import { PublicNavbar } from '@mounaji_npm/auth';
<PublicNavbar
appName="My App"
isDark={isDark}
onThemeToggle={() => setIsDark(d => !d)}
activePath={currentPath}
onNavigate={router.push}
navItems={[
{ label: 'Home', path: '/' },
{ label: 'Pricing', path: '/pricing' },
]}
loginProps={{ redirectTo: '/dashboard' }}
/>CSS Theming
All components use inline styles driven by CSS variables with the --mn-* prefix. Override them globally to match your brand:
:root {
--mn-color-primary: #3B82F6;
--mn-color-accent: #7C3AED;
--mn-color-bg-dark: #060919;
--mn-color-bg-light: #E5DED2;
--mn-border-dark: rgba(255,255,255,0.07);
--mn-border-light: #C9C2B6;
--mn-text-primary-dark: #F0F4FF;
--mn-text-primary-light: #1C1915;
--mn-text-secondary-dark: #94A3B8;
--mn-text-secondary-light: #47413C;
--mn-font-family: 'Inter', system-ui, sans-serif;
}Wiring with @mounaji_npm/api-client
import { useEffect } from 'react';
import { AuthProvider } from '@mounaji_npm/auth';
import { apiClient } from './api/client.js';
function ApiAuthBridge() {
useEffect(() => {
const unsub = adapter.onAuthChange((user, token) => {
apiClient.setAuthToken(token);
});
apiClient.setTokenRefresher(() => adapter.getToken(true));
return () => unsub?.();
}, []);
return null;
}
export default function Root() {
return (
<AuthProvider adapter={adapter}>
<ApiAuthBridge />
<App />
</AuthProvider>
);
}Full API reference and NextAuth.js setup: DOCS.md
