edaten-auth
v3.1.1
Published
Plug-and-play JWT auth router for Express + MongoDB
Readme
edaten-auth
Plug-and-play JWT authentication router for Express. Database-agnostic — works with any database through a simple adapter interface.
Features
- JWT access + refresh token authentication
- HttpOnly cookie for refresh token
- Automatic token rotation on every refresh
- Works with any database (MongoDB, PostgreSQL, MySQL, Prisma, etc.)
- Protected route middleware included
- Lifecycle hooks:
onRegister,onLogin,onRefresh,onLogout
Installation
npm install edaten-authQuick Start
import express from "express";
import cookieParser from "cookie-parser";
import createAuth from "edaten-auth";
import { userAdapter } from "./userAdapter.js";
const app = express();
app.use(express.json());
app.use(cookieParser());
const authRouter = createAuth({
jwtSecret: process.env.JWT_SECRET,
jwtRefreshSecret: process.env.JWT_REFRESH_SECRET,
userAdapter,
});
app.use(authRouter);
app.listen(3000);Testing
The library includes a full test suite built with Vitest and Supertest.
Run tests
npm testWhat's covered
POST /register— successful registration, missing password, DB errorsPOST /login— successful login, wrong password, user not found, missing fieldsPOST /refresh— successful refresh, missing cookie, invalid/expired tokenPOST /logout— successful logout, missing cookie
Options
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
| jwtSecret | string | ✅ | — | Secret for access token |
| jwtRefreshSecret | string | ✅ | — | Secret for refresh token |
| userAdapter | object | ✅ | — | Database adapter (see below) |
| loginField | string | ❌ | "email" | Field used for login ("email", "username", etc.) |
| isProduction | boolean | ❌ | NODE_ENV === "production" | Sets secure cookie flags |
| cookieOptions | object | ❌ | {} | Override default cookie options |
| onRegister | function | ❌ | — | Called after successful register |
| onLogin | function | ❌ | — | Called after successful login |
| onRefresh | function | ❌ | — | Called after successful token refresh |
| onLogout | function | ❌ | — | Called after successful logout |
Endpoints
POST /register
// Request
{ "username": "john", "email": "[email protected]", "password": "123456" }
// Response
{ "user": { "id": 1, "email": "[email protected]", "username": "john" }, "accessToken": "..." }POST /login
// Request
{ "email": "[email protected]", "password": "123456" }
// Response
{ "user": { "id": 1, "email": "[email protected]", "username": "john" }, "accessToken": "..." }POST /refresh
Reads refreshToken from cookie automatically.
// Response
{ "user": { "id": 1, "email": "[email protected]" }, "accessToken": "..." }POST /logout
Clears refreshToken cookie.
// Response
{ "message": "Logged out" }Hooks
Hooks are async functions called after each auth event. Each hook receives the user object.
const authRouter = createAuth({
jwtSecret: process.env.JWT_SECRET,
jwtRefreshSecret: process.env.JWT_REFRESH_SECRET,
userAdapter,
onRegister: async (user) => {
console.log("New user registered:", user.email);
// send welcome email, create profile, etc.
},
onLogin: async (user) => {
console.log("User logged in:", user.email);
// log activity, update last_seen, etc.
},
onRefresh: async (user) => {
console.log("Token refreshed for:", user.email);
},
onLogout: async (user) => {
console.log("User logged out:", user.email);
// clear session data, log activity, etc.
},
});Protected Routes
import { authMiddleware } from "edaten-auth/middleware";
app.get("/profile", authMiddleware(process.env.JWT_SECRET), (req, res) => {
res.json({ userId: req.user.id });
});Security
- Access token — short-lived (15min), sent in response body. Store in memory, not localStorage.
- Refresh token — long-lived (30d), stored in
HttpOnlycookie. Not accessible via JavaScript. - Token rotation — on every
/refreshthe old token is invalidated and a new one is issued. - Secure cookie — in production
secure: trueandsameSite: "none"are set automatically. - Passwords — hashed with
bcrypt(salt rounds: 10) before storing.
Errors
| Endpoint | Status | Message |
|---|---|---|
| POST /register | 400 | Password is required |
| POST /register | 400 | Any DB error (e.g. duplicate email) |
| POST /login | 400 | {loginField} and password are required |
| POST /login | 400 | Invalid credentials |
| POST /login | 404 | User not found |
| POST /login | 500 | DB error |
| POST /refresh | 401 | Refresh token required |
| POST /refresh | 401 | Invalid refresh token |
| POST /refresh | 401 | Invalid or expired refresh token |
| POST /logout | 400 | Refresh token required |
| POST /logout | 500 | DB error |
userAdapter
You must implement a userAdapter object with these methods:
const userAdapter = {
findByLogin(loginField, value), // find user by field name and value
findById(id), // find user by id
create(data), // create user, return { id, ... }
addRefreshToken(id, token), // add token to user's refresh tokens
removeRefreshToken(id, token), // remove token from user's refresh tokens
hasRefreshToken(id, token), // return true/false
}See the /examples directory in the repository for ready-made adapters for MongoDB, PostgreSQL, MySQL and Prisma.
.env
JWT_SECRET=your_jwt_secret
JWT_REFRESH_SECRET=your_jwt_refresh_secret
# MongoDB
MONGO_URI=mongodb://...
# PostgreSQL
DATABASE_URL=postgresql://...
# MySQL
MYSQL_URL=mysql://...License
MIT
