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

@codeimplants/authkit

v2.1.0

Published

Multi-platform authentication module for Node.js with web and mobile support. Includes OTP, JWT, email/password authentication, and role-based authorization.

Readme

🔐 Auth Package

A comprehensive, production-ready authentication package for Node.js applications with full TypeScript support, web and mobile platform support, and multiple authentication methods including OTP, JWT, and role-based authorization.

✨ Features

  • 🔑 JWT Authentication - Access and refresh token management
  • 📧 Email/Password Authentication - Traditional email and password login with bcrypt encryption
  • 📱 Phone OTP - SMS-based one-time password verification
  • 📧 Email OTP - Email-based verification (ready for integration)
  • 🔒 Role-based Authorization - Flexible permission system
  • 🌐 Multi-Platform Support - Automatic detection and handling of web (cookies) and mobile (JSON) clients
  • 🔄 Hybrid App Support - Seamless authentication for React + Capacitor, Ionic, and other hybrid frameworks
  • 🍪 Cookie-based Tokens - Secure token storage for web applications
  • 📱 JSON Response Mode - Token delivery in JSON for mobile applications (React Native, Flutter, Ionic, etc.)
  • 🔄 Automatic Token Refresh - Seamless session management for both web and mobile
  • ⚡ Express Middleware - Easy integration with Express apps
  • 📝 Input Validation - Built-in request validation
  • 🛡️ Security Features - Rate limiting, secure cookies, environment-based configs
  • 📘 Full TypeScript Support - Complete type definitions and IntelliSense

🚀 Quick Start

Installation

npm install @codeimplants/authkit

Basic Usage (JavaScript)

import express from "express";
import mongoose from "mongoose";
import { AuthManager } from "@codeimplants/authkit";

// Define your User model
const userSchema = new mongoose.Schema({
  name: String,
  phoneNumber: { type: String, unique: true },
  userRole: { type: String, default: "user" },
  // ... other fields
});
const User = mongoose.model("User", userSchema);

// Initialize Auth Manager
const authManager = new AuthManager({
  User,
  jwtAccessSecret: process.env.JWT_ACCESS_SECRET,
  jwtRefreshSecret: process.env.JWT_REFRESH_SECRET,
  otpUrl: process.env.OTP_URL,
  otpApiKey: process.env.OTP_API_KEY,
  otptemplate: process.env.OTP_TEMPLATE,
  security: {
    responseMode: "auto", // 'auto', 'cookie', or 'json'
  },
});

// Set up routes
app.post(
  "/send-register-otp",
  authManager.validators.validateSendOtp,
  authManager.controllers.otp.sendRegisterOTP,
);
app.post(
  "/send-login-otp",
  authManager.validators.validateSendOtp,
  authManager.controllers.otp.sendLoginOTP,
);
app.post(
  "/verify-otp",
  authManager.validators.validateVerifyOtp,
  authManager.controllers.otp.verifyOtp,
);

// Email OTP routes
app.post("/send-email-otp", authManager.controllers.emailOtp.sendEmailOtp);
app.post("/verify-email-otp", authManager.controllers.emailOtp.verifyEmailOtp);

app.post("/refresh-token", authManager.controllers.auth.refreshAccessToken);
app.post("/logout", authManager.controllers.auth.logoutUser);

// Protected route
app.get("/profile", authManager.middleware.protect, (req, res) => {
  res.json({ user: req.user });
});

🌐 Web vs Mobile Support

Automatic Detection (Recommended)

The package automatically detects whether the client is a web browser or mobile app and responds accordingly:

const authManager = new AuthManager({
  User,
  jwtAccessSecret: process.env.JWT_ACCESS_SECRET,
  jwtRefreshSecret: process.env.JWT_REFRESH_SECRET,
  security: {
    responseMode: "auto", // Automatically detects client type
  },
});

Detection Priority (in order):

  1. X-Client-Type: web → Always uses cookie-based authentication (HTTP-only cookies)
  2. X-Client-Type: mobile → Always uses JSON-based authentication (tokens in response)
  3. User-Agent fallback → Checks for mobile indicators: Mobile, Android, iPhone, iPad, React Native, Ionic, Flutter

