secure-web-token 1.0.0
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.
- package/README.md +178 -0
- package/dist/decrypt.d.ts +2 -0
- package/dist/decrypt.d.ts.map +1 -0
- package/dist/decrypt.js +55 -0
- package/dist/encrypt.d.ts +2 -0
- package/dist/encrypt.d.ts.map +1 -0
- package/dist/encrypt.js +52 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/sign.d.ts +29 -0
- package/dist/sign.d.ts.map +1 -0
- package/dist/sign.js +76 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +54 -0
- package/dist/verify.d.ts +5 -0
- package/dist/verify.d.ts.map +1 -0
- package/dist/verify.js +75 -0
- package/package.json +26 -0
package/README.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# 🔐 Secure Web Token (Fingerprint Based)
|
|
2
|
+
|
|
3
|
+
A lightweight Node.js authentication package that allows you to **sign and verify tokens using one or more device fingerprints**.
|
|
4
|
+
|
|
5
|
+
This helps prevent token misuse across different devices.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## ✨ Features
|
|
10
|
+
|
|
11
|
+
- Supports **single or multiple fingerprints**
|
|
12
|
+
- Simple `sign()` and `verify()` API
|
|
13
|
+
- CommonJS compatible
|
|
14
|
+
- Editor IntelliSense support
|
|
15
|
+
- No heavy dependencies
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 📦 Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install secure-web-token
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 📥 Import
|
|
28
|
+
|
|
29
|
+
```js
|
|
30
|
+
const { sign, verify } = require("secure-web-token");
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 🧠 What is Fingerprint?
|
|
36
|
+
|
|
37
|
+
A fingerprint is any unique identifier of a device, for example:
|
|
38
|
+
- Browser + OS (`Chrome-Linux`)
|
|
39
|
+
- Device name
|
|
40
|
+
- IP address
|
|
41
|
+
- Custom string
|
|
42
|
+
|
|
43
|
+
You can pass **one or multiple fingerprints**.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## ✍️ sign()
|
|
48
|
+
|
|
49
|
+
Creates a secure token with fingerprint validation.
|
|
50
|
+
|
|
51
|
+
### Syntax
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
sign(payload, secret, options)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Parameters
|
|
58
|
+
|
|
59
|
+
| Parameter | Type | Description |
|
|
60
|
+
|---------|-----|-------------|
|
|
61
|
+
| payload | Object | Data you want inside token |
|
|
62
|
+
| secret | String | Secret key |
|
|
63
|
+
| options.expiresIn | Number | Expiry time (seconds) |
|
|
64
|
+
| options.fingerprint | String \| String[] | Allowed fingerprint(s) |
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
### Example: Single Fingerprint
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
const token = sign(
|
|
72
|
+
{ userId: 1 },
|
|
73
|
+
"my-secret",
|
|
74
|
+
{
|
|
75
|
+
expiresIn: 60,
|
|
76
|
+
fingerprint: "Chrome-Linux"
|
|
77
|
+
}
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
console.log("TOKEN:", token);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
### Example: Multiple Fingerprints
|
|
86
|
+
|
|
87
|
+
```js
|
|
88
|
+
const token = sign(
|
|
89
|
+
{ userId: 1 },
|
|
90
|
+
"my-secret",
|
|
91
|
+
{
|
|
92
|
+
expiresIn: 60,
|
|
93
|
+
fingerprint: ["Chrome-Linux", "monjal"]
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## ✅ verify()
|
|
101
|
+
|
|
102
|
+
Verifies the token and checks whether the fingerprint is valid.
|
|
103
|
+
|
|
104
|
+
### Syntax
|
|
105
|
+
|
|
106
|
+
```js
|
|
107
|
+
verify(token, secret, options)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Parameters
|
|
111
|
+
|
|
112
|
+
| Parameter | Type | Description |
|
|
113
|
+
|---------|-----|-------------|
|
|
114
|
+
| token | String | Token to verify |
|
|
115
|
+
| secret | String | Same secret used while signing |
|
|
116
|
+
| options.fingerprint | String \| String[] | Current device fingerprint |
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
### Example: Successful Verification
|
|
121
|
+
|
|
122
|
+
```js
|
|
123
|
+
const payload = verify(token, "my-secret", {
|
|
124
|
+
fingerprint: "Chrome-Linux"
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
console.log("PAYLOAD:", payload);
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
### Example: Multiple Fingerprints Verification
|
|
133
|
+
|
|
134
|
+
```js
|
|
135
|
+
const payload = verify(token, "my-secret", {
|
|
136
|
+
fingerprint: ["Chrome-Linux", "monjal"]
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## ❌ Invalid Fingerprint Example
|
|
143
|
+
|
|
144
|
+
```js
|
|
145
|
+
try {
|
|
146
|
+
verify(token, "my-secret", {
|
|
147
|
+
fingerprint: "Unknown-Device"
|
|
148
|
+
});
|
|
149
|
+
} catch (err) {
|
|
150
|
+
console.error(err.message);
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## ⏰ Token Expiry Example
|
|
157
|
+
|
|
158
|
+
```js
|
|
159
|
+
const token = sign(
|
|
160
|
+
{ role: "admin" },
|
|
161
|
+
"secret-key",
|
|
162
|
+
{
|
|
163
|
+
expiresIn: 5,
|
|
164
|
+
fingerprint: "Chrome-Linux"
|
|
165
|
+
}
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
// After 5 seconds
|
|
169
|
+
verify(token, "secret-key", {
|
|
170
|
+
fingerprint: "Chrome-Linux"
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## 📜 License
|
|
177
|
+
|
|
178
|
+
MIT License
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decrypt.d.ts","sourceRoot":"","sources":["../src/decrypt.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,OAAO,UAAU,OAAO,CAC3B,gBAAgB,EAAE,MAAM,EACxB,MAAM,EAAE,MAAM,GACf,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CA0BrB"}
|
package/dist/decrypt.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.default = decrypt;
|
|
37
|
+
const crypto = __importStar(require("crypto"));
|
|
38
|
+
const utils_1 = require("./utils");
|
|
39
|
+
function decrypt(encryptedPayload, secret) {
|
|
40
|
+
const data = (0, utils_1.base64urlDecode)(encryptedPayload);
|
|
41
|
+
const iv = data.subarray(0, 12);
|
|
42
|
+
const tag = data.subarray(12, 28);
|
|
43
|
+
const text = data.subarray(28);
|
|
44
|
+
const key = crypto
|
|
45
|
+
.createHash("sha256")
|
|
46
|
+
.update(secret)
|
|
47
|
+
.digest();
|
|
48
|
+
const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
|
|
49
|
+
decipher.setAuthTag(tag);
|
|
50
|
+
const decrypted = Buffer.concat([
|
|
51
|
+
decipher.update(text),
|
|
52
|
+
decipher.final(),
|
|
53
|
+
]);
|
|
54
|
+
return JSON.parse(decrypted.toString("utf8"));
|
|
55
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encrypt.d.ts","sourceRoot":"","sources":["../src/encrypt.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,OAAO,UAAU,OAAO,CAC3B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC5B,MAAM,EAAE,MAAM,GACf,MAAM,CAoBR"}
|
package/dist/encrypt.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.default = encrypt;
|
|
37
|
+
const crypto = __importStar(require("crypto"));
|
|
38
|
+
const utils_1 = require("./utils");
|
|
39
|
+
function encrypt(payload, secret) {
|
|
40
|
+
const iv = crypto.randomBytes(12);
|
|
41
|
+
const key = crypto
|
|
42
|
+
.createHash("sha256")
|
|
43
|
+
.update(secret)
|
|
44
|
+
.digest();
|
|
45
|
+
const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
|
|
46
|
+
const encrypted = Buffer.concat([
|
|
47
|
+
cipher.update(JSON.stringify(payload), "utf8"),
|
|
48
|
+
cipher.final(),
|
|
49
|
+
]);
|
|
50
|
+
const tag = cipher.getAuthTag();
|
|
51
|
+
return (0, utils_1.base64urlEncode)(Buffer.concat([iv, tag, encrypted]));
|
|
52
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,OAAO,IAAI,MAAM,EAAE,MAAM,UAAU,CAAC;AAE7C,YAAY,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAC1C,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.verify = exports.sign = void 0;
|
|
7
|
+
var sign_1 = require("./sign");
|
|
8
|
+
Object.defineProperty(exports, "sign", { enumerable: true, get: function () { return __importDefault(sign_1).default; } });
|
|
9
|
+
var verify_1 = require("./verify");
|
|
10
|
+
Object.defineProperty(exports, "verify", { enumerable: true, get: function () { return __importDefault(verify_1).default; } });
|
package/dist/sign.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for signing a secure token
|
|
3
|
+
*/
|
|
4
|
+
export interface SignOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Token expiry time in seconds
|
|
7
|
+
* @default 900 (15 minutes)
|
|
8
|
+
*/
|
|
9
|
+
expiresIn?: number;
|
|
10
|
+
/**
|
|
11
|
+
* One or more fingerprints allowed to use this token
|
|
12
|
+
* Example: device hash, IP hash, browser hash
|
|
13
|
+
*/
|
|
14
|
+
fingerprint?: string | string[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Creates a secure encrypted token.
|
|
18
|
+
*
|
|
19
|
+
* @param payload - Data you want to store inside the token
|
|
20
|
+
* @param secret - Secret key used for encryption & signature
|
|
21
|
+
* @param options - Optional token settings (expiry, fingerprint)
|
|
22
|
+
*
|
|
23
|
+
* @returns Encrypted and signed token string
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* sign({ userId: 1 }, "secret", { expiresIn: 60 })
|
|
27
|
+
*/
|
|
28
|
+
export default function sign(payload: Record<string, any>, secret: string, options?: SignOptions): string;
|
|
29
|
+
//# sourceMappingURL=sign.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sign.d.ts","sourceRoot":"","sources":["../src/sign.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CACjC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,OAAO,UAAU,IAAI,CAC1B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC5B,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,WAAgB,GACxB,MAAM,CA0BR"}
|
package/dist/sign.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.default = sign;
|
|
40
|
+
const crypto = __importStar(require("crypto"));
|
|
41
|
+
const encrypt_1 = __importDefault(require("./encrypt"));
|
|
42
|
+
const utils_1 = require("./utils");
|
|
43
|
+
/**
|
|
44
|
+
* Creates a secure encrypted token.
|
|
45
|
+
*
|
|
46
|
+
* @param payload - Data you want to store inside the token
|
|
47
|
+
* @param secret - Secret key used for encryption & signature
|
|
48
|
+
* @param options - Optional token settings (expiry, fingerprint)
|
|
49
|
+
*
|
|
50
|
+
* @returns Encrypted and signed token string
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* sign({ userId: 1 }, "secret", { expiresIn: 60 })
|
|
54
|
+
*/
|
|
55
|
+
function sign(payload, secret, options = {}) {
|
|
56
|
+
const header = {
|
|
57
|
+
alg: "AES-256-GCM+HMAC",
|
|
58
|
+
typ: "STK",
|
|
59
|
+
};
|
|
60
|
+
const now = Math.floor(Date.now() / 1000);
|
|
61
|
+
payload.iat = now;
|
|
62
|
+
payload.exp = now + (options.expiresIn ?? 900);
|
|
63
|
+
if (options.fingerprint) {
|
|
64
|
+
payload.fp = Array.isArray(options.fingerprint)
|
|
65
|
+
? options.fingerprint
|
|
66
|
+
: [options.fingerprint];
|
|
67
|
+
}
|
|
68
|
+
const encodedHeader = (0, utils_1.base64urlEncode)(JSON.stringify(header));
|
|
69
|
+
const encryptedPayload = (0, encrypt_1.default)(payload, secret);
|
|
70
|
+
const dataToSign = `${encodedHeader}.${encryptedPayload}`;
|
|
71
|
+
const signature = crypto
|
|
72
|
+
.createHmac("sha256", secret)
|
|
73
|
+
.update(dataToSign)
|
|
74
|
+
.digest("base64url");
|
|
75
|
+
return `${dataToSign}.${signature}`;
|
|
76
|
+
}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare function base64urlEncode(input: Buffer | string): string;
|
|
2
|
+
export declare function base64urlDecode(input: string): Buffer;
|
|
3
|
+
export declare function hash(data: string | Buffer): Buffer;
|
|
4
|
+
export declare function timingSafeEqual(a: Buffer, b: Buffer): boolean;
|
|
5
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAE9D;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAG7D"}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.base64urlEncode = base64urlEncode;
|
|
37
|
+
exports.base64urlDecode = base64urlDecode;
|
|
38
|
+
exports.hash = hash;
|
|
39
|
+
exports.timingSafeEqual = timingSafeEqual;
|
|
40
|
+
const crypto = __importStar(require("crypto"));
|
|
41
|
+
function base64urlEncode(input) {
|
|
42
|
+
return Buffer.from(input).toString("base64url");
|
|
43
|
+
}
|
|
44
|
+
function base64urlDecode(input) {
|
|
45
|
+
return Buffer.from(input, "base64url");
|
|
46
|
+
}
|
|
47
|
+
function hash(data) {
|
|
48
|
+
return crypto.createHash("sha256").update(data).digest();
|
|
49
|
+
}
|
|
50
|
+
function timingSafeEqual(a, b) {
|
|
51
|
+
if (a.length !== b.length)
|
|
52
|
+
return false;
|
|
53
|
+
return crypto.timingSafeEqual(a, b);
|
|
54
|
+
}
|
package/dist/verify.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,aAAa;IAC1B,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CACnC;AAED,MAAM,CAAC,OAAO,UAAU,MAAM,CAC1B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,aAAkB,GAC5B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAiDrB"}
|
package/dist/verify.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.default = verify;
|
|
40
|
+
const crypto = __importStar(require("crypto"));
|
|
41
|
+
const decrypt_1 = __importDefault(require("./decrypt"));
|
|
42
|
+
const utils_1 = require("./utils");
|
|
43
|
+
function verify(token, secret, options = {}) {
|
|
44
|
+
const parts = token.split(".");
|
|
45
|
+
if (parts.length !== 3) {
|
|
46
|
+
throw new Error("Invalid token format");
|
|
47
|
+
}
|
|
48
|
+
const [header, encryptedPayload, signature] = parts;
|
|
49
|
+
const dataToVerify = `${header}.${encryptedPayload}`;
|
|
50
|
+
const expectedSignature = crypto
|
|
51
|
+
.createHmac("sha256", secret)
|
|
52
|
+
.update(dataToVerify)
|
|
53
|
+
.digest("base64url");
|
|
54
|
+
if (!(0, utils_1.timingSafeEqual)(Buffer.from(signature), Buffer.from(expectedSignature))) {
|
|
55
|
+
throw new Error("Invalid signature");
|
|
56
|
+
}
|
|
57
|
+
const payload = (0, decrypt_1.default)(encryptedPayload, secret);
|
|
58
|
+
const now = Math.floor(Date.now() / 1000);
|
|
59
|
+
if (payload.exp < now) {
|
|
60
|
+
throw new Error("Token expired");
|
|
61
|
+
}
|
|
62
|
+
if (options.fingerprint) {
|
|
63
|
+
const provided = Array.isArray(options.fingerprint)
|
|
64
|
+
? options.fingerprint
|
|
65
|
+
: [options.fingerprint];
|
|
66
|
+
const stored = Array.isArray(payload.fp)
|
|
67
|
+
? payload.fp
|
|
68
|
+
: [];
|
|
69
|
+
const matched = provided.some(fp => stored.includes(fp));
|
|
70
|
+
if (!matched) {
|
|
71
|
+
throw new Error("Fingerprint mismatch");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return payload;
|
|
75
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "secure-web-token",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A secure web token utility",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"prepublishOnly": "npm run build",
|
|
10
|
+
"test": "node test.js"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"token",
|
|
14
|
+
"security"
|
|
15
|
+
],
|
|
16
|
+
"author": "Znos",
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"files": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"type": "commonjs",
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/node": "^25.0.6",
|
|
24
|
+
"typescript": "^5.9.3"
|
|
25
|
+
}
|
|
26
|
+
}
|