secure-web-token 1.0.1 β 1.0.2
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 +49 -101
- package/dist/decrypt.d.ts +7 -0
- package/dist/decrypt.d.ts.map +1 -1
- package/dist/decrypt.js +7 -0
- package/dist/device.d.ts +8 -0
- package/dist/device.d.ts.map +1 -0
- package/dist/device.js +46 -0
- package/dist/encrypt.d.ts +8 -0
- package/dist/encrypt.d.ts.map +1 -1
- package/dist/encrypt.js +8 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/sign.d.ts +10 -15
- package/dist/sign.d.ts.map +1 -1
- package/dist/sign.js +30 -19
- package/dist/utils.d.ts +28 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +28 -0
- package/dist/verify.d.ts +9 -0
- package/dist/verify.d.ts.map +1 -1
- package/dist/verify.js +10 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
# π Secure Web Token (
|
|
1
|
+
# π Secure Web Token (SWT)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Secure Web Token (SWT)** is a lightweight Node.js authentication package designed to make token-based authentication **more secure than traditional JWTs** by binding tokens to **device fingerprints**.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Unlike JWTs, SWT:
|
|
6
|
+
- **Encrypts the payload** (canβt be decoded without the secret)
|
|
7
|
+
- Allows **device-level access control**
|
|
8
|
+
- Prevents token misuse across multiple devices
|
|
6
9
|
|
|
7
10
|
---
|
|
8
11
|
|
|
9
12
|
## β¨ Features
|
|
10
13
|
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
14
|
+
- π Encrypted payload (AES-256-GCM)
|
|
15
|
+
- π± Device fingerprint support (single or multiple)
|
|
16
|
+
- β± Token expiration
|
|
17
|
+
- β‘ Lightweight & fast
|
|
18
|
+
- π§ TypeScript + IntelliSense support
|
|
19
|
+
- β
CommonJS compatible
|
|
16
20
|
|
|
17
21
|
---
|
|
18
22
|
|
|
@@ -32,144 +36,88 @@ const { sign, verify } = require("secure-web-token");
|
|
|
32
36
|
|
|
33
37
|
---
|
|
34
38
|
|
|
35
|
-
## π§ What is Fingerprint?
|
|
39
|
+
## π§ What is a Fingerprint?
|
|
36
40
|
|
|
37
|
-
A fingerprint is any
|
|
41
|
+
A fingerprint is any identifier that represents a device.
|
|
42
|
+
|
|
43
|
+
Examples:
|
|
38
44
|
- Browser + OS (`Chrome-Linux`)
|
|
39
|
-
- Device
|
|
45
|
+
- Device ID
|
|
40
46
|
- IP address
|
|
41
|
-
-
|
|
47
|
+
- Any custom unique string
|
|
42
48
|
|
|
43
|
-
You can
|
|
49
|
+
You can allow **one or multiple fingerprints** per token.
|
|
44
50
|
|
|
45
51
|
---
|
|
46
52
|
|
|
47
53
|
## βοΈ sign()
|
|
48
54
|
|
|
49
|
-
Creates a secure token
|
|
55
|
+
Creates a secure encrypted token.
|
|
50
56
|
|
|
51
57
|
### Syntax
|
|
52
58
|
|
|
53
59
|
```js
|
|
54
|
-
sign(
|
|
60
|
+
sign(data, secret, options)
|
|
55
61
|
```
|
|
56
62
|
|
|
57
|
-
###
|
|
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) |
|
|
63
|
+
### Options
|
|
65
64
|
|
|
66
|
-
|
|
65
|
+
| Option | Type | Description |
|
|
66
|
+
|------|------|------------|
|
|
67
|
+
| expiresIn | number | Token expiry (seconds) |
|
|
68
|
+
| fingerprint | true \| string \| string[] | Device fingerprint(s) |
|
|
67
69
|
|
|
68
|
-
###
|
|
70
|
+
### Payload Structure
|
|
69
71
|
|
|
70
72
|
```js
|
|
71
|
-
|
|
72
|
-
{
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
console.log("TOKEN:", token);
|
|
73
|
+
{
|
|
74
|
+
data: { ... },
|
|
75
|
+
iat: number,
|
|
76
|
+
exp: number,
|
|
77
|
+
fp: string[]
|
|
78
|
+
}
|
|
81
79
|
```
|
|
82
80
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
### Example: Multiple Fingerprints
|
|
81
|
+
### Example (Auto Device ID)
|
|
86
82
|
|
|
87
83
|
```js
|
|
88
|
-
const token = sign(
|
|
89
|
-
{ userId: 1 },
|
|
84
|
+
const { token, deviceId } = sign(
|
|
85
|
+
{ userId: 1, role: "admin" },
|
|
90
86
|
"my-secret",
|
|
91
|
-
{
|
|
92
|
-
expiresIn: 60,
|
|
93
|
-
fingerprint: ["Chrome-Linux", "Windows"]
|
|
94
|
-
}
|
|
87
|
+
{ fingerprint: true }
|
|
95
88
|
);
|
|
89
|
+
|
|
90
|
+
console.log(token, deviceId);
|
|
96
91
|
```
|
|
97
92
|
|
|
98
93
|
---
|
|
99
94
|
|
|
100
95
|
## β
verify()
|
|
101
96
|
|
|
102
|
-
Verifies
|
|
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
|
|
97
|
+
Verifies token integrity, expiry, and fingerprint.
|
|
121
98
|
|
|
122
99
|
```js
|
|
123
|
-
|
|
124
|
-
fingerprint: "Chrome-Linux"
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
console.log("PAYLOAD:", payload);
|
|
100
|
+
verify(token, secret, { fingerprint })
|
|
128
101
|
```
|
|
129
102
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
### Example: Multiple Fingerprints Verification
|
|
103
|
+
### Example
|
|
133
104
|
|
|
134
105
|
```js
|
|
135
106
|
const payload = verify(token, "my-secret", {
|
|
136
|
-
fingerprint:
|
|
107
|
+
fingerprint: deviceId
|
|
137
108
|
});
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
---
|
|
141
|
-
|
|
142
|
-
## β Invalid Fingerprint Example
|
|
143
109
|
|
|
144
|
-
|
|
145
|
-
try {
|
|
146
|
-
verify(token, "my-secret", {
|
|
147
|
-
fingerprint: "Unknown-Device"
|
|
148
|
-
});
|
|
149
|
-
} catch (err) {
|
|
150
|
-
console.error(err.message);
|
|
151
|
-
}
|
|
110
|
+
console.log(payload.data);
|
|
152
111
|
```
|
|
153
112
|
|
|
154
113
|
---
|
|
155
114
|
|
|
156
|
-
##
|
|
115
|
+
## π Use Cases
|
|
157
116
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
```
|
|
117
|
+
- Prevent account sharing
|
|
118
|
+
- Device-restricted access
|
|
119
|
+
- Secure SaaS authentication
|
|
120
|
+
- Course/content protection
|
|
173
121
|
|
|
174
122
|
---
|
|
175
123
|
|
package/dist/decrypt.d.ts
CHANGED
|
@@ -1,2 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decrypts an encrypted SWT payload.
|
|
3
|
+
*
|
|
4
|
+
* @param encryptedPayload - Encrypted Base64URL payload
|
|
5
|
+
* @param secret - Secret key
|
|
6
|
+
* @returns Decrypted payload object
|
|
7
|
+
*/
|
|
1
8
|
export default function decrypt(encryptedPayload: string, secret: string): Record<string, any>;
|
|
2
9
|
//# sourceMappingURL=decrypt.d.ts.map
|
package/dist/decrypt.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decrypt.d.ts","sourceRoot":"","sources":["../src/decrypt.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,OAAO,UAAU,OAAO,
|
|
1
|
+
{"version":3,"file":"decrypt.d.ts","sourceRoot":"","sources":["../src/decrypt.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,UAAU,OAAO,CAC7B,gBAAgB,EAAE,MAAM,EACxB,MAAM,EAAE,MAAM,GACb,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAqBrB"}
|
package/dist/decrypt.js
CHANGED
|
@@ -36,6 +36,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.default = decrypt;
|
|
37
37
|
const crypto = __importStar(require("crypto"));
|
|
38
38
|
const utils_1 = require("./utils");
|
|
39
|
+
/**
|
|
40
|
+
* Decrypts an encrypted SWT payload.
|
|
41
|
+
*
|
|
42
|
+
* @param encryptedPayload - Encrypted Base64URL payload
|
|
43
|
+
* @param secret - Secret key
|
|
44
|
+
* @returns Decrypted payload object
|
|
45
|
+
*/
|
|
39
46
|
function decrypt(encryptedPayload, secret) {
|
|
40
47
|
const data = (0, utils_1.base64urlDecode)(encryptedPayload);
|
|
41
48
|
const iv = data.subarray(0, 12);
|
package/dist/device.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device.d.ts","sourceRoot":"","sources":["../src/device.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC"}
|
package/dist/device.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
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.generateDeviceId = generateDeviceId;
|
|
37
|
+
const crypto = __importStar(require("crypto"));
|
|
38
|
+
/**
|
|
39
|
+
* Generate a secure, random device ID.
|
|
40
|
+
* Used for Device Registration Model.
|
|
41
|
+
*
|
|
42
|
+
* @returns UUID v4 string
|
|
43
|
+
*/
|
|
44
|
+
function generateDeviceId() {
|
|
45
|
+
return crypto.randomUUID();
|
|
46
|
+
}
|
package/dist/encrypt.d.ts
CHANGED
|
@@ -1,2 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encrypts payload using AES-256-GCM.
|
|
3
|
+
* Payload is fully encrypted (unlike JWT).
|
|
4
|
+
*
|
|
5
|
+
* @param payload - Object to encrypt
|
|
6
|
+
* @param secret - Secret key
|
|
7
|
+
* @returns Encrypted Base64URL string
|
|
8
|
+
*/
|
|
1
9
|
export default function encrypt(payload: Record<string, any>, secret: string): string;
|
|
2
10
|
//# sourceMappingURL=encrypt.d.ts.map
|
package/dist/encrypt.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encrypt.d.ts","sourceRoot":"","sources":["../src/encrypt.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,OAAO,UAAU,OAAO,
|
|
1
|
+
{"version":3,"file":"encrypt.d.ts","sourceRoot":"","sources":["../src/encrypt.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,MAAM,CAAC,OAAO,UAAU,OAAO,CAC7B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC5B,MAAM,EAAE,MAAM,GACb,MAAM,CAkBR"}
|
package/dist/encrypt.js
CHANGED
|
@@ -36,6 +36,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.default = encrypt;
|
|
37
37
|
const crypto = __importStar(require("crypto"));
|
|
38
38
|
const utils_1 = require("./utils");
|
|
39
|
+
/**
|
|
40
|
+
* Encrypts payload using AES-256-GCM.
|
|
41
|
+
* Payload is fully encrypted (unlike JWT).
|
|
42
|
+
*
|
|
43
|
+
* @param payload - Object to encrypt
|
|
44
|
+
* @param secret - Secret key
|
|
45
|
+
* @returns Encrypted Base64URL string
|
|
46
|
+
*/
|
|
39
47
|
function encrypt(payload, secret) {
|
|
40
48
|
const iv = crypto.randomBytes(12);
|
|
41
49
|
const key = crypto
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,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
CHANGED
package/dist/sign.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Options for signing a
|
|
2
|
+
* Options for signing a Secure Web Token (SWT)
|
|
3
3
|
*/
|
|
4
4
|
export interface SignOptions {
|
|
5
5
|
/**
|
|
@@ -8,22 +8,17 @@ export interface SignOptions {
|
|
|
8
8
|
*/
|
|
9
9
|
expiresIn?: number;
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* true β auto-generate device ID
|
|
12
|
+
* string | string[] β custom fingerprints
|
|
13
13
|
*/
|
|
14
|
-
fingerprint?: string | string[];
|
|
14
|
+
fingerprint?: true | string | string[];
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
|
-
* Creates a
|
|
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 })
|
|
17
|
+
* Creates a Secure Web Token (SWT).
|
|
18
|
+
* User data is stored inside `payload.data`.
|
|
27
19
|
*/
|
|
28
|
-
export default function sign(
|
|
20
|
+
export default function sign(data: Record<string, any>, secret: string, options?: SignOptions): {
|
|
21
|
+
token: string;
|
|
22
|
+
deviceId?: string;
|
|
23
|
+
};
|
|
29
24
|
//# sourceMappingURL=sign.d.ts.map
|
package/dist/sign.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sign.d.ts","sourceRoot":"","sources":["../src/sign.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sign.d.ts","sourceRoot":"","sources":["../src/sign.ts"],"names":[],"mappings":"AAKA;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,WAAW,CAAC,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,EAAE,CAAC;CACxC;AAED;;;GAGG;AACH,MAAM,CAAC,OAAO,UAAU,IAAI,CAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,WAAgB,GACxB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,CAgDtC"}
|
package/dist/sign.js
CHANGED
|
@@ -40,31 +40,39 @@ exports.default = sign;
|
|
|
40
40
|
const crypto = __importStar(require("crypto"));
|
|
41
41
|
const encrypt_1 = __importDefault(require("./encrypt"));
|
|
42
42
|
const utils_1 = require("./utils");
|
|
43
|
+
const device_1 = require("./device");
|
|
43
44
|
/**
|
|
44
|
-
* Creates a
|
|
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 })
|
|
45
|
+
* Creates a Secure Web Token (SWT).
|
|
46
|
+
* User data is stored inside `payload.data`.
|
|
54
47
|
*/
|
|
55
|
-
function sign(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
48
|
+
function sign(data, secret, options = {}) {
|
|
49
|
+
if (!secret || typeof secret !== "string") {
|
|
50
|
+
throw new Error("Secret must be a non-empty string");
|
|
51
|
+
}
|
|
52
|
+
if (!data || typeof data !== "object") {
|
|
53
|
+
throw new Error("Data must be an object");
|
|
54
|
+
}
|
|
60
55
|
const now = Math.floor(Date.now() / 1000);
|
|
61
|
-
payload
|
|
62
|
-
|
|
63
|
-
|
|
56
|
+
const payload = {
|
|
57
|
+
data, // π user data lives here
|
|
58
|
+
iat: now,
|
|
59
|
+
exp: now + (options.expiresIn ?? 900),
|
|
60
|
+
};
|
|
61
|
+
let deviceId;
|
|
62
|
+
// π Device Registration Model
|
|
63
|
+
if (options.fingerprint === true) {
|
|
64
|
+
deviceId = (0, device_1.generateDeviceId)();
|
|
65
|
+
payload.fp = [deviceId];
|
|
66
|
+
}
|
|
67
|
+
else if (options.fingerprint) {
|
|
64
68
|
payload.fp = Array.isArray(options.fingerprint)
|
|
65
69
|
? options.fingerprint
|
|
66
70
|
: [options.fingerprint];
|
|
67
71
|
}
|
|
72
|
+
const header = {
|
|
73
|
+
alg: "AES-256-GCM+HMAC",
|
|
74
|
+
typ: "SWT",
|
|
75
|
+
};
|
|
68
76
|
const encodedHeader = (0, utils_1.base64urlEncode)(JSON.stringify(header));
|
|
69
77
|
const encryptedPayload = (0, encrypt_1.default)(payload, secret);
|
|
70
78
|
const dataToSign = `${encodedHeader}.${encryptedPayload}`;
|
|
@@ -72,5 +80,8 @@ function sign(payload, secret, options = {}) {
|
|
|
72
80
|
.createHmac("sha256", secret)
|
|
73
81
|
.update(dataToSign)
|
|
74
82
|
.digest("base64url");
|
|
75
|
-
return
|
|
83
|
+
return {
|
|
84
|
+
token: `${dataToSign}.${signature}`,
|
|
85
|
+
deviceId,
|
|
86
|
+
};
|
|
76
87
|
}
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encode data into Base64URL format.
|
|
3
|
+
* Used to make tokens URL-safe.
|
|
4
|
+
*
|
|
5
|
+
* @param input - Buffer or string to encode
|
|
6
|
+
* @returns Base64URL encoded string
|
|
7
|
+
*/
|
|
1
8
|
export declare function base64urlEncode(input: Buffer | string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Decode Base64URL encoded data back into Buffer.
|
|
11
|
+
*
|
|
12
|
+
* @param input - Base64URL string
|
|
13
|
+
* @returns Decoded Buffer
|
|
14
|
+
*/
|
|
2
15
|
export declare function base64urlDecode(input: string): Buffer;
|
|
16
|
+
/**
|
|
17
|
+
* Create a SHA-256 hash.
|
|
18
|
+
* Useful for hashing fingerprints or secrets.
|
|
19
|
+
*
|
|
20
|
+
* @param data - String or Buffer to hash
|
|
21
|
+
* @returns SHA-256 hash Buffer
|
|
22
|
+
*/
|
|
3
23
|
export declare function hash(data: string | Buffer): Buffer;
|
|
24
|
+
/**
|
|
25
|
+
* Compare two buffers in constant time.
|
|
26
|
+
* Prevents timing attacks.
|
|
27
|
+
*
|
|
28
|
+
* @param a - First buffer
|
|
29
|
+
* @param b - Second buffer
|
|
30
|
+
* @returns True if equal, false otherwise
|
|
31
|
+
*/
|
|
4
32
|
export declare function timingSafeEqual(a: Buffer, b: Buffer): boolean;
|
|
5
33
|
//# sourceMappingURL=utils.d.ts.map
|
package/dist/utils.d.ts.map
CHANGED
|
@@ -1 +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"}
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAE9D;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;;;;;GAMG;AACH,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAElD;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAG7D"}
|
package/dist/utils.js
CHANGED
|
@@ -38,15 +38,43 @@ exports.base64urlDecode = base64urlDecode;
|
|
|
38
38
|
exports.hash = hash;
|
|
39
39
|
exports.timingSafeEqual = timingSafeEqual;
|
|
40
40
|
const crypto = __importStar(require("crypto"));
|
|
41
|
+
/**
|
|
42
|
+
* Encode data into Base64URL format.
|
|
43
|
+
* Used to make tokens URL-safe.
|
|
44
|
+
*
|
|
45
|
+
* @param input - Buffer or string to encode
|
|
46
|
+
* @returns Base64URL encoded string
|
|
47
|
+
*/
|
|
41
48
|
function base64urlEncode(input) {
|
|
42
49
|
return Buffer.from(input).toString("base64url");
|
|
43
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Decode Base64URL encoded data back into Buffer.
|
|
53
|
+
*
|
|
54
|
+
* @param input - Base64URL string
|
|
55
|
+
* @returns Decoded Buffer
|
|
56
|
+
*/
|
|
44
57
|
function base64urlDecode(input) {
|
|
45
58
|
return Buffer.from(input, "base64url");
|
|
46
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Create a SHA-256 hash.
|
|
62
|
+
* Useful for hashing fingerprints or secrets.
|
|
63
|
+
*
|
|
64
|
+
* @param data - String or Buffer to hash
|
|
65
|
+
* @returns SHA-256 hash Buffer
|
|
66
|
+
*/
|
|
47
67
|
function hash(data) {
|
|
48
68
|
return crypto.createHash("sha256").update(data).digest();
|
|
49
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Compare two buffers in constant time.
|
|
72
|
+
* Prevents timing attacks.
|
|
73
|
+
*
|
|
74
|
+
* @param a - First buffer
|
|
75
|
+
* @param b - Second buffer
|
|
76
|
+
* @returns True if equal, false otherwise
|
|
77
|
+
*/
|
|
50
78
|
function timingSafeEqual(a, b) {
|
|
51
79
|
if (a.length !== b.length)
|
|
52
80
|
return false;
|
package/dist/verify.d.ts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Options for verifying a Secure Web Token
|
|
3
|
+
*/
|
|
1
4
|
export interface VerifyOptions {
|
|
5
|
+
/**
|
|
6
|
+
* Device fingerprint(s) allowed to verify token
|
|
7
|
+
*/
|
|
2
8
|
fingerprint?: string | string[];
|
|
3
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* Verifies and decrypts a Secure Web Token.
|
|
12
|
+
*/
|
|
4
13
|
export default function verify(token: string, secret: string, options?: VerifyOptions): Record<string, any>;
|
|
5
14
|
//# sourceMappingURL=verify.d.ts.map
|
package/dist/verify.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,aAAa;
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CACjC;AAED;;GAEG;AACH,MAAM,CAAC,OAAO,UAAU,MAAM,CAC5B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,aAAkB,GAC1B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAuDrB"}
|
package/dist/verify.js
CHANGED
|
@@ -40,7 +40,13 @@ exports.default = verify;
|
|
|
40
40
|
const crypto = __importStar(require("crypto"));
|
|
41
41
|
const decrypt_1 = __importDefault(require("./decrypt"));
|
|
42
42
|
const utils_1 = require("./utils");
|
|
43
|
+
/**
|
|
44
|
+
* Verifies and decrypts a Secure Web Token.
|
|
45
|
+
*/
|
|
43
46
|
function verify(token, secret, options = {}) {
|
|
47
|
+
if (!token || typeof token !== "string") {
|
|
48
|
+
throw new Error("Token must be a string");
|
|
49
|
+
}
|
|
44
50
|
const parts = token.split(".");
|
|
45
51
|
if (parts.length !== 3) {
|
|
46
52
|
throw new Error("Invalid token format");
|
|
@@ -59,6 +65,9 @@ function verify(token, secret, options = {}) {
|
|
|
59
65
|
if (payload.exp < now) {
|
|
60
66
|
throw new Error("Token expired");
|
|
61
67
|
}
|
|
68
|
+
if (!payload.data || typeof payload.data !== "object") {
|
|
69
|
+
throw new Error("Invalid payload structure");
|
|
70
|
+
}
|
|
62
71
|
if (options.fingerprint) {
|
|
63
72
|
const provided = Array.isArray(options.fingerprint)
|
|
64
73
|
? options.fingerprint
|
|
@@ -71,5 +80,5 @@ function verify(token, secret, options = {}) {
|
|
|
71
80
|
throw new Error("Fingerprint mismatch");
|
|
72
81
|
}
|
|
73
82
|
}
|
|
74
|
-
return payload;
|
|
83
|
+
return payload; // { data, iat, exp, fp }
|
|
75
84
|
}
|