⚠️ Important for Hybrid Apps (React + Capacitor, Ionic, etc.):
Always send the X-Client-Type header to ensure correct authentication mode. The User-Agent alone cannot reliably distinguish between web and mobile versions of hybrid apps.

Manual Configuration

You can also manually set the response mode:

// Force cookie mode (web only)
security: {
  responseMode: "cookie";
}

// Force JSON mode (mobile only)
security: {
  responseMode: "json";
}

🔄 Hybrid App Support (React + Capacitor, Ionic, etc.)

The Challenge

Hybrid apps (React + Capacitor, Ionic, etc.) can run in two different environments:

  • Web: Running in a browser → Needs cookie-based authentication
  • Mobile: Running as a native app on iOS/Android → Needs JSON token authentication

Using the wrong authentication mode can lead to security vulnerabilities!

The Solution

Always send the X-Client-Type header based on the runtime platform:

import { Capacitor } from "@capacitor/core";
import axios from "axios";

// Detect if running as native mobile app
const isNativePlatform = () => {
  return Capacitor.isNativePlatform(); // true for iOS/Android, false for web
};

// Get the appropriate client type
const getClientType = () => {
  return isNativePlatform() ? "mobile" : "web";
};

// Create axios instance with dynamic configuration
const api = axios.create({
  baseURL: "https://api.example.com",
  withCredentials: !isNativePlatform(), // Only for web (cookies)
});

// Add interceptor to set X-Client-Type header
api.interceptors.request.use((config) => {
  config.headers["X-Client-Type"] = getClientType();

  // For mobile: Add access token from secure storage
  if (isNativePlatform()) {
    const accessToken = await SecureStore.getItemAsync("accessToken");
    if (accessToken) {
      config.headers["Authorization"] = `Bearer ${accessToken}`;
    }
  }
  // For web: Cookies are sent automatically

  return config;
});

export default api;

Login Flow for Hybrid Apps

import api from "./api";
import * as SecureStore from "expo-secure-store";

const login = async (phoneNumber: string, otp: string) => {
  const response = await api.post("/auth/verify-otp", {
    phoneNumber,
    otp,
  });

  if (isNativePlatform()) {
    // Mobile: Store tokens in secure storage
    const { accessToken, refreshToken } = response.data;
    await SecureStore.setItemAsync("accessToken", accessToken);
    await SecureStore.setItemAsync("refreshToken", refreshToken);
  }
  // Web: Tokens are automatically stored in HTTP-only cookies

  return response.data;
};

Why This Matters

| Scenario | Without X-Client-Type | With X-Client-Type | | ---------------- | -------------------------------------------- | ------------------------------------- | | Capacitor Web | ❌ May get JSON tokens (insecure in browser) | ✅ Gets HTTP-only cookies (secure) | | Capacitor Mobile | ⚠️ May get cookies (won't work) | ✅ Gets JSON tokens (works correctly) | | Pure Web | ✅ Gets cookies | ✅ Gets cookies | | Pure Mobile | ✅ Gets JSON tokens | ✅ Gets JSON tokens |

📱 Mobile Client Integration

React Native Example

// Login request
const login = async (email, password) => {
  const response = await fetch("https://api.example.com/auth/login", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-Client-Type": "mobile", // Ensures JSON response
    },
    body: JSON.stringify({ email, password }),
  });

  const data = await response.json();

  // Store tokens in secure storage
  await SecureStore.setItemAsync("accessToken", data.accessToken);
  await SecureStore.setItemAsync("refreshToken", data.refreshToken);

  return data;
};

// Protected API request
const fetchProfile = async () => {
  const accessToken = await SecureStore.getItemAsync("accessToken");

  const response = await fetch("https://api.example.com/profile", {
    headers: {
      Authorization: `Bearer ${accessToken}`,
      "X-Client-Type": "mobile",
    },
  });

  // Check for token refresh
  const newAccessToken = response.headers.get("X-New-Access-Token");
  const newRefreshToken = response.headers.get("X-New-Refresh-Token");

  if (newAccessToken) {
    await SecureStore.setItemAsync("accessToken", newAccessToken);
    await SecureStore.setItemAsync("refreshToken", newRefreshToken);
  }

  return response.json();
};

