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 🙏

© 2024 – Pkg Stats / Ryan Hefner

authenticrealm

v1.3.9

Published

Comprehensive authentication and authorization framework for Node.js applications, supporting user registration, login, email verification, password reset, and role-based access control.

Downloads

48

Readme

AuthenticRealm - Node.js Authentication Framework AuthenticRealm is a comprehensive solution for handling user authentication and authorization in Node.js applications. It simplifies the process of implementing user registration, login, email verification, password reset, and role-based access control, making it ideal for rapid development.

** search for Code Examples for In-depth explanation **

Features User registration and email verification Secure user login with JWT token management Password reset functionality Role-based access control for routes Extendable and customizable for various use cases Built-in error handling and middleware support

Prerequisites Node.js (version 12 or higher) Express MongoDB set up and running with mongoose package Sendgrid mailer set up jsonwebtoken and bcryptjs Basic understanding of Express.js and Mongoose

Quick Start Install AuthenticRealm

npm install authenticrealm

****Environment Setup Create a .env file in your project root:

JWT_SECRET=your_jwt_secret
SENDGRID_API_KEY=your_sendgrid_api_key
const { authenticateUser } = require('AuthenticRealm');

app.get('/protected', authenticateUser, (req, res) => {
res.send('Protected content');
});

OverView: This authentication framework provides a comprehensive solution for handling user authentication and authorization in Node.js applications. It includes support for user registration, login, email verification, password reset, and role-based access control.

Installation

npm install authenticrealm

Components

Error Handling CustomError: Base class for custom errors. BadRequest: Error for bad request (Status Code: 400). Unauthenticated: Error for unauthenticated access (Status Code: 401). Unauthorized: Error for unauthorized access (Status Code: 403). NotFound: Error for not found resources (Status Code: 404).

Middleware authenticateUser: Middleware to authenticate a user based on JWT tokens. authorizePermissions: Middleware to authorize user based on their roles. errorHandlerMiddleware: Middleware for handling errors in Express applications. notFound: Middleware to handle 404 Not Found errors. sessionTransactionMiddleware: Middleware for handling MongoDB transactions.

Models User: Mongoose model for users. Token: Mongoose model for storing refresh tokens.

Services AuthService: Service for handling user login and logout processes. UserService: Service for managing user data and operations. RegisterService: Service for handling user registration. EmailService: Service for sending emails using SendGrid.

Utilities checkPermissions: Utility function to check user permissions. cookiesHandler: Utility function to handle setting authentication tokens as cookies.

Before you start: Special NOTE

EmailService Customization

In the EmailService class, you need to customize the this.fromEmail and this.origin fields to match your application's details. These fields are used when sending out verification and password reset emails.

this.fromEmail: Set this to the email address you wish to use as the sender for your emails. For example: "YourName [email protected]". this.origin: This should be set to the base URL of your application. It's used for creating links in the email content, such as verification links or password reset links.

Customizing Verification and Reset Email URLs

In the EmailService methods sendVerificationEmail and sendResetPasswordEmail, ensure you adjust the URLs in the verifyEmailUrl and resetURL variables to match the routes in your application.

verifyEmailUrl: The URL for email verification. It should point to the route in your application that handles email verification. resetURL: The URL for password reset. It should direct the user to the password reset page in your application.

Usage

User Registration: **** To register a new user

const { RegisterService } = require('AuthenticRealm');
const registerService = new RegisterService(User, EmailService);

// In your route handler
await registerService.registerUser(req.body);

User Login **** To authenticate a user:

const { AuthService } = require('AuthenticRealm');
const authService = new AuthService();

// In your route handler
const user = await authService.login(email, password, req, res);

UserService Method Example:

// Import UserService
const { UserService } = require('AuthenticRealm');
const userService = new UserService(User);

// In a controller, for updating user details
const updateUserDetails = async (req, res) => {
const updatedUser = await userService.updateUser(req.user.id, req.body.email, req.body.name);
res.status(200).json({ message: "User updated successfully.", user: updatedUser });
};

Protecting Routes **** To protect routes using authentication and authorization middleware:

const { authenticateUser, authorizePermissions } = require('AuthenticRealm');

router.get('/protected-route', authenticateUser, authorizePermissions('admin'), (req, res) => {
// Protected route logic
});

User verification:

