npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mounaji_npm/auth

v0.4.0

Published

Authentication provider, hooks, guards, and login components for Mounaji applications

Downloads

727

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 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: '[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