// Refresh token
const refreshToken = async () => {
  const refreshToken = await SecureStore.getItemAsync("refreshToken");

  const response = await fetch("https://api.example.com/auth/refresh-token", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-Client-Type": "mobile",
      "X-Refresh-Token": refreshToken,
    },
  });

  const data = await response.json();

  await SecureStore.setItemAsync("accessToken", data.accessToken);
  await SecureStore.setItemAsync("refreshToken", data.refreshToken);

  return data;
};

Flutter Example

import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';

class AuthService {
  final storage = FlutterSecureStorage();
  final String baseUrl = 'https://api.example.com';

  Future<Map<String, dynamic>> login(String email, String password) async {
    final response = await http.post(
      Uri.parse('$baseUrl/auth/login'),
      headers: {
        'Content-Type': 'application/json',
        'X-Client-Type': 'mobile',
      },
      body: jsonEncode({'email': email, 'password': password}),
    );

    final data = jsonDecode(response.body);

    // Store tokens securely
    await storage.write(key: 'accessToken', value: data['accessToken']);
    await storage.write(key: 'refreshToken', value: data['refreshToken']);

    return data;
  }

  Future<Map<String, dynamic>> fetchProfile() async {
    final accessToken = await storage.read(key: 'accessToken');

    final response = await http.get(
      Uri.parse('$baseUrl/profile'),
      headers: {
        'Authorization': 'Bearer $accessToken',
        'X-Client-Type': 'mobile',
      },
    );

    // Check for token refresh
    final newAccessToken = response.headers['x-new-access-token'];
    final newRefreshToken = response.headers['x-new-refresh-token'];

    if (newAccessToken != null) {
      await storage.write(key: 'accessToken', value: newAccessToken);
      await storage.write(key: 'refreshToken', value: newRefreshToken);
    }

    return jsonDecode(response.body);
  }
}

🌐 Web Client Integration

For web clients, tokens are automatically stored in HTTP-only cookies:

// Login request (browser)
const login = async (email, password) => {
  const response = await fetch("/api/auth/login", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    credentials: "include", // Important: Include cookies
    body: JSON.stringify({ email, password }),
  });

  const data = await response.json();
  // Tokens are automatically stored in cookies
  return data;
};

// Protected API request (browser)
const fetchProfile = async () => {
  const response = await fetch("/api/profile", {
    credentials: "include", // Important: Include cookies
  });

  return response.json();
};

📋 Response Formats

Web Response (Cookie Mode)

Login/Register Response:

{
  "_id": "user_id",
  "isVerified": true,
  "message": "Authentication successful"
}

Tokens are set in HTTP-only cookies:

  • jwt - Access token
  • refreshToken - Refresh token

Mobile Response (JSON Mode)

Login/Register Response:

{
  "_id": "user_id",
  "isVerified": true,
  "message": "Authentication successful",
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "tokenType": "Bearer",
  "expiresIn": 900
}

📋 Configuration Options

Required Configuration

{
    User: mongoose.Model,           // Your Mongoose User model
    jwtAccessSecret: string,        // Secret for access tokens
    jwtRefreshSecret: string,       // Secret for refresh tokens
}

Optional Configuration

{
    // OTP Configuration
    otp: {
        ttl: 5,                     // OTP time-to-live in minutes
        length: 6,                  // OTP length
        retryLimit: 3,              // Maximum retry attempts
        retryDelay: 60,             // Delay between retries (seconds)
    },

    // JWT Configuration
    jwt: {
        accessExpiresIn: '15m',     // Access token expiration
        refreshExpiresIn: '7d',     // Refresh token expiration
    },

    // Security Configuration
    security: {
        nodeEnv: 'development',     // Environment
        cookieSecure: false,        // Secure cookies (auto-set based on env)
        cookieSameSite: 'Lax',      // SameSite cookie attribute
        responseMode: 'auto',       // 'auto', 'cookie', or 'json'
    },

    // OTP Service Configuration
    otpUrl: string,                 // OTP service URL
    otpApiKey: string,              // OTP service API key
    otptemplate: string,            // OTP template name

    // Email Configuration (for Email OTP)
    email: {
        provider: 'nodemailer',     // Email provider: 'nodemailer', 'sendgrid', 'aws-ses'
        fromEmail: '[email protected]',
        nodemailer: {
            host: 'smtp.gmail.com', // SMTP host
            port: 587,              // SMTP port
            secure: false,          // true for 465, false for other ports
            auth: {
                user: '[email protected]',
                pass: 'your-app-password'
            },
            service: 'gmail'        // Optional: service name
        }
    }
}

