https://github.com/paulsmithkc/express-auth
Collection of authentication and authorization middleware functions for Express apps.
https://github.com/paulsmithkc/express-auth
auth authentication authorization expressjs nodejs
Last synced: 4 months ago
JSON representation
Collection of authentication and authorization middleware functions for Express apps.
- Host: GitHub
- URL: https://github.com/paulsmithkc/express-auth
- Owner: paulsmithkc
- License: mit
- Created: 2021-10-27T14:50:41.000Z (over 4 years ago)
- Default Branch: main
- Last Pushed: 2022-06-10T17:12:55.000Z (about 4 years ago)
- Last Synced: 2025-03-24T14:39:26.498Z (about 1 year ago)
- Topics: auth, authentication, authorization, expressjs, nodejs
- Language: JavaScript
- Homepage:
- Size: 79.1 KB
- Stars: 3
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# express-auth
Collection of authentication and authorization middleware functions.
## Install
Install this package.
```bash
npm install @merlin4/express-auth
```
Install peer dependencies, if needed.
```bash
npm install express cookie-parser jsonwebtoken config
```
## Table of Contents
- Middleware
- [authMiddleware](#authmiddleware)
- [installation](#installation)
- [configuration](#configuration)
- [req.auth](#reqauth)
- [isLoggedIn](#isloggedin-middleware)
- [hasAnyRole](#hasanyrole-middleware)
- [hasRole](#hasrole-middleware)
- [hasPermission](#haspermission-middleware)
- Utility Functions
- [fetchRoles](#fetchroles-utility-function)
- [mergePermissions](#mergepermissions-utility-function)
## authMiddleware
### installation
The authentication middleware will parse a Json Web Token (JWT) provided by the client,
via either an "Authorization" header with type "Bearer", or optionally via a cookie.
Install this middleware in your application entry point (index.js/server.js/app.js):
```js
const { authMiddleware } = require('@merlin4/express-auth');
app.use(
authMiddleware('my super secret key', 'authToken', {
httpOnly: true,
maxAge: 1000 * 60 * 15,
})
);
```
### configuration
The constructor for this middleware accepts 3 arguments:
1. **secret** the secret used to sign the JWT token (required)
2. **cookieName** the name of the cookie that holds the token (optional)
3. **cookieOptions** the options to use for refreshing the token (optional)
The **secret** is required to verify the token. This middleware will throw an exception on startup, if it not provided.
The **cookieName** will be used to parse the token from a cookie, when no "Authorization" header is provided. Note that you will need to install a cookie parser beforehand to parse the cookies. (Example shown below.)
```js
const cookieParser = require('cookie-parser');
const { authMiddleware } = require('@merlin4/express-auth');
app.use(cookieParser());
app.use(authMiddleware('my super secret key', 'authToken'));
```
If **cookieName** is not provided, then this middleware will ignore cookies and a cookie parser is not required.
If **cookieOptions** are also provided, then the auth cookie will be refreshed on every request, using the provided options. For security it is recommended that you provide the following options:
- `maxAge`, will set an expiration time in milliseconds for the auth cookie.
- `httpOnly: true`, will prevent the cookie from being read by front-end javascript code.
- `secure: true`, will prevent the cookie from being sent over HTTP.
See the [res.cookie()](https://expressjs.com/en/4x/api.html#res.cookie) for a full list of the available options.
If **cookieName** or **cookieOptions** are not provided, then the cookie will not be automatically refreshed.
We recommend using the [config](https://www.npmjs.com/package/config) package to help set these options.
```js
const config = require('config');
const cookieParser = require('cookie-parser');
const { authMiddleware } = require('@merlin4/express-auth');
app.use(cookieParser());
app.use(
authMiddleware(
config.get('auth.secret'),
config.get('auth.cookie.name'),
{
maxAge: parseInt(config.get('auth.cookie.maxAge')),
httpOnly: config.get('auth.cookie.httpOnly'),
secure: config.get('auth.cookie.secure'),
}
)
);
```
### req.auth
Once the middleware is installed and configured in the application. A new field will be accessible on the **Request** object to your other routes.
```js
app.get('/api/auth/me', (req, res) => {
const auth = req.auth;
// If a valid auth token is provided, then req.auth will be the token's payload.
// Otherwise req.auth, will be be undefined.
// ...
});
```
## isLoggedIn middleware
*Requires **authMiddleware** to be installed first.*
The **isLoggedIn** middleware checks that the user is logged in. It will send a 401 error if the user is not authenticated.
**Example:** Allow **authenticated users** to access the route.
```js
const { isLoggedIn } = require('@merlin4/express-auth');
app.get('/api/auth/me', isLoggedIn(), (req, res) => {
const auth = req.auth;
// ...
});
```
## hasAnyRole middleware
*Requires **authMiddleware** to be installed first.*
The **hasAnyRole** middleware checks that the user is logged in and has at least one role. It will send a 401 error if the user is not authenticated. It will send a 403 error if the user has no roles.
**Example:** Allow users with **any role** to access the route.
```js
const { hasAnyRole } = require('@merlin4/express-auth');
app.get('/api/auth/me', hasAnyRole(), (req, res) => {
const auth = req.auth;
const authRole = req.auth.role;
// ...
});
```
## hasRole middleware
*Requires **authMiddleware** to be installed first.*
The **hasRole** middleware checks that the user is logged in and has one of the allowed roles. It will send a 401 error if the user is not authenticated. It will send a 403 error if the user does not have one of the allowed roles.
**Example:** Allow **admins** to access this route.
```js
const { hasRole } = require('@merlin4/express-auth');
app.get('/api/admin', hasRole('admin'), (req, res) => {
const auth = req.auth;
const authRole = req.auth.role;
// ...
});
```
**Example:** Allow users with either the **admin or moderator** role to access this route.
```js
const { hasRole } = require('@merlin4/express-auth');
app.get('/api/mod', hasRole('admin', 'moderator'), (req, res) => {
const auth = req.auth;
const authRole = req.auth.role;
// ...
});
```
## hasPermission middleware
*Requires **authMiddleware** to be installed first.*
The **hasPermission** middleware checks the user has at least one of the listed permissions. It will send a 401 error if the user is not authenticated. It will send a 403 error if the user does not have one of the listed permissions.
**Example:** Allow users with the **manageUsers** permission to access this route.
```js
const { hasRole } = require('@merlin4/express-auth');
app.get('/api/user/list', hasPermission('manageUsers'), (req, res) => {
const auth = req.auth;
const permissions = req.auth.permissions;
// ...
});
```
**Example:** Allow users with either the **viewUsers or editUsers** permission to access this route.
```js
const { hasRole } = require('@merlin4/express-auth');
app.get('/api/user/list', hasPermission('viewUsers', 'editUsers'), (req, res) => {
const auth = req.auth;
const permissions = req.auth.permissions;
// ...
});
```
## fetchRoles utility function
The **fetchRoles** function will read the assigned roles from a user document and fetch all of roles from a data source, in parallel.
**Example:** Fetch the user from the database, and then fetch all of the assigned roles.
```js
const { fetchRoles } = require('@merlin4/express-auth');
const user = await db.findUserById(userId);
const roles = await fetchRoles(user, role => db.findRoleByName(role));
```
This function assumes that the user has a **"role"** field which specifies their role(s).
The user object may be in any of the following states:
User has **no role:**
```js
{ ...userData, role: null }
```
User has **a role:**
```js
{ ...userData, role: "admin" }
```
User has **an array of roles:**
```js
{ ...userData, role: ["admin", "manager"] }
```
User has **a map of roles:**
```js
{ ...userData, role: { admin: true, manager: true, editor: false } }
```
All of the above data structures are supported by **fetchRoles**
## mergePermissions utility function
The **mergePermissions** function will merge the permissions of the user and their assigned roles into a combined permissions map.
**Example:** Fetch the user from the database, fetch all of the assigned roles, and then merge all of the permission maps.
```js
const { fetchRoles, mergePermissions } = require('@merlin4/express-auth');
const user = await db.findUserById(userId);
const roles = await fetchRoles(user, role => db.findRoleByName(role));
const permissions = mergePermissions(user, roles);
```
This will read any user specific permissions from the user document, such as:
```js
{ ...userData, permissions: { canLogin: true } }
```
It will also read any permissions granted by a role, such as:
```js
[
{ name: 'Technical Manager', permissions: { manageUsers: true } },
{ name: 'Product Manager', permissions: { manageProducts: true } }
}
```
The combined permissions map would be as follows, for the data above:
```js
{
canLogin: true,
manageUsers: true,
manageProducts: true
}
```