secure-web-token 1.2.10 → 1.2.11

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 +291 -113
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,47 +1,81 @@
1
- # secure-web-token (SWT)
2
-
3
- > **The secure alternative to JWT** — encrypted, device-bound, and built for production security.
4
-
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)
12
-
13
- ```bash
14
- npm install secure-web-token
15
- ```
1
+ <p align="center">
2
+ <img src="https://res.cloudinary.com/dch9wfmjd/image/upload/v1778127305/logo1_eg2kay.png" alt="Secure Web Token Logo" width="150" />
3
+ </p>
4
+
5
+ <h1 align="center">secure-web-token (SWT)</h1>
6
+
7
+ <p align="center">
8
+ <strong>The secure, encrypted, device-bound alternative to JWT — built for Node.js</strong>
9
+ </p>
10
+
11
+ <p align="center">
12
+ <img src="https://res.cloudinary.com/dch9wfmjd/image/upload/v1778126974/downloads-badge_vyp6px.svg" alt="50K+ Downloads" width="560" />
13
+ </p>
14
+
15
+ <p align="center">
16
+ <a href="https://www.npmjs.com/package/secure-web-token">
17
+ <img src="https://img.shields.io/badge/downloads-50k%2B-orange?logo=npm" alt="Downloads" />
18
+ </a>
19
+ <a href="https://github.com/MintuSingh07/node-securewebtoken/stargazers">
20
+ <img src="https://img.shields.io/github/stars/MintuSingh07/node-securewebtoken?style=flat&logo=github&color=yellow" alt="GitHub Stars" />
21
+ </a>
22
+ <a href="https://www.npmjs.com/package/secure-web-token">
23
+ <img src="https://img.shields.io/badge/node-%3E%3D25.5.0-green?logo=node.js" alt="Node.js Version" />
24
+ </a>
25
+ <a href="https://www.npmjs.com/package/secure-web-token">
26
+ <img src="https://img.shields.io/badge/TypeScript-Ready-3178C6?logo=typescript" alt="TypeScript Ready" />
27
+ </a>
28
+ <a href="https://github.com/MintuSingh07/node-securewebtoken">
29
+ <img src="https://img.shields.io/badge/Encryption-AES--256--GCM-brightgreen" alt="AES-256-GCM" />
30
+ </a>
31
+ <a href="https://github.com/MintuSingh07/node-securewebtoken/pulls">
32
+ <img src="https://img.shields.io/badge/PRs-welcome-brightgreen" alt="PRs Welcome" />
33
+ </a>
34
+ <a href="https://snyk.io/test/github/MintuSingh07/node-securewebtoken">
35
+ <img src="https://snyk.io/test/github/MintuSingh07/node-securewebtoken/badge.svg" alt="Known Vulnerabilities" />
36
+ </a>
37
+ </p>
38
+
39
+ <p align="center">
40
+ <a href="#why-swt">Why SWT?</a> •
41
+ <a href="#installation">Installation</a> •
42
+ <a href="#quick-start">Quick Start</a> •
43
+ <a href="#full-expressjs-example">Full Example</a> •
44
+ <a href="#swt-vs-jwt--deep-comparison">SWT vs JWT</a> •
45
+ <a href="#faq">FAQ</a> •
46
+ <a href="#roadmap">Roadmap</a>
47
+ </p>
16
48
 
17
49
  ---
18
50
 
19
- ## Why SWT? (The JWT Problem)
51
+ ## Why SWT?
20
52
 
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:
53
+ **JWT has well-known, unfixed security problems.** If you're running a security-critical app — admin panel, SaaS dashboard, fintech, healthcare — and you haven't thought about these, stop and read this.
22
54
 
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 |
55
+ | Problem | JWT | SWT |
56
+ |---|---|---|
57
+ | Payload encryption | ❌ Base64 only readable by anyone | ✅ AES-256-GCM encrypted |
58
+ | Device binding | ❌ Token works on any device, anywhere | ✅ Bound to the original device/session |
59
+ | True logout | ❌ Tokens stay valid after logout | ✅ Instant server-side revocation |
60
+ | Token theft impact | ❌ Stolen token = full account access | ✅ Stolen token is useless on another device |
61
+ | Sensitive data in token | ❌ Visible in browser devtools | ✅ Encrypted, never exposed |
30
62
 
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.
63
+ > **If you're storing user roles, permissions, or any sensitive identifiers in a JWT — they're readable by anyone who gets that token.** SWT fixes this at the architecture level.
32
64
 