🔧 API Reference

Controllers

Email/Password Controller

  • registerUser(req: Request, res: Response) - Register new user with email and password
  • loginUser(req: Request, res: Response) - Login user with email and password
  • changePassword(req: Request, res: Response) - Change password (authenticated)
  • resetPassword(req: Request, res: Response) - Reset password (unauthenticated)

OTP Controller

  • sendOtp(req: Request, res: Response) - Send OTP to phone number
  • verifyOtp(req: Request, res: Response) - Verify OTP and authenticate user

Email OTP Controller

  • sendEmailOtp(req: Request, res: Response) - Send OTP to email
  • verifyEmailOtp(req: Request, res: Response) - Verify email OTP and authenticate user

Auth Controller

  • refreshAccessToken(req: Request, res: Response) - Refresh access token using refresh token
  • logoutUser(req: Request, res: Response) - Logout user and clear tokens

Middleware

Protect Middleware

app.get("/protected", authManager.middleware.protect, (req: any, res) => {
  // req.user contains authenticated user with full type safety
  const user = req.user as IUser;
  res.json({ user });
});

Token Sources (in order of priority):

  1. Authorization header: Bearer <token>
  2. Cookie: jwt
  3. Custom header: X-Refresh-Token (for refresh tokens)

Authorization Middleware

app.get(
  "/admin",
  authManager.middleware.protect,
  authManager.middleware.authorize("admin"),
  (req: any, res) => {
    const user = req.user as IUser;
    res.json({ message: "Admin access granted", user });
  },
);

📱 Authentication Flow

Phone OTP Flow

  1. Send OTP

    POST /api/auth/send-otp
    Content-Type: application/json
    X-Client-Type: mobile
    
    {
      "phoneNumber": "+1234567890"
    }
  2. Verify OTP

    POST /api/auth/verify-otp
    Content-Type: application/json
    X-Client-Type: mobile
    
    {
      "phoneNumber": "+1234567890",
      "otp": "123456"
    }

    Mobile Response:

    {
      "_id": "user_id",
      "isVerified": true,
      "message": "Authentication successful",
      "accessToken": "...",
      "refreshToken": "...",
      "tokenType": "Bearer",
      "expiresIn": 900
    }
  3. Access Protected Resources

    GET /api/profile
    Authorization: Bearer <access-token>
    X-Client-Type: mobile

Token Refresh Flow

Web (Cookie):

POST /api/auth/refresh-token
Cookie: refreshToken=<refresh-token>

Mobile (JSON):

POST /api/auth/refresh-token
Content-Type: application/json
X-Client-Type: mobile
X-Refresh-Token: <refresh-token>

OR

{
  "refreshToken": "<refresh-token>"
}

🛡️ Security Features

  • HTTP-Only Cookies: Tokens stored in secure HTTP-only cookies (web)
  • Secure Storage: Tokens delivered in JSON for secure storage (mobile)
  • Automatic Token Refresh: Seamless token renewal in middleware for both platforms
  • Environment-Based Security: Different security settings for dev/prod
  • Input Validation: Built-in request validation using express-validator
  • Rate Limiting: Built-in rate limiting for OTP and password endpoints
  • Account Lockout: Automatic account lockout after failed attempts
  • Multi-Platform Support: Automatic detection and handling of web/mobile clients

🔮 Migration Guide

Updating from Cookie-Only Version

