secure-web-token 1.2.8 → 1.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +181 -113
  2. package/package.json +2 -2
package/README.md CHANGED
@@ -1,131 +1,135 @@
1
- # Secure Web Token (SWT)
1
+ # secure-web-token (SWT)
2
2
 
3
- A **secure, device-bound authentication token system** for Node.js applications.
3
+ > **The secure alternative to JWT** — encrypted, device-bound, and built for production security.
4
4
 
5
- ---
6
-
7
- ## 1. About the Package
5
+ [![npm total downloads](https://img.shields.io/npm/dt/secure-web-token?color=orange&logo=npm&label=total%20downloads)](https://www.npmjs.com/package/secure-web-token)
6
+ [![GitHub stars](https://img.shields.io/github/stars/your-username/secure-web-token?style=flat&logo=github&color=yellow)](https://github.com/your-username/secure-web-token/stargazers)
7
+ [![Node.js version](https://img.shields.io/node/v/secure-web-token?logo=node.js&color=green)](https://www.npmjs.com/package/secure-web-token)
8
+ [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-3178C6?logo=typescript)](https://www.npmjs.com/package/secure-web-token)
9
+ [![Encryption](https://img.shields.io/badge/Encryption-AES--256--GCM-brightgreen)](https://github.com/your-username/secure-web-token)
10
+ [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen)](https://github.com/your-username/secure-web-token/pulls)
11
+ [![Snyk Vulnerabilities](https://snyk.io/test/github/your-username/secure-web-token/badge.svg)](https://snyk.io/test/github/your-username/secure-web-token)
8
12
 
9
- **Secure Web Token (SWT)** is a next-generation alternative to JWT, built for **security-critical applications** where token leakage, device hijacking, or session reuse must be prevented.
13
+ ```bash
14
+ npm install secure-web-token
15
+ ```
10
16
 
11
- Unlike JWTs (which are only Base64 encoded), SWT uses **full encryption + server-side session binding**, making stolen tokens useless on other devices.
17
+ ---
12
18
 
13
- ### Key Highlights
19
+ ## Why SWT? (The JWT Problem)
14
20
 
15
- - **AES-256-GCM encrypted payloads**
16
- - **Device-bound tokens (single-device login)**
17
- - **Server-side session management**
18
- - **HttpOnly session cookies**
19
- - **Expiry support (`iat`, `exp`)**
20
- - Simple API: `sign()` and `verify()`
21
- - Memory store (Redis-ready design)
21
+ **JWT has well-known, unfixed security flaws.** If you are using JWT in a security-critical app and have not thought about these, you should stop and read this:
22
22
 
23
- ---
23
+ | Problem | JWT | SWT (Secure Web Token) |
24
+ | ----------------------- | ------------------------------------- | -------------------------------------------- |
25
+ | Payload encryption | ❌ Base64 only (readable by anyone) | ✅ AES-256-GCM encrypted |
26
+ | Device binding | ❌ Token works on any device | ✅ Bound to one device/session |
27
+ | True logout | ❌ Tokens stay valid after logout | ✅ Server-side revocation |
28
+ | Token theft impact | ❌ Stolen token = full account access | ✅ Stolen token is useless on another device |
29
+ | Sensitive data in token | ❌ Visible in browser devtools | ✅ Encrypted, never exposed |
24
30
 
25
- ## 2. What Problem Does It Solve?
31
+ > **If you are storing user roles, permissions, or any sensitive identifiers in a JWT — they are readable by anyone with the token.** SWT fixes this.
26
32
 
27
- ### Problems with JWT
33
+ ---
28
34
 
29
- - Payloads are readable (Base64 encryption)
30
- - Tokens can be reused on any device
31
- - No native device binding
32
- - Logout does not truly invalidate tokens
35
+ ## What is Secure Web Token (SWT)?
33
36
 
34
- ### How SWT Solves This
37
+ **Secure Web Token (SWT)** is a Node.js authentication library that replaces JWT with a system that is fundamentally more secure by design:
35
38
 
36
- - Encrypts payload using **AES-256-GCM**
37
- - Binds tokens to **server-managed device sessions**
38
- - Prevents token reuse across devices
39
- - Supports true logout via session revocation
40
- - Sensitive identifiers never reach the browser
39
+ 1. **AES-256-GCM Encryption** — Your token payload is fully encrypted, not just encoded.
40
+ 2. **Device Binding** — Each token is tied to the exact device it was issued to. A stolen token cannot be used from a different device.
41
+ 3. **Server-Side Session Management** Sessions live on the server. Logout actually works.
42
+ 4. **HttpOnly Cookie + Token Dual Guard** — Combines the security of HttpOnly cookies with encrypted bearer tokens.
41
43
 
42
- **Best suited for:**
43
- - Admin panels
44
- - SaaS dashboards
45
- - Course platforms
46
- - Internal tools
47
- - High-security APIs
44
+ **Best suited for:** Admin dashboards, SaaS apps, course platforms, internal tools, healthcare apps, fintech, and any application where a stolen session is unacceptable.
48
45
 
49
46
  ---
50
47
 
51
- ## 3. Available Functions
48
+ ## Installation
52
49
 
53
- ### `sign()`
54
-
55
- Creates a **secure, encrypted token** and optionally registers a **server-side device session**.
50
+ ```bash
51
+ npm install secure-web-token
52
+ ```
56
53
 
57
- ### `verify()`
54
+ ```ts
55
+ // ESM
56
+ import { sign, verify, getStore } from "secure-web-token";
58
57
 
59
- Validates and decrypts the token, ensuring the request comes from the **correct device and active session**.
58
+ // CommonJS
59
+ const { sign, verify, getStore } = require("secure-web-token");
60
+ ```
60
61
 
61
62
  ---
62
63
 
63
- ## 4. Boiler Code (Core Usage)
64
+ ## Quick Start
65
+
66
+ ### 1. Sign a Token (Login)
64
67
 
65
- ### `sign()` function
66
68
  ```ts
67
69
  import { sign } from "secure-web-token";
68
70
 
69
- const SECRET = "super-secret-key";
71
+ const SECRET = "your-256-bit-secret";
70
72
 
71
- const { token, sessionId } = sign(
72
- { userId: 1, role: "admin" },
73
- SECRET,
74
- {
75
- fingerprint: true,
76
- store: "memory",
77
- expiresIn: 3600,
78
- }
79
- );
73
+ const { token, sessionId } = sign({ userId: 1, role: "admin" }, SECRET, {
74
+ fingerprint: true, // bind to device
75
+ store: "memory", // server-side session store
76
+ expiresIn: 3600, // 1 hour
77
+ });
78
+
79
+ // Send `token` to client, store `sessionId` in HttpOnly cookie
80
80
  ```
81
81
 
82
- ---
82
+ ### 2. Verify a Token (Protected Route)
83
83
 
84
- ### `verify()` function
85
84
  ```ts
86
85
  import { verify, getStore } from "secure-web-token";
87
86
 
88
87
  const store = getStore("memory");
89
- const session = store.getSession(sessionId);
88
+ const session = store.getSession(sessionId); // from HttpOnly cookie
90
89
 
91
90
  const payload = verify(token, SECRET, {
92
91
  sessionId,
93
92
  fingerprint: session.fingerprint,
94
93
  store: "memory",
95
94
  });
95
+
96
+ // payload.data => { userId: 1, role: "admin" }
96
97
  ```
97
98
 
98
99
  ---
99
100
 
100
- ## 5. Demo App
101
+ ## Full Express.js Example
101
102
 
102
- ### Backend (Express.js + Node.js)
103
103
  ```ts
104
104
  import express from "express";
105
105
  import cookieParser from "cookie-parser";
106
- import cors from "cors";
107
106
  import { sign, verify, getStore } from "secure-web-token";
108
107
 
109
108
  const app = express();
110
- app.use(cors({ origin: true, credentials: true }));
111
- app.use(cookieParser());
112
109
  app.use(express.json());
110
+ app.use(cookieParser());
113
111
 
114
- const SECRET = "super-secret-key";
112
+ const SECRET = process.env.SWT_SECRET!;
115
113
  const store = getStore("memory");
116
114
 
115
+ // Login — issue SWT
117
116
  app.post("/login", (req, res) => {
118
- const user = { userId: 1, name: "Mintu" };
117
+ const user = { userId: 1, name: "Alice", role: "admin" };
119
118
 
120
119
  const { token, sessionId } = sign(user, SECRET, {
121
120
  fingerprint: true,
122
121
  store: "memory",
122
+ expiresIn: 3600,
123
123
  });
124
124
 
125
- res.cookie("swt_session", sessionId, { httpOnly: true });
125
+ // sessionId goes in an HttpOnly cookie — never accessible to JS
126
+ res.cookie("swt_session", sessionId, { httpOnly: true, secure: true });
127
+
128
+ // Encrypted token goes to client
126
129
  res.json({ token });
127
130
  });
128
131
 
132
+ // Protected route — verify SWT
129
133
  app.get("/profile", (req, res) => {
130
134
  try {
131
135
  const sessionId = req.cookies.swt_session;
@@ -144,48 +148,23 @@ app.get("/profile", (req, res) => {
144
148
  }
145
149
  });
146
150
 
151
+ // Logout — truly invalidates the session
152
+ app.post("/logout", (req, res) => {
153
+ const sessionId = req.cookies.swt_session;
154
+ store.deleteSession(sessionId);
155
+ res.clearCookie("swt_session");
156
+ res.json({ success: true });
157
+ });
158
+
147
159
  app.listen(4000);
148
160
  ```
149
161
 
150
- ### Frontend (React.js)
151
- ```tsx
152
- import { useState } from "react";
153
-
154
- function App() {
155
- const [user, setUser] = useState(null);
156
-
157
- const login = async () => {
158
- const res = await fetch("http://localhost:4000/login", {
159
- method: "POST",
160
- credentials: "include",
161
- });
162
- const data = await res.json();
163
- localStorage.setItem("token", data.token);
164
- };
165
-
166
- const profile = async () => {
167
- const token = localStorage.getItem("token");
168
- const res = await fetch("http://localhost:4000/profile", {
169
- credentials: "include",
170
- headers: { Authorization: `Bearer ${token}` },
171
- });
172
- console.log(await res.json());
173
- };
174
-
175
- return (
176
- <>
177
- <button onClick={login}>Login</button>
178
- <button onClick={profile}>View Profile</button>
179
- </>
180
- );
181
- }
162
+ ---
182
163
 
183
- export default App;
184
- ```
164
+ ## Token Payload Structure
185
165
 
186
- ---
166
+ The payload sent to the client is **fully AES-256-GCM encrypted**. Internally it looks like:
187
167
 
188
- ## 6. Payload Structure
189
168
  ```json
190
169
  {
191
170
  "data": {
@@ -194,31 +173,120 @@ export default App;
194
173
  },
195
174
  "iat": 1768368114,
196
175
  "exp": 1768369014,
197
- "fp": "device-id"
176
+ "fp": "device-fingerprint-id"
198
177
  }
199
178
  ```
200
179
 
180
+ Unlike JWT, **this is not readable** by decoding the token in the browser or on another server.
181
+
201
182
  ---
202
183
 
203
- ## 7. Installation
204
- ```bash
205
- npm install secure-web-token
206
- ```
184
+ ## SWT vs JWT — Deep Comparison
185
+
186
+ ### JWT Security Flaws (Why You Should Replace JWT)
187
+
188
+ **1. Payloads are not encrypted.**
189
+ JWT payloads are Base64URL encoded — not encrypted. Anyone who intercepts or steals the token can read the payload. If you store `role: "admin"` in a JWT, an attacker can see it.
190
+
191
+ **2. No device binding.**
192
+ A JWT issued to a user in New York can be used from a server in Russia. There is no native mechanism in JWT to prevent this.
193
+
194
+ **3. Logout does not work (by design).**
195
+ JWT is stateless. Once issued, a JWT is valid until it expires — even after the user logs out. The only fix (token blocklist) defeats the purpose of being stateless.
196
+
197
+ **4. Token theft = full session compromise.**
198
+ If a JWT is stolen via XSS or network interception, the attacker has full access for the token's entire lifetime.
199
+
200
+ ### How SWT Fixes Every One of These
201
+
202
+ | JWT Flaw | SWT Solution |
203
+ | ------------------- | --------------------------------------------------------------------------------- |
204
+ | Readable payload | AES-256-GCM encryption — payload is unreadable without the server secret |
205
+ | No device binding | Device fingerprint stored in server session — token only valid on original device |
206
+ | Logout doesn't work | Server-side session deletion — revocation is instant and permanent |
207
+ | Token theft | Stolen token cannot be used without matching device fingerprint + server session |
207
208
 
208
209
  ---
209
210
 
210
- ## 8. Importing
211
- ```ts
212
- // ESM
213
- import { sign, verify, getStore } from "secure-web-token";
211
+ ## Frequently Asked Questions
214
212
 
215
- // CommonJS
216
- const { sign, verify, getStore } = require("secure-web-token");
213
+ **Q: Is SWT a drop-in replacement for JWT?**
214
+ A: The API is simple and migration is straightforward. Instead of `jwt.sign()` use `swt.sign()`. The main addition is server-side session storage and device fingerprinting.
215
+
216
+ **Q: What encryption does SWT use?**
217
+ A: AES-256-GCM — the gold standard for symmetric authenticated encryption. The same algorithm used by TLS 1.3.
218
+
219
+ **Q: Does SWT support Redis?**
220
+ A: The architecture is Redis-ready. The store interface is designed to plug in Redis for production distributed systems.
221
+
222
+ **Q: Does using server-side sessions make SWT stateful?**
223
+ A: Yes — intentionally. The "stateless = good" assumption of JWT trades security for scalability. SWT recovers security while maintaining a minimal server-side footprint. For most applications, this is the correct tradeoff.
224
+
225
+ **Q: When should I still use JWT?**
226
+ A: JWT is acceptable for low-security, public-data, short-lived tokens between internal microservices where token interception risk is low. For anything user-facing, SWT is the better choice.
227
+
228
+ **Q: What Node.js version is required?**
229
+ A: Node.js 16+ (uses native `crypto` module for AES-256-GCM).
230
+
231
+ ---
232
+
233
+ ## Security Architecture
234
+
235
+ ```
236
+ Client Server
237
+ │ │
238
+ │ POST /login │
239
+ ├──────────────────────────────►│
240
+ │ │ sign(payload, secret, { fingerprint: true })
241
+ │ │ ┌─────────────────────────────────┐
242
+ │ │ │ 1. Encrypt payload (AES-256-GCM)│
243
+ │ │ │ 2. Generate device fingerprint │
244
+ │ │ │ 3. Store session server-side │
245
+ │ │ └─────────────────────────────────┘
246
+ │ { token } + [HttpOnly Cookie: sessionId]
247
+ │◄──────────────────────────────┤
248
+ │ │
249
+ │ GET /profile │
250
+ │ Authorization: Bearer <token>│
251
+ │ Cookie: swt_session=<id> │
252
+ ├──────────────────────────────►│
253
+ │ │ verify(token, secret, { sessionId, fingerprint })
254
+ │ │ ┌─────────────────────────────────┐
255
+ │ │ │ 1. Decrypt token │
256
+ │ │ │ 2. Match device fingerprint │
257
+ │ │ │ 3. Validate server session │
258
+ │ │ └─────────────────────────────────┘
259
+ │ { user: { ... } } │
260
+ │◄──────────────────────────────┤
217
261
  ```
218
262
 
219
263
  ---
220
264
 
221
- ### Final Note
265
+ ## Roadmap
266
+
267
+ - [x] AES-256-GCM payload encryption
268
+ - [x] Device fingerprint binding
269
+ - [x] Memory session store
270
+ - [x] Token expiry (`iat`, `exp`)
271
+ - [ ] Redis session store adapter
272
+ - [ ] Token rotation / refresh
273
+ - [ ] TypeScript types (strict)
274
+ - [ ] Express.js middleware helper
275
+ - [ ] Audit log support
276
+
277
+ ---
278
+
279
+ ## Contributing
280
+
281
+ PRs and issues welcome. If you find a security vulnerability, please open a private security advisory rather than a public issue.
282
+
283
+ ---
284
+
285
+ ## License
286
+
287
+ MIT
288
+
289
+ ---
222
290
 
223
- If you need **encrypted tokens**, **single-device login**, and **true logout**,
224
- **Secure Web Token (SWT)** is built for you.
291
+ > **Stop using JWT for sensitive user sessions. Your users deserve better.**
292
+ > `npm install secure-web-token`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "secure-web-token",
3
- "version": "1.2.8",
3
+ "version": "1.2.10",
4
4
  "description": "A secure web token utility",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -31,4 +31,4 @@
31
31
  "@types/node": "^25.0.6",
32
32
  "typescript": "^5.9.3"
33
33
  }
34
- }
34
+ }