33
65
  ---
34
66
 
35
- ## What is Secure Web Token (SWT)?
67
+ ## What is Secure Web Token?
68
+
69
+ **Secure Web Token (SWT)** is a Node.js authentication library that replaces JWT with a system that is fundamentally more secure by design. It solves all four of JWT's critical weaknesses in one package.
36
70
 
37
- **Secure Web Token (SWT)** is a Node.js authentication library that replaces JWT with a system that is fundamentally more secure by design:
71
+ **How it works:**
38
72
 
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.
73
+ - 🔐 **AES-256-GCM Encryption** — Your token payload is fully encrypted, not just Base64 encoded. No one can read it without the server secret.
74
+ - 📱 **Device Binding** — Each token is tied to the exact device it was issued to via a server-stored fingerprint. A stolen token cannot be replayed from a different device.
75
+ - 🗄️ **Server-Side Session Management** — Sessions live on the server. Logout actually works — revocation is instant and permanent.
76
+ - 🍪 **HttpOnly Cookie + Token Dual Guard** — The session ID lives in an HttpOnly cookie (XSS-proof), the encrypted payload travels via Authorization header. Neither alone is enough.
43
77
 
44
- **Best suited for:** Admin dashboards, SaaS apps, course platforms, internal tools, healthcare apps, fintech, and any application where a stolen session is unacceptable.
78
+ **Best suited for:** Admin panels, SaaS dashboards, course platforms, internal tools, healthcare apps, fintech APIs, and any application where a stolen session is unacceptable.
45
79
 
46
80
  ---
47
81
 
@@ -70,13 +104,18 @@ import { sign } from "secure-web-token";
70
104
 
71
105
  const SECRET = "your-256-bit-secret";
72
106
 
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
- });
107
+ const { token, sessionId } = sign(
108
+ { userId: 1, role: "admin" },
109
+ SECRET,
110
+ {
111
+ fingerprint: true, // bind to this device
112
+ store: "memory", // server-side session store
113
+ expiresIn: 3600, // expires in 1 hour
114
+ }
115
+ );
78
116
 
79
- // Send `token` to client, store `sessionId` in HttpOnly cookie
117
+ // Send `token` to client
118
+ // → Store `sessionId` in an HttpOnly cookie (never send to client directly)
80
119
  ```
81
120
 
82
121
  ### 2. Verify a Token (Protected Route)
@@ -85,15 +124,23 @@ const { token, sessionId } = sign({ userId: 1, role: "admin" }, SECRET, {
85
124
  import { verify, getStore } from "secure-web-token";
86
125
 
87
126
  const store = getStore("memory");
88
- const session = store.getSession(sessionId); // from HttpOnly cookie
127
+ const session = store.getSession(sessionId); // retrieved from HttpOnly cookie
89
128
 
90
129
  const payload = verify(token, SECRET, {
91
130
  sessionId,
92
- fingerprint: session.fingerprint,
131
+ fingerprint: session.fingerprint, // must match original device
93
132
  store: "memory",
94
133
  });
95
134
 
96
- // payload.data => { userId: 1, role: "admin" }
135
+ // payload.data { userId: 1, role: "admin" }
136
+ ```
137
+
138
+ ### 3. Logout (True Revocation)
139
+
140
+ ```ts
141
+ // Session is deleted server-side — token is immediately dead
142
+ store.deleteSession(sessionId);
143
+ res.clearCookie("swt_session");
97
144
  ```
98
145
 
99
146
  ---