// In your Express route handler
router.post('/verify-email', async (req, res) => {
const { verificationToken, email } = req.body;

const user = await User.findOne({ email });
if (!user) {
throw new Unauthenticated("Email verification failed.");
}
user.verifyEmail(verificationToken);
await user.save();
res.status(200).json({ message: "Email successfully verified." });

});

Pasword Reset Request:

// In your Express route handler
router.post('/forgot-password', async (req, res) => {
const { email } = req.body;

const user = await User.findOne({ email });
if (user) {
const resetToken = user.generatePasswordResetToken();
await user.save();
await EmailService.sendResetPasswordEmail(user, resetToken);
}
res.status(200).json({
message: "If an account with the provided email exists, a password reset link has been sent to it.",
});

});

Resetting Password:

// In your Express route handler
router.post('/reset-password', async (req, res) => {
const { token, email, password } = req.body;

const user = await User.findOne({ email });
if (user) {
await user.resetPassword(token, password);
await user.save();
res.status(200).json({ message: "Your password has been successfully reset." });
}

});

Using Cookies:

// Import cookiesHandler from your framework
const { cookiesHandler } = require('AuthenticRealm');

// In a controller function, after user authentication
const user = ...; // User object after authentication
cookiesHandler({ res, user });

// This sets access and refresh tokens as HTTP-only cookies in the response

Handling Errors:

// Import error classes
const { NotFound, BadRequest, Unauthorized } = require('AuthenticRealm');

// In a controller function
const getUser = async (req, res) => {
const user = await User.findById(req.params.id);
if (!user) {
throw new NotFound("User not found.");
}

    // Check for other conditions
    if (someBadRequestCondition) {
        throw new BadRequest("Invalid request data.");
    }

    // Check for permissions
    if (!user.isAdmin) {
        throw new Unauthorized("Access denied.");
    }

    res.status(200).json({ user });

};

Using Session Middleware:

// Import sessionTransactionMiddleware
const { sessionTransactionMiddleware } = require('AuthenticRealm');

// Wrap your controller logic
const updateUser = sessionTransactionMiddleware(
async (req, res, next, session) => {
// Use session in your database operations
const updatedUser = await User.findByIdAndUpdate(req.user.id, { name: req.body.name }, { new: true, session });
res.status(200).json({ message: "User updated successfully.", user: updatedUser });
}
);

// Use 'updateUser' in your route definition
router.patch('/user/update', authenticateUser, updateUser);

Code Examples:

*Controller example for auth:

// Import necessary services and utilities from the 'authenticrealm' framework.
const {
  AuthService,
  RegisterService,
  EmailService,
  User,
  sessionTransactionMiddleware,
  BadRequest,
} = require("authenticrealm");

// Initialize the RegisterService and AuthService.
// RegisterService is responsible for handling user registration processes.
// AuthService handles login and logout functionalities.
const registerService = new RegisterService(User, EmailService);
const authService = new AuthService();

/**
 * User registration controller wrapped in sessionTransactionMiddleware.
 * This middleware ensures that all database operations within this controller
 * are part of a MongoDB transaction. This is crucial for maintaining data integrity,
 * especially when operations depend on each other (like user creation and email sending).
 */
const register = sessionTransactionMiddleware(
  async (req, res, next, session) => {
    // The registration process is invoked with the request body.
    // The session parameter ensures that this operation is part of the transaction.
    await registerService.registerUser(req.body, session);

    // If the registration process is successful, send a 201 response.
    res.status(201).json({
      msg: "Registration successful. Check your email for verification.",
    });
  }
);

// User login controller.
// This controller does not involve a transaction, as it's a straightforward authentication process.
const login = async (req, res) => {
  const { email, password } = req.body;
  const user = await authService.login(email, password, req, res);
  res.status(200).json({ user, msg: "Login successful." });
};

// User logout controller.
const logout = async (req, res) => {
  // The authService.logout method logs out the user based on their ID.
  await authService.logout(req.user.userId, res);
  res.status(200).json({ msg: "You have been successfully logged out." });
};

// Email verification controller.
// This controller allows users to verify their email address using a token sent to their email.
const verifyEmail = async (req, res) => {
  const { verificationToken, email } = req.body;
  const user = await User.findByEmailOrFail(email);
  user.verifyEmail(verificationToken);
  await user.save();
  res.status(200).json({ msg: "Email successfully verified." });
};