If you're upgrading from a version that only supported cookies, no changes are required! The package is backward compatible:

  1. Default behavior: Set responseMode: 'auto' to automatically detect client type
  2. Web-only apps: Keep using responseMode: 'cookie' or omit the setting
  3. Mobile apps: Add X-Client-Type: mobile header in requests or set responseMode: 'json'

Mobile App Checklist

  • [ ] Add X-Client-Type: mobile header to all requests
  • [ ] Use Authorization: Bearer <token> header for authenticated requests
  • [ ] Store tokens in secure storage (SecureStore, Keychain, etc.)
  • [ ] Handle token refresh by checking response headers
  • [ ] Implement token refresh logic when access token expires

Hybrid App Checklist (React + Capacitor, Ionic, etc.)

  • [ ] Use Capacitor.isNativePlatform() to detect runtime environment
  • [ ] Send X-Client-Type: web when running in browser
  • [ ] Send X-Client-Type: mobile when running as native app
  • [ ] Use withCredentials: true for web, false for mobile
  • [ ] Store tokens in secure storage for mobile, rely on cookies for web
  • [ ] Add request interceptor to automatically set correct headers
  • [ ] Test authentication in both web and mobile environments

📝 Environment Variables

Create a .env file:

# JWT Secrets
JWT_ACCESS_SECRET=your-super-secret-access-key
JWT_REFRESH_SECRET=your-super-secret-refresh-key

# OTP Service (for production)
OTP_URL=https://your-otp-service.com/api
OTP_API_KEY=your-otp-api-key
OTP_TEMPLATE=your-otp-template

# App Configuration
NODE_ENV=development
PORT=5000
FRONTEND_URL=http://localhost:3000

# Email Configuration (for Email OTP with Nodemailer)
[email protected]
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_SECURE=false
[email protected]
SMTP_PASS=your-app-password
SMTP_SERVICE=gmail

🔮 Roadmap

  • [x] Multi-platform support (Web & Mobile)
  • [x] Automatic client detection
  • [x] JSON response mode for mobile
  • [x] Hybrid app support with X-Client-Type header
  • [ ] OAuth providers (Google, GitHub, Facebook)
  • [ ] Two-factor authentication (2FA)
  • [ ] Session management
  • [ ] Audit logging
  • [ ] Enhanced TypeScript types for better IntelliSense

📚 Quick Reference

Platform Configuration Summary

| Platform | X-Client-Type Header | withCredentials | Token Storage | Authorization Header | | --------------------------------------- | -------------------- | --------------- | ----------------- | --------------------------------------- | | Pure Web (React, Vue, Angular) | web or omit | true | HTTP-only cookies | Not needed (cookies sent automatically) | | Pure Mobile (React Native, Flutter) | mobile | false | Secure storage | Bearer <accessToken> | | Capacitor/Ionic Web | web | true | HTTP-only cookies | Not needed (cookies sent automatically) | | Capacitor/Ionic Mobile | mobile | false | Secure storage | Bearer <accessToken> |

Response Format by Client Type

| Client Type | Access Token Location | Refresh Token Location | Additional Data in Response | | ------------------------------------ | ------------------------ | --------------------------------- | -------------------------------------------------------- | | Web (X-Client-Type: web) | jwt cookie (HTTP-only) | refreshToken cookie (HTTP-only) | _id, isVerified, message | | Mobile (X-Client-Type: mobile) | accessToken in JSON | refreshToken in JSON | _id, isVerified, message, tokenType, expiresIn |

Common Integration Patterns

Web App (React/Vue/Angular)

// Set once in your API client
axios.defaults.withCredentials = true;

// No need to set X-Client-Type (defaults to web)
// Tokens are automatically managed via cookies

Mobile App (React Native/Flutter)

// Always send X-Client-Type header
headers: { 'X-Client-Type': 'mobile' }

// Store tokens in secure storage
// Send access token in Authorization header

Hybrid App (Capacitor/Ionic)

// Detect platform and configure accordingly
const isNative = Capacitor.isNativePlatform();

api.defaults.headers["X-Client-Type"] = isNative ? "mobile" : "web";
api.defaults.withCredentials = !isNative;

📄 License

MIT

🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.