security-express
v0.3.2
Published
security-express
Maintainers
Readme
security-express
A lightweight authorization middleware for Express applications.
security-express provides a simple, flexible authorization layer based on middleware and dependency injection. It does not dictate how users authenticate or where permissions are stored, allowing it to integrate seamlessly with existing authentication systems and data sources.
Features
- Express-native middleware
- Dependency injection for permission resolution
- Supports bitmask permissions
- Supports hierarchical permission levels
- Database-agnostic
- Framework-independent permission loading
- Zero runtime dependencies (other than Express)
- Small, composable API
Installation
npm install security-expressor
yarn add security-expressRequest flow
Auth middleware → sets res.locals.userId
↓
authorize(\"invoice\", write)
↓
calls privilege(userId, \"invoice\")
↓
returns permission bitmask (e.g. 3 = read|write)
↓
If (action & p) === action → next(),
else 403Concepts
The library separates authorization into two responsibilities:
- Authorizer — Express middleware that enforces permissions.
- PrivilegeLoader — Helper that loads user permissions from a data source.
Authentication is intentionally outside the scope of this library.
Authentication
│
▼
res.locals.userId
│
▼
Authorizer
│
▼
privilege(userId, privilege)
│
▼
Database / API / Redis / LDAP / ...Examples
- cms-backoffice: A backoffice microservice for a CMS (user, role, audit-log, category, content, job)
- admin-service: A backoffice microservice for a common fintech product (user, role, audit-log, currency, country, locale)
Basic Usage
import express from "express";
import { Authorizer, PrivilegeLoader, read, write} from "security-express";
const app = express();
const loader = new PrivilegeLoader(
`
SELECT permission
FROM user_privileges
WHERE user_id = ?
AND privilege = ?
`,
query
);
const authorizer = new Authorizer(loader.privilege, console.error);
app.get(
"/users",
authorizer.authorize("user", read),
(req, res) => {
res.send("Allowed");
}
);
app.post(
"/users",
authorizer.authorize("user", write),
(req, res) => {
res.send("Created");
}
);Authorizer
Authorizer creates Express middleware that verifies whether the current user has sufficient permissions.
const authorizer = new Authorizer(privilege, logError);Constructor
new Authorizer(
privilege,
logError,
exact?,
userId?,
permissions?
)| Parameter | Description |
| ------------- | ---------------------------------------------------------------------------- |
| privilege | Function that returns a user's permission value. |
| logError | Error logging callback. |
| exact | Permission comparison mode. Defaults to true. |
| userId | Key used in res.locals for the authenticated user id. Default: "userId". |
| permissions | Key used to store resolved permissions. Default: "permissions". |
authorize()
authorizer.authorize(privilege, action?)Returns an Express middleware.
app.get(
"/orders",
authorizer.authorize("order", read),
handler
);If action is omitted, the middleware only verifies that the user possesses the specified privilege.
Permission Models
Bitmask Permissions (Default)
The default mode uses bitwise permission flags.
import { read, write, approve } from "security-express";| Permission | Value |
| ---------- | ---------: |
| none | 0 |
| read | 1 |
| write | 2 |
| approve | 4 |
| all | 2147483647 |
Permissions may be combined.
const permission = read | write;Checking permissions:
authorizer.authorize("invoice", write);A user with
read | writeis authorized.
Hierarchical Permissions
If permissions represent levels instead of flags, set exact to false.
const authorizer = new Authorizer(privilege, logError, false);Example:
| Level | Meaning | | ----: | ------------- | | 1 | Viewer | | 2 | Editor | | 3 | Manager | | 4 | Administrator |
A user with level 4 automatically satisfies checks for levels 1, 2, and 3.
PrivilegeLoader
PrivilegeLoader is a helper that converts database results into permission values.
const loader = new PrivilegeLoader(sql, query);Constructor
new PrivilegeLoader(sql, query)Where
query<T>(sql, args): Promise<T[]>can be implemented using any database library.
Example:
const loader = new PrivilegeLoader(
`
SELECT permission
FROM user_privileges
WHERE user_id = ?
AND privilege = ?
`,
db.query
);The loader combines multiple rows using bitwise OR.
For example:
| Row | | --: | | 1 | | 2 | | 4 |
becomes
7Custom Permission Provider
Instead of using SQL, permissions may come from any source.
const authorizer = new Authorizer(
async (userId, privilege) => {
return permissionService.getPermission(
userId,
privilege
);
},
console.error
);Possible sources include:
- SQL databases
- MongoDB
- Redis
- REST APIs
- GraphQL
- LDAP
- Active Directory
Authentication
Authentication is intentionally not included.
Populate res.locals.userId before calling the authorization middleware.
Example:
app.use(jwtAuthentication);
app.use(authorizer.authorize("invoice", read));Error Responses
The middleware returns:
| Status | Description | | -----: | ------------------------------------------- | | 401 | User is not authenticated. | | 403 | User does not have the required permission. |
API
Types
type HandleExpress middleware.
type AuthorizeAuthorization middleware factory.
interface SimpleMapSimple key/value map used for logging.
Constants
none
read
write
approve
allExports
Authorizer
PrivilegeLoader
none
read
write
approve
all
toString
Handle
Authorize
SimpleMapDesign Philosophy
security-express focuses on one responsibility:
Determine whether a request is authorized.
It deliberately avoids implementing authentication, session management, JWT validation, OAuth, or user management. Those concerns belong to the application.
By keeping authorization independent from authentication and storage, the library remains lightweight, composable, and easy to integrate into existing Express applications.
License
MIT