// Forgot password controller.
// This controller handles the process of sending a password reset link to the user's email.
const forgotPassword = async (req, res) => {
  const { email } = req.body;
  if (!email) {
    throw new BadRequest("A valid email address is required.");
  }
  const user = await User.findOne({ email });
  // If the user exists, generate a password reset token and send an email with the reset link.
  if (user) {
    const resetToken = user.generatePasswordResetToken();
    await user.save();
    await EmailService.sendResetPasswordEmail(user, resetToken);
  }
  // Send a response indicating that if an account exists, a reset link has been sent.
  res.status(200).json({
    msg: "If an account with the provided email exists, a password reset link has been sent to it.",
  });
};

// Password reset controller.
// This controller handles the actual password resetting using a token provided to the user.
const resetPassword = async (req, res) => {
  const { token, email, password } = req.body;
  if (!token || !email || !password) {
    throw new BadRequest("All fields are required to reset the password.");
  }

  // Find the user by email and reset their password using the provided token.
  const user = await User.findOne({ email });
  if (user) {
    await user.resetPassword(token, password);
    await user.save();
    res.status(200).json({ msg: "Your password has been successfully reset." });
  }
};

// Export the controllers to be used in routes.
module.exports = {
  register,
  login,
  logout,
  verifyEmail,
  forgotPassword,
  resetPassword,
};

Routes example:

const express = require("express");
const router = express.Router();

// Import authentication middleware from 'authenticrealm' framework.
// This middleware is used to ensure that the user is authenticated for certain routes.
const { authenticateUser } = require("authenticrealm");

// Import controllers from the authController.
// These controllers handle the various authentication and user account operations.
const {
  register,
  verifyEmail,
  login,
  resetPassword,
  forgotPassword,
  logout,
} = require("../controller/authController");

// Define routes and associate them with their respective controllers.

// Route for user registration.
router.post("/register", register);

// Route for user login.
router.post("/login", login);

// Route for user logout. The authenticateUser middleware ensures that only logged-in users can access this route.
router.delete("/logout", authenticateUser, logout);

// Route for email verification.
router.post("/verify-email", verifyEmail);

// Route for password reset request (forgot password).
router.post("/forgot-password", forgotPassword);

// Route for resetting the password.
router.post("/reset-password", resetPassword);

// Export the router to be used in the application.
module.exports = router;

User Controller:

// Import necessary services, utilities, and custom errors from the 'authenticrealm' framework.
const {
  User,
  NotFound,
  BadRequest,
  checkPermissions,
  sessionTransactionMiddleware,
  UserService,
} = require("authenticrealm");

// Initialize UserService with the User model to handle user-related operations.
const userService = new UserService(User);

// Controller for getting all users. This controller is typically used by admin users.
const getAllUsers = async (req, res) => {
  const users = await userService.getAllUsers();
  res.status(200).json({ msg: "Users retrieved successfully.", users });
};

// Controller for getting a single user by their ID.
const getSingleUser = async (req, res) => {
  const user = await userService.getSingleUser(req.params.id);
  if (!user) {
    throw new NotFound(`User not found with id: ${req.params.id}`);
  }
  // Check if the authenticated user has permission to access this user's data.
  checkPermissions(req.user, user._id);
  res.status(200).json({ msg: "User retrieved successfully.", user });
};

// Controller to show the currently authenticated user's information.
const showCurrentUser = async (req, res) => {
  res.status(200).json({ msg: "Current user information.", user: req.user });
};

// Controller for updating user details, wrapped in sessionTransactionMiddleware to ensure transactional integrity.
const updateUser = sessionTransactionMiddleware(
  async (req, res, next, session) => {
    const { email, name } = req.body;
    if (!email || !name) {
      throw new BadRequest(
        "Email and name are required for updating user details."
      );
    }
    const updatedUser = await userService.updateUser(
      req.user.userId,
      email,
      name,
      session,
      res
    );
    res
      .status(200)
      .json({ msg: "User details updated successfully.", updatedUser });
  }
);

// Controller for updating the user's password, also wrapped in sessionTransactionMiddleware.
const updateUserPassword = sessionTransactionMiddleware(
  async (req, res, next, session) => {
    const { oldPassword, newPassword } = req.body;
    if (!oldPassword || !newPassword) {
      throw new BadRequest("Both old and new passwords should be provided");
    }
    await userService.updateUserPassword(
      req.user.userId,
      oldPassword,
      newPassword,
      session
    );
    res.status(200).json({ msg: "Password updated successfully" });
  }
);