@@ -103,17 +150,22 @@ const payload = verify(token, SECRET, {
103
150
  ```ts
104
151
  import express from "express";
105
152
  import cookieParser from "cookie-parser";
153
+ import cors from "cors";
106
154
  import { sign, verify, getStore } from "secure-web-token";
107
155
 
108
156
  const app = express();
109
- app.use(express.json());
157
+ app.use(cors({ origin: true, credentials: true }));
110
158
  app.use(cookieParser());
159
+ app.use(express.json());
111
160
 
112
161
  const SECRET = process.env.SWT_SECRET!;
113
162
  const store = getStore("memory");
114
163
 
115
- // Login — issue SWT
164
+ // ──────────────────────────────────────────
165
+ // POST /login — Issue a secure session
166
+ // ──────────────────────────────────────────
116
167
  app.post("/login", (req, res) => {
168
+ // Authenticate user here (DB lookup, password check, etc.)
117
169
  const user = { userId: 1, name: "Alice", role: "admin" };
118
170
 
119
171
  const { token, sessionId } = sign(user, SECRET, {
@@ -122,14 +174,20 @@ app.post("/login", (req, res) => {
122
174
  expiresIn: 3600,
123
175
  });
124
176
 
125
- // sessionId goes in an HttpOnly cookie never accessible to JS
126
- res.cookie("swt_session", sessionId, { httpOnly: true, secure: true });
177
+ // sessionId HttpOnly cookie (invisible to JavaScript, XSS-proof)
178
+ res.cookie("swt_session", sessionId, {
179
+ httpOnly: true,
180
+ secure: true,
181
+ sameSite: "strict",
182
+ });
127
183
 
128
- // Encrypted token goes to client
184
+ // Encrypted token client (localStorage or memory)
129
185
  res.json({ token });
130
186
  });
131
187
 
132
- // Protected route — verify SWT
188
+ // ──────────────────────────────────────────
189
+ // GET /profile — Protected route
190
+ // ──────────────────────────────────────────
133
191
  app.get("/profile", (req, res) => {
134
192
  try {
135
193
  const sessionId = req.cookies.swt_session;
@@ -148,10 +206,12 @@ app.get("/profile", (req, res) => {
148
206
  }
149
207
  });
150
208
 
151
- // Logout — truly invalidates the session
209
+ // ──────────────────────────────────────────
210
+ // POST /logout — True session revocation
211
+ // ──────────────────────────────────────────
152
212
  app.post("/logout", (req, res) => {
153
213
  const sessionId = req.cookies.swt_session;
154
- store.deleteSession(sessionId);
214
+ store.deleteSession(sessionId); // token is dead immediately
155
215
  res.clearCookie("swt_session");
156
216
  res.json({ success: true });
157
217
  });
@@ -159,11 +219,60 @@ app.post("/logout", (req, res) => {
159
219
  app.listen(4000);
160
220
  ```
161
221
 
222
+ ### Frontend (React)
223
+
224
+ ```tsx
225
+ import { useState } from "react";
226
+
227
+ function App() {
228
+ const [user, setUser] = useState(null);
229
+
230
+ const login = async () => {
231
+ const res = await fetch("http://localhost:4000/login", {
232
+ method: "POST",
233
+ credentials: "include", // sends/receives the HttpOnly cookie
234
+ });
235
+ const { token } = await res.json();
236
+ localStorage.setItem("swt_token", token);
237
+ };
238
+
239
+ const getProfile = async () => {
240
+ const token = localStorage.getItem("swt_token");
241
+ const res = await fetch("http://localhost:4000/profile", {
242
+ credentials: "include",
243
+ headers: { Authorization: `Bearer ${token}` },
244
+ });
245
+ const data = await res.json();
246
+ setUser(data.user);
247
+ };
248
+
249
+ const logout = async () => {
250
+ await fetch("http://localhost:4000/logout", {
251
+ method: "POST",
252
+ credentials: "include",
253
+ });
254
+ localStorage.removeItem("swt_token");
255
+ setUser(null);
256
+ };
257
+
258
+ return (
259
+ <>
260
+ <button onClick={login}>Login</button>
261
+ <button onClick={getProfile}>View Profile</button>
262
+ <button onClick={logout}>Logout</button>
263
+ {user && <pre>{JSON.stringify(user, null, 2)}</pre>}
264
+ </>
265
+ );
266
+ }
267
+
268
+ export default App;
269
+ ```
270
+
162
271
  ---
163
272
 
164
273
  ## Token Payload Structure
165
274
 
166
- The payload sent to the client is **fully AES-256-GCM encrypted**. Internally it looks like:
275
+ The payload delivered to the client is **fully AES-256-GCM encrypted**. What lives inside (server-side only):
167
276
 
168
277
  ```json
169
278
  {
@@ -177,88 +286,145 @@ The payload sent to the client is **fully AES-256-GCM encrypted**. Internally it
177
286
  }
178
287
  ```
179
288
 
180
- Unlike JWT, **this is not readable** by decoding the token in the browser or on another server.
289
+ Unlike JWT, this structure **cannot be decoded in the browser**. There is no `atob()` trick. Without the server secret, it is ciphertext.
181
290
 
182
291
  ---
183
292
 
184
293
  ## SWT vs JWT — Deep Comparison
185
294
 
186
- ### JWT Security Flaws (Why You Should Replace JWT)
295
+ ### The 4 Security Problems with JWT
296
+
297
+ **1. Payloads are not encrypted**
298
+
299
+ JWT uses Base64URL encoding — not encryption. Anyone with the token can decode the payload instantly:
300
+
301
+ ```js
302
+ // This works on ANY JWT right now — no key required
303
+ JSON.parse(atob(token.split('.')[1]));
304
+ // → { userId: 1, role: "admin", email: "alice@example.com" }
305
+ ```
306
+
307
+ If your JWT payload leaks (XSS, logs, network interception), all your user data is exposed in plaintext.
308
+
309
+ **2. No device binding**
310
+
311
+ A JWT issued in one country works equally from any other device or server. There is no native way to say "this token belongs to this device." A stolen token is a valid credential — period.
187
312
 
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.
313
+ **3. Logout is not real**
190
314
 
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.
315
+ JWT is stateless by design. Once issued, a token remains cryptographically valid until it expires — regardless of what you do on the server. Client-side logout (clearing cookies/localStorage) doesn't invalidate the token. An attacker who stole it before logout still has access.
193
316
 
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.
317
+ **4. Token theft = full session compromise**
196
318
 
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.
319
+ There is no fallback. A stolen JWT gives the attacker the same access as the legitimate user for the token's entire lifetime, with no way to tell them apart.
199
320
 
200
- ### How SWT Fixes Every One of These
321
+ ### How SWT Fixes All Four
201
322
 
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 |
323
+ | JWT Flaw | SWT Solution |
324
+ |---|---|
325
+ | Readable payload | AES-256-GCM — unreadable without the server secret |
326
+ | No device binding | Device fingerprint stored in server session — wrong device = rejected |
327
+ | Logout doesn't work | `store.deleteSession()` immediate, permanent revocation |
328
+ | Token theft | Stolen token fails fingerprint check on any other device |
329
+
330
+ ### Attack Surface Comparison
331
+
332
+ ```
333
+ JWT Attack Model:
334
+ Attacker steals token via XSS
335
+ → Token is valid anywhere
336
+ → Full account access until expiry
337
+ → Nothing you can do
338
+
339
+ SWT Attack Model:
340
+ Attacker steals token via XSS
341
+ → Token requires matching HttpOnly cookie (not stealable via XSS)
342
+ → Even with both, device fingerprint must match
343
+ → Session can be revoked server-side instantly
344
+ ```
208
345
 
209
346
  ---
210
347
 
211
- ## Frequently Asked Questions
348
+ ## Security Architecture
349
+
350
+ ```
351
+ Client Server
352
+ │ │
353
+ │ POST /login │
354
+ ├──────────────────────────────────►│
355
+ │ │ sign(payload, secret, { fingerprint: true })
356
+ │ │ ┌───────────────────────────────────┐
357
+ │ │ │ 1. Encrypt payload (AES-256-GCM) │
358
+ │ │ │ 2. Generate device fingerprint │
359
+ │ │ │ 3. Store session server-side │
360
+ │ │ └───────────────────────────────────┘
361
+ │ { token } + Cookie: sessionId │
362
+ │◄──────────────────────────────────┤
363
+ │ │
364
+ │ GET /profile │
365
+ │ Authorization: Bearer <token> │
366
+ │ Cookie: swt_session=<id> │
367
+ ├──────────────────────────────────►│
368
+ │ │ verify(token, secret, { sessionId, fingerprint })
369
+ │ │ ┌───────────────────────────────────┐
370
+ │ │ │ 1. Decrypt token │
371
+ │ │ │ 2. Match device fingerprint │
372
+ │ │ │ 3. Validate active server session │
373
+ │ │ └───────────────────────────────────┘
374
+ │ { user: { ... } } │
375
+ │◄──────────────────────────────────┤
376
+ │ │
377
+ │ POST /logout │
378
+ ├──────────────────────────────────►│
379
+ │ │ store.deleteSession(sessionId)
380
+ │ │ → Token is dead. Immediately.
381
+ │ { success: true } │
382
+ │◄──────────────────────────────────┤
383
+ ```
384
+
385
+ ---
386
+
387
+ ## FAQ
212
388
 
213
389
  **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
390
 
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.
391
+ Migration is straightforward. Replace `jwt.sign()` with `sign()` from SWT and `jwt.verify()` with `verify()`. The main additions are server-side session storage and device fingerprinting — both handled automatically when you pass `fingerprint: true`.
392
+
393
+ ---
394
+
395
+ **Q: What encryption algorithm does SWT use?**
396
+
397
+ AES-256-GCM — the gold standard for symmetric authenticated encryption, recommended by NIST, and the same cipher used in TLS 1.3. It provides both confidentiality and integrity (tamper detection) in a single pass.
398
+
399
+ ---
218
400
 
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.
401
+ **Q: Does SWT support Redis for distributed systems?**
221
402
 
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.
403
+ The architecture is Redis-ready by design. The session store interface is built to accept pluggable adapters — Redis support is on the roadmap and can be integrated without changing your application code.
404
+
405
+ ---
406
+
407
+ **Q: SWT is stateful. Isn't stateless better?**
408
+
409
+ Stateless JWT trades security for scalability. That tradeoff made sense for internal microservices, but not for user-facing auth. SWT uses a minimal server-side footprint — one small session record per active user — which is manageable at any production scale. The security gains far outweigh the overhead.
410
+
411
+ ---
224
412
 
225
413
  **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.
414
+
415
+ JWT is fine for short-lived, low-sensitivity tokens between internal services where interception risk is low and logout/device binding don't matter. For any user-facing session, SWT is the better choice.
416
+
417
+ ---
227
418
 
228
419
  **Q: What Node.js version is required?**
229
- A: Node.js 16+ (uses native `crypto` module for AES-256-GCM).
420
+
421
+ Node.js `>=25.5.0`. SWT uses the native `crypto` module for AES-256-GCM — no external cryptography dependencies.
230
422
 
231
423
  ---
232
424
 
233
- ## Security Architecture
425
+ **Q: Does SWT prevent XSS attacks entirely?**
234
426
 
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
- │◄──────────────────────────────┤
261
- ```
427
+ SWT significantly reduces the impact of XSS. Because the session ID lives in an HttpOnly cookie, XSS cannot steal it via `document.cookie`. An attacker who steals only the bearer token still can't authenticate without the cookie — and even if they somehow get both, the device fingerprint check provides a third layer of validation.
262
428
 
263
429
  ---
264
430
 
@@ -266,27 +432,39 @@ Client Server
266
432
 
267
433
  - [x] AES-256-GCM payload encryption
268
434
  - [x] Device fingerprint binding
269
- - [x] Memory session store
435
+ - [x] In-memory session store
270
436
  - [x] Token expiry (`iat`, `exp`)
271
437
  - [ ] Redis session store adapter
272
- - [ ] Token rotation / refresh
273
- - [ ] TypeScript types (strict)
274
- - [ ] Express.js middleware helper
438
+ - [ ] Token rotation / silent refresh
439
+ - [ ] Strict TypeScript types
440
+ - [ ] Express.js middleware helper (`swtMiddleware()`)
275
441
  - [ ] Audit log support
442
+ - [ ] React hooks (`useSWT`)
276
443
 
277
444
  ---
278
445
 
279
446
  ## Contributing
280
447
 
281
- PRs and issues welcome. If you find a security vulnerability, please open a private security advisory rather than a public issue.
448
+ PRs and issues are welcome. For security vulnerabilities, please open a **private security advisory** on GitHub rather than a public issue.
449
+
450
+ ```bash
451
+ git clone https://github.com/MintuSingh07/node-securewebtoken.git
452
+ cd node-securewebtoken
453
+ npm install
454
+ npm run build
455
+ ```
282
456
 
283
457
  ---
284
458
 
285
459
  ## License
286
460
 
287
- MIT
461
+ [MIT](./LICENSE) © [MintuSingh07](https://github.com/MintuSingh07)
288
462
 
289
463
  ---
290
464
 
291
- > **Stop using JWT for sensitive user sessions. Your users deserve better.**
292
- > `npm install secure-web-token`
465
+ <p align="center">
466
+ <strong>Stop using JWT for sensitive user sessions.</strong><br/>
467
+ Your users deserve encrypted, device-bound, truly revocable auth.
468
+ <br/><br/>
469
+ <code>npm install secure-web-token</code>
470
+ </p>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "secure-web-token",
3
- "version": "1.2.10",
3
+ "version": "1.2.11",
4
4
  "description": "A secure web token utility",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",