npm.io
0.5.0 • Published 3d agoCLI

@mounaji_npm/auth

Licence
MIT
Version
0.5.0
Deps
0
Size
581 kB
Vulns
0
Weekly
55

@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.


# In your project directory — asks which provider, installs deps, writes boilerplate
npx @mounaji_npm/auth setup

The 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 deps

Quick 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 pages
Normalized 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: 'jane@example.com', password: 'secret123' });

// Send a reset email
await resetPassword('jane@example.com');

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 }) => voidundefined if not supported
  resetPassword,    // async (email) => voidundefined 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

Keywords