// Export the controllers to be used in the routes file.
module.exports = {
  getAllUsers,
  getSingleUser,
  showCurrentUser,
  updateUser,
  updateUserPassword,
};

User routes example:

const express = require("express");
const router = express.Router();

// Import authentication and authorization middleware from 'authenticrealm'.
const { authenticateUser, authorizePermissions } = require("authenticrealm");

// Import user-related controllers.
const {
  getAllUsers,
  getSingleUser,
  showCurrentUser,
  updateUser,
  updateUserPassword,
} = require("../controller/userController");

// Define user-related routes and associate them with their respective controllers.

// Route to get all users. Accessible only by admin users.
router
  .route("/")
  .get(authenticateUser, authorizePermissions("admin"), getAllUsers);

// Route for the current user to view their own information.
router.route("/myInfo").get(authenticateUser, showCurrentUser);

// Route for the current user to update their details.
router.route("/updateUser").patch(authenticateUser, updateUser);

// Route for the current user to update their password.
router.route("/updateUserPassword").patch(authenticateUser, updateUserPassword);

// Route to get a single user by ID. Requires authentication.
router.route("/:id").get(authenticateUser, getSingleUser);

// Export the router to be used in the main application.
module.exports = router;

User Model Methods:

comparePassword(userPassword) Purpose: Compares a provided plaintext password with the user's hashed password stored in the database. Parameters: userPassword: The plaintext password to compare against the hashed password. Returns: A boolean value (true if the password matches, false otherwise). Usage: Often used during the login process to validate a user's password.

createTokenUser() Purpose: Creates a simplified user object suitable for generating JWT payloads. This method excludes sensitive information like the user's password. Returns: An object containing selected user fields (e.g., name, userId, role). Usage: Used when creating JWTs to ensure sensitive information is not included in the token.

createJWT() Purpose: Generates a JSON Web Token (JWT) for user authentication. Returns: A JWT string. Usage: Called after a successful login or registration to create an access token for the user.

generateTokens() Purpose: Generates both access and refresh tokens for the user. Returns: An object containing accessToken and refreshToken. Usage: Utilized during the login and registration process to provide the user with tokens for authentication and session management.

getCookieOptions(isAccessToken) Purpose: Defines cookie options for setting cookies in HTTP responses. Parameters: isAccessToken: A boolean indicating if the cookie is for the access token (true) or the refresh token (false). Returns: Cookie options object. Usage: Called when setting access and refresh tokens as HTTP-only cookies.

generateVerificationToken() Purpose: Generates a token for email verification. Modifies: Sets a random token string to the verificationToken field of the user. Usage: Called during user registration to create a token for verifying the user's email address.

verifyEmail(token) Purpose: Verifies the user's email using a provided token. Parameters: token: The verification token to match. Throws: An error if the token does not match. Modifies: Updates the user's verification status if the token matches. Usage: Used in the email verification process.

generatePasswordResetToken() Purpose: Generates a password reset token. Returns: The generated raw password reset token. Modifies: Sets a random token string to the passwordToken field and defines its expiration date. Usage: Called when a user requests a password reset.

resetPassword(token, newPassword) Purpose: Resets the user's password using a provided reset token. Parameters: token: The password reset token to be matched. newPassword: The new password to set for the user. Throws: An error if the token is invalid or expired. Modifies: Updates the user's password and clears the password reset token and expiration date. Usage: Used in the password reset process.

findByEmailOrFail(email) Purpose: Finds a user by email or throws an error if not found. Parameters: email: The email address to search for. Returns: The found user document. Throws: A NotFound error if no user is found with the given email. Usage: Useful for operations where a user must be retrieved based on their email, such as during login or password reset.

Configuration: Make sure to configure the necessary environment variables such as 'JWT_SECRET' for JWT token generation and 'SENDGRID_API_KEY'

Dependencies: The framework depends on several npm packages like 'express', 'mongoose', 'jsonwebtoken', '@sendgrid/mail', etc. Ensure therse are installed in your application.

Support and Contact For support or feedback, please contact [email protected].

Special Thanks A special thanks to John Smilga for his invaluable contributions and insights. This package draws inspiration from his code and teachings in the realm of web development. His work has been instrumental in shaping various aspects of this framework.