sprint-es 0.0.37 ā 0.0.38
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/dist/cjs/cli.cjs
CHANGED
|
@@ -4,6 +4,24 @@ const child_process = require("child_process");
|
|
|
4
4
|
const fs = require("fs");
|
|
5
5
|
const crypto = require("crypto");
|
|
6
6
|
const path = require("path");
|
|
7
|
+
const dotenv = require("dotenv");
|
|
8
|
+
function _interopNamespaceDefault(e) {
|
|
9
|
+
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
10
|
+
if (e) {
|
|
11
|
+
for (const k in e) {
|
|
12
|
+
if (k !== "default") {
|
|
13
|
+
const d = Object.getOwnPropertyDescriptor(e, k);
|
|
14
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
15
|
+
enumerable: true,
|
|
16
|
+
get: () => e[k]
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
n.default = e;
|
|
22
|
+
return Object.freeze(n);
|
|
23
|
+
}
|
|
24
|
+
const crypto__namespace = /* @__PURE__ */ _interopNamespaceDefault(crypto);
|
|
7
25
|
const args = process.argv.slice(2);
|
|
8
26
|
const command = args[0];
|
|
9
27
|
if (!command) {
|
|
@@ -38,6 +56,23 @@ function getProjectRoot() {
|
|
|
38
56
|
}
|
|
39
57
|
return process.cwd();
|
|
40
58
|
}
|
|
59
|
+
function loadEnv() {
|
|
60
|
+
const projectRoot2 = getProjectRoot();
|
|
61
|
+
const envFile = fs.existsSync(path.join(projectRoot2, ".env")) ? ".env" : fs.existsSync(path.join(projectRoot2, ".env.development")) ? ".env.development" : fs.existsSync(path.join(projectRoot2, ".env.production")) ? ".env.production" : null;
|
|
62
|
+
if (envFile) {
|
|
63
|
+
dotenv.config({ path: path.join(projectRoot2, envFile) });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function checkJwtKeys() {
|
|
67
|
+
loadEnv();
|
|
68
|
+
const publicKey = process.env.JWT_PUBLIC_KEY;
|
|
69
|
+
const privateKey = process.env.JWT_PRIVATE_KEY;
|
|
70
|
+
if (!publicKey || !privateKey) {
|
|
71
|
+
console.error("\nā Error: JWT keys not configured.");
|
|
72
|
+
console.error("Run 'npm run generate:keys' to generate new keys.\n");
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
41
76
|
const projectRoot = getProjectRoot();
|
|
42
77
|
function runCommand(cmd, envVars) {
|
|
43
78
|
const child = child_process.spawn(cmd, args.slice(1), {
|
|
@@ -52,6 +87,7 @@ function runCommand(cmd, envVars) {
|
|
|
52
87
|
}
|
|
53
88
|
switch (command) {
|
|
54
89
|
case "dev":
|
|
90
|
+
checkJwtKeys();
|
|
55
91
|
console.log("š Starting development server with hot reload...");
|
|
56
92
|
const srcFile = fs.existsSync(path.join(projectRoot, "src/app.ts")) ? "src/app.ts" : fs.existsSync(path.join(projectRoot, "src/app.js")) ? "src/app.js" : fs.existsSync(path.join(projectRoot, "src/index.ts")) ? "src/index.ts" : "src/index.js";
|
|
57
93
|
runCommand(`tsx --watch ${srcFile}`, { NODE_ENV: "development" });
|
|
@@ -66,14 +102,20 @@ switch (command) {
|
|
|
66
102
|
}
|
|
67
103
|
break;
|
|
68
104
|
case "start":
|
|
105
|
+
checkJwtKeys();
|
|
69
106
|
console.log("š Starting production server...");
|
|
70
107
|
runCommand("node dist/index.js", { NODE_ENV: "production" });
|
|
71
108
|
break;
|
|
72
109
|
case "generate-keys":
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
110
|
+
const { publicKey, privateKey } = crypto__namespace.generateKeyPairSync("ec", {
|
|
111
|
+
namedCurve: "prime256v1",
|
|
112
|
+
publicKeyEncoding: { type: "spki", format: "pem" },
|
|
113
|
+
privateKeyEncoding: { type: "pkcs8", format: "pem" }
|
|
114
|
+
});
|
|
115
|
+
console.log("\nš Generating JWT keys...\n");
|
|
116
|
+
console.log("JWT_PUBLIC_KEY=" + publicKey.replace(/\n/g, "\\n"));
|
|
117
|
+
console.log("\nJWT_PRIVATE_KEY=" + privateKey.replace(/\n/g, "\\n"));
|
|
118
|
+
console.log("\nš Add these to your .env file:\n");
|
|
77
119
|
process.exit(0);
|
|
78
120
|
break;
|
|
79
121
|
default:
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const crypto = require("crypto");
|
|
4
|
-
const ALGORITHM = "
|
|
5
|
-
const ENC_ALGORITHM = "aes-256-gcm";
|
|
6
|
-
const KEY_LENGTH = 32;
|
|
7
|
-
const IV_LENGTH = 12;
|
|
4
|
+
const ALGORITHM = "ES256";
|
|
8
5
|
function base64UrlEncode(buffer) {
|
|
9
6
|
return buffer.toString("base64url");
|
|
10
7
|
}
|
|
11
|
-
function
|
|
8
|
+
function base64UrlDecode(str) {
|
|
12
9
|
const pad = str.length % 4;
|
|
13
10
|
const padded = pad ? str + "=".repeat(4 - pad) : str;
|
|
14
11
|
return Buffer.from(padded.replace(/-/g, "+").replace(/_/g, "/"), "base64");
|
|
@@ -16,20 +13,22 @@ function base64UrlDecodeSafe(str) {
|
|
|
16
13
|
function decodeBase64Url(str) {
|
|
17
14
|
return Buffer.from(str, "base64url").toString();
|
|
18
15
|
}
|
|
19
|
-
function
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
16
|
+
function generateKeyPair(algorithm = ALGORITHM) {
|
|
17
|
+
const { publicKey, privateKey } = crypto.generateKeyPairSync("ec", {
|
|
18
|
+
namedCurve: "prime256v1",
|
|
19
|
+
publicKeyEncoding: {
|
|
20
|
+
type: "spki",
|
|
21
|
+
format: "pem"
|
|
22
|
+
},
|
|
23
|
+
privateKeyEncoding: {
|
|
24
|
+
type: "pkcs8",
|
|
25
|
+
format: "pem"
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
return { publicKey, privateKey };
|
|
30
29
|
}
|
|
31
|
-
function sign(payload,
|
|
32
|
-
const header = { alg:
|
|
30
|
+
function sign(payload, privateKey, options = {}) {
|
|
31
|
+
const header = { alg: ALGORITHM, typ: "JWT" };
|
|
33
32
|
const now = Math.floor(Date.now() / 1e3);
|
|
34
33
|
const claims = { ...payload };
|
|
35
34
|
if (options.expiresIn) {
|
|
@@ -39,19 +38,22 @@ function sign(payload, secret, options = {}) {
|
|
|
39
38
|
if (options.subject) claims.sub = options.subject;
|
|
40
39
|
if (options.audience) claims.aud = options.audience;
|
|
41
40
|
claims.iat = now;
|
|
42
|
-
const encodedHeader =
|
|
43
|
-
const encodedPayload =
|
|
44
|
-
const
|
|
41
|
+
const encodedHeader = base64UrlEncode(Buffer.from(JSON.stringify(header)));
|
|
42
|
+
const encodedPayload = base64UrlEncode(Buffer.from(JSON.stringify(claims)));
|
|
43
|
+
const sign2 = crypto.createSign("SHA256");
|
|
44
|
+
sign2.update(`${encodedHeader}.${encodedPayload}`);
|
|
45
|
+
const signature = sign2.sign(privateKey);
|
|
45
46
|
return `${encodedHeader}.${encodedPayload}.${base64UrlEncode(signature)}`;
|
|
46
47
|
}
|
|
47
|
-
function verify(token,
|
|
48
|
+
function verify(token, publicKey) {
|
|
48
49
|
try {
|
|
49
50
|
const parts = token.split(".");
|
|
50
51
|
if (parts.length !== 3) return null;
|
|
51
52
|
const [encodedHeader, encodedPayload, signature] = parts;
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
const verify2 = crypto.createVerify("SHA256");
|
|
54
|
+
verify2.update(`${encodedHeader}.${encodedPayload}`);
|
|
55
|
+
const isValid = verify2.verify(publicKey, base64UrlDecode(signature));
|
|
56
|
+
if (!isValid) return null;
|
|
55
57
|
const payload = JSON.parse(decodeBase64Url(encodedPayload));
|
|
56
58
|
if (payload.exp && payload.exp < Math.floor(Date.now() / 1e3)) return null;
|
|
57
59
|
return payload;
|
|
@@ -59,78 +61,36 @@ function verify(token, secret) {
|
|
|
59
61
|
return null;
|
|
60
62
|
}
|
|
61
63
|
}
|
|
62
|
-
function
|
|
63
|
-
|
|
64
|
-
const key = importKey(secret);
|
|
65
|
-
const header = {
|
|
66
|
-
alg: ALGORITHM,
|
|
67
|
-
enc: ENC_ALGORITHM,
|
|
68
|
-
typ: "JWT"
|
|
69
|
-
};
|
|
70
|
-
const claims = { ...payload };
|
|
71
|
-
if (options.expiresIn) {
|
|
72
|
-
claims.exp = now + parseInt(String(options.expiresIn));
|
|
73
|
-
}
|
|
74
|
-
if (options.issuer) claims.iss = options.issuer;
|
|
75
|
-
claims.iat = now;
|
|
76
|
-
const iv = crypto.randomBytes(IV_LENGTH);
|
|
77
|
-
const aad = Buffer.from(JSON.stringify(header));
|
|
78
|
-
const cipher = crypto.createCipheriv(ENC_ALGORITHM, key, iv);
|
|
79
|
-
cipher.setAAD(aad);
|
|
80
|
-
const plaintext = Buffer.from(JSON.stringify(claims));
|
|
81
|
-
const encrypted = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
82
|
-
const authTag = cipher.getAuthTag();
|
|
83
|
-
const encodedHeader = encodeBase64Url(JSON.stringify(header));
|
|
84
|
-
const encodedIv = base64UrlEncode(iv);
|
|
85
|
-
const encodedEncrypted = base64UrlEncode(encrypted);
|
|
86
|
-
const encodedAuthTag = base64UrlEncode(authTag);
|
|
87
|
-
return `${encodedHeader}.${encodedIv}.${encodedEncrypted}.${encodedAuthTag}`;
|
|
88
|
-
}
|
|
89
|
-
function decrypt(token, secret) {
|
|
90
|
-
try {
|
|
91
|
-
const parts = token.split(".");
|
|
92
|
-
if (parts.length !== 4) return null;
|
|
93
|
-
const [encodedHeader, encodedIv, encodedEncrypted, encodedAuthTag] = parts;
|
|
94
|
-
const key = importKey(secret);
|
|
95
|
-
const header = JSON.parse(decodeBase64Url(encodedHeader));
|
|
96
|
-
if (header.alg !== ALGORITHM || header.enc !== ENC_ALGORITHM) return null;
|
|
97
|
-
const iv = base64UrlDecodeSafe(encodedIv);
|
|
98
|
-
const encrypted = base64UrlDecodeSafe(encodedEncrypted);
|
|
99
|
-
const authTag = base64UrlDecodeSafe(encodedAuthTag);
|
|
100
|
-
const aad = Buffer.from(encodedHeader);
|
|
101
|
-
const decipher = crypto.createDecipheriv(ENC_ALGORITHM, key, iv);
|
|
102
|
-
decipher.setAAD(aad);
|
|
103
|
-
decipher.setAuthTag(authTag);
|
|
104
|
-
const plaintext = Buffer.concat([decipher.update(encrypted), decipher.final()]);
|
|
105
|
-
const payload = JSON.parse(plaintext.toString());
|
|
106
|
-
if (payload.exp && payload.exp < Math.floor(Date.now() / 1e3)) return null;
|
|
107
|
-
return payload;
|
|
108
|
-
} catch {
|
|
109
|
-
return null;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
function signEncrypted(payload, secret, options = { secret }) {
|
|
113
|
-
return encrypt(payload, secret, options);
|
|
64
|
+
function signEncrypted(payload, privateKey, options = {}) {
|
|
65
|
+
return sign(payload, privateKey, options);
|
|
114
66
|
}
|
|
115
|
-
function verifyEncrypted(token,
|
|
116
|
-
return
|
|
67
|
+
function verifyEncrypted(token, publicKey) {
|
|
68
|
+
return verify(token, publicKey);
|
|
117
69
|
}
|
|
118
|
-
function createTokenPair(payload,
|
|
119
|
-
const accessToken = sign(payload,
|
|
70
|
+
function createTokenPair(payload, privateKey, options = {}) {
|
|
71
|
+
const accessToken = sign(payload, privateKey, options);
|
|
120
72
|
const refreshPayload = { ...payload, type: "refresh" };
|
|
121
|
-
const refreshToken = sign(refreshPayload,
|
|
73
|
+
const refreshToken = sign(refreshPayload, privateKey, { ...options, expiresIn: "7d" });
|
|
122
74
|
return { accessToken, refreshToken };
|
|
123
75
|
}
|
|
124
|
-
function verifyTokenPair(token,
|
|
76
|
+
function verifyTokenPair(token, publicKey) {
|
|
125
77
|
const [accessToken, refreshToken] = token.split(".");
|
|
126
78
|
return {
|
|
127
|
-
accessToken: verify(accessToken,
|
|
128
|
-
refreshToken: verify(refreshToken,
|
|
79
|
+
accessToken: verify(accessToken, publicKey),
|
|
80
|
+
refreshToken: verify(refreshToken, publicKey)
|
|
129
81
|
};
|
|
130
82
|
}
|
|
83
|
+
function getJwtFromEnv() {
|
|
84
|
+
const publicKey = process.env.JWT_PUBLIC_KEY;
|
|
85
|
+
const privateKey = process.env.JWT_PRIVATE_KEY;
|
|
86
|
+
if (!publicKey || !privateKey) {
|
|
87
|
+
throw new Error("JWT keys not configured. Run 'npm run generate:keys' and add the keys to your .env file.");
|
|
88
|
+
}
|
|
89
|
+
return { publicKey, privateKey };
|
|
90
|
+
}
|
|
131
91
|
exports.createTokenPair = createTokenPair;
|
|
132
|
-
exports.
|
|
133
|
-
exports.
|
|
92
|
+
exports.generateKeyPair = generateKeyPair;
|
|
93
|
+
exports.getJwtFromEnv = getJwtFromEnv;
|
|
134
94
|
exports.sign = sign;
|
|
135
95
|
exports.signEncrypted = signEncrypted;
|
|
136
96
|
exports.verify = verify;
|
package/dist/esm/cli.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawn } from "child_process";
|
|
3
3
|
import { existsSync } from "fs";
|
|
4
|
-
import
|
|
4
|
+
import * as crypto from "crypto";
|
|
5
5
|
import { join, resolve } from "path";
|
|
6
|
+
import dotenv from "dotenv";
|
|
6
7
|
const args = process.argv.slice(2);
|
|
7
8
|
const command = args[0];
|
|
8
9
|
if (!command) {
|
|
@@ -37,6 +38,23 @@ function getProjectRoot() {
|
|
|
37
38
|
}
|
|
38
39
|
return process.cwd();
|
|
39
40
|
}
|
|
41
|
+
function loadEnv() {
|
|
42
|
+
const projectRoot2 = getProjectRoot();
|
|
43
|
+
const envFile = existsSync(join(projectRoot2, ".env")) ? ".env" : existsSync(join(projectRoot2, ".env.development")) ? ".env.development" : existsSync(join(projectRoot2, ".env.production")) ? ".env.production" : null;
|
|
44
|
+
if (envFile) {
|
|
45
|
+
dotenv.config({ path: join(projectRoot2, envFile) });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function checkJwtKeys() {
|
|
49
|
+
loadEnv();
|
|
50
|
+
const publicKey = process.env.JWT_PUBLIC_KEY;
|
|
51
|
+
const privateKey = process.env.JWT_PRIVATE_KEY;
|
|
52
|
+
if (!publicKey || !privateKey) {
|
|
53
|
+
console.error("\nā Error: JWT keys not configured.");
|
|
54
|
+
console.error("Run 'npm run generate:keys' to generate new keys.\n");
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
40
58
|
const projectRoot = getProjectRoot();
|
|
41
59
|
function runCommand(cmd, envVars) {
|
|
42
60
|
const child = spawn(cmd, args.slice(1), {
|
|
@@ -51,6 +69,7 @@ function runCommand(cmd, envVars) {
|
|
|
51
69
|
}
|
|
52
70
|
switch (command) {
|
|
53
71
|
case "dev":
|
|
72
|
+
checkJwtKeys();
|
|
54
73
|
console.log("š Starting development server with hot reload...");
|
|
55
74
|
const srcFile = existsSync(join(projectRoot, "src/app.ts")) ? "src/app.ts" : existsSync(join(projectRoot, "src/app.js")) ? "src/app.js" : existsSync(join(projectRoot, "src/index.ts")) ? "src/index.ts" : "src/index.js";
|
|
56
75
|
runCommand(`tsx --watch ${srcFile}`, { NODE_ENV: "development" });
|
|
@@ -65,14 +84,20 @@ switch (command) {
|
|
|
65
84
|
}
|
|
66
85
|
break;
|
|
67
86
|
case "start":
|
|
87
|
+
checkJwtKeys();
|
|
68
88
|
console.log("š Starting production server...");
|
|
69
89
|
runCommand("node dist/index.js", { NODE_ENV: "production" });
|
|
70
90
|
break;
|
|
71
91
|
case "generate-keys":
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
92
|
+
const { publicKey, privateKey } = crypto.generateKeyPairSync("ec", {
|
|
93
|
+
namedCurve: "prime256v1",
|
|
94
|
+
publicKeyEncoding: { type: "spki", format: "pem" },
|
|
95
|
+
privateKeyEncoding: { type: "pkcs8", format: "pem" }
|
|
96
|
+
});
|
|
97
|
+
console.log("\nš Generating JWT keys...\n");
|
|
98
|
+
console.log("JWT_PUBLIC_KEY=" + publicKey.replace(/\n/g, "\\n"));
|
|
99
|
+
console.log("\nJWT_PRIVATE_KEY=" + privateKey.replace(/\n/g, "\\n"));
|
|
100
|
+
console.log("\nš Add these to your .env file:\n");
|
|
76
101
|
process.exit(0);
|
|
77
102
|
break;
|
|
78
103
|
default:
|
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
const ALGORITHM = "
|
|
3
|
-
const ENC_ALGORITHM = "aes-256-gcm";
|
|
4
|
-
const KEY_LENGTH = 32;
|
|
5
|
-
const IV_LENGTH = 12;
|
|
1
|
+
import crypto__default from "crypto";
|
|
2
|
+
const ALGORITHM = "ES256";
|
|
6
3
|
function base64UrlEncode(buffer) {
|
|
7
4
|
return buffer.toString("base64url");
|
|
8
5
|
}
|
|
9
|
-
function
|
|
6
|
+
function base64UrlDecode(str) {
|
|
10
7
|
const pad = str.length % 4;
|
|
11
8
|
const padded = pad ? str + "=".repeat(4 - pad) : str;
|
|
12
9
|
return Buffer.from(padded.replace(/-/g, "+").replace(/_/g, "/"), "base64");
|
|
@@ -14,20 +11,22 @@ function base64UrlDecodeSafe(str) {
|
|
|
14
11
|
function decodeBase64Url(str) {
|
|
15
12
|
return Buffer.from(str, "base64url").toString();
|
|
16
13
|
}
|
|
17
|
-
function
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
14
|
+
function generateKeyPair(algorithm = ALGORITHM) {
|
|
15
|
+
const { publicKey, privateKey } = crypto__default.generateKeyPairSync("ec", {
|
|
16
|
+
namedCurve: "prime256v1",
|
|
17
|
+
publicKeyEncoding: {
|
|
18
|
+
type: "spki",
|
|
19
|
+
format: "pem"
|
|
20
|
+
},
|
|
21
|
+
privateKeyEncoding: {
|
|
22
|
+
type: "pkcs8",
|
|
23
|
+
format: "pem"
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
return { publicKey, privateKey };
|
|
28
27
|
}
|
|
29
|
-
function sign(payload,
|
|
30
|
-
const header = { alg:
|
|
28
|
+
function sign(payload, privateKey, options = {}) {
|
|
29
|
+
const header = { alg: ALGORITHM, typ: "JWT" };
|
|
31
30
|
const now = Math.floor(Date.now() / 1e3);
|
|
32
31
|
const claims = { ...payload };
|
|
33
32
|
if (options.expiresIn) {
|
|
@@ -37,19 +36,22 @@ function sign(payload, secret, options = {}) {
|
|
|
37
36
|
if (options.subject) claims.sub = options.subject;
|
|
38
37
|
if (options.audience) claims.aud = options.audience;
|
|
39
38
|
claims.iat = now;
|
|
40
|
-
const encodedHeader =
|
|
41
|
-
const encodedPayload =
|
|
42
|
-
const
|
|
39
|
+
const encodedHeader = base64UrlEncode(Buffer.from(JSON.stringify(header)));
|
|
40
|
+
const encodedPayload = base64UrlEncode(Buffer.from(JSON.stringify(claims)));
|
|
41
|
+
const sign2 = crypto__default.createSign("SHA256");
|
|
42
|
+
sign2.update(`${encodedHeader}.${encodedPayload}`);
|
|
43
|
+
const signature = sign2.sign(privateKey);
|
|
43
44
|
return `${encodedHeader}.${encodedPayload}.${base64UrlEncode(signature)}`;
|
|
44
45
|
}
|
|
45
|
-
function verify(token,
|
|
46
|
+
function verify(token, publicKey) {
|
|
46
47
|
try {
|
|
47
48
|
const parts = token.split(".");
|
|
48
49
|
if (parts.length !== 3) return null;
|
|
49
50
|
const [encodedHeader, encodedPayload, signature] = parts;
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
const verify2 = crypto__default.createVerify("SHA256");
|
|
52
|
+
verify2.update(`${encodedHeader}.${encodedPayload}`);
|
|
53
|
+
const isValid = verify2.verify(publicKey, base64UrlDecode(signature));
|
|
54
|
+
if (!isValid) return null;
|
|
53
55
|
const payload = JSON.parse(decodeBase64Url(encodedPayload));
|
|
54
56
|
if (payload.exp && payload.exp < Math.floor(Date.now() / 1e3)) return null;
|
|
55
57
|
return payload;
|
|
@@ -57,79 +59,37 @@ function verify(token, secret) {
|
|
|
57
59
|
return null;
|
|
58
60
|
}
|
|
59
61
|
}
|
|
60
|
-
function
|
|
61
|
-
|
|
62
|
-
const key = importKey(secret);
|
|
63
|
-
const header = {
|
|
64
|
-
alg: ALGORITHM,
|
|
65
|
-
enc: ENC_ALGORITHM,
|
|
66
|
-
typ: "JWT"
|
|
67
|
-
};
|
|
68
|
-
const claims = { ...payload };
|
|
69
|
-
if (options.expiresIn) {
|
|
70
|
-
claims.exp = now + parseInt(String(options.expiresIn));
|
|
71
|
-
}
|
|
72
|
-
if (options.issuer) claims.iss = options.issuer;
|
|
73
|
-
claims.iat = now;
|
|
74
|
-
const iv = crypto.randomBytes(IV_LENGTH);
|
|
75
|
-
const aad = Buffer.from(JSON.stringify(header));
|
|
76
|
-
const cipher = crypto.createCipheriv(ENC_ALGORITHM, key, iv);
|
|
77
|
-
cipher.setAAD(aad);
|
|
78
|
-
const plaintext = Buffer.from(JSON.stringify(claims));
|
|
79
|
-
const encrypted = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
80
|
-
const authTag = cipher.getAuthTag();
|
|
81
|
-
const encodedHeader = encodeBase64Url(JSON.stringify(header));
|
|
82
|
-
const encodedIv = base64UrlEncode(iv);
|
|
83
|
-
const encodedEncrypted = base64UrlEncode(encrypted);
|
|
84
|
-
const encodedAuthTag = base64UrlEncode(authTag);
|
|
85
|
-
return `${encodedHeader}.${encodedIv}.${encodedEncrypted}.${encodedAuthTag}`;
|
|
86
|
-
}
|
|
87
|
-
function decrypt(token, secret) {
|
|
88
|
-
try {
|
|
89
|
-
const parts = token.split(".");
|
|
90
|
-
if (parts.length !== 4) return null;
|
|
91
|
-
const [encodedHeader, encodedIv, encodedEncrypted, encodedAuthTag] = parts;
|
|
92
|
-
const key = importKey(secret);
|
|
93
|
-
const header = JSON.parse(decodeBase64Url(encodedHeader));
|
|
94
|
-
if (header.alg !== ALGORITHM || header.enc !== ENC_ALGORITHM) return null;
|
|
95
|
-
const iv = base64UrlDecodeSafe(encodedIv);
|
|
96
|
-
const encrypted = base64UrlDecodeSafe(encodedEncrypted);
|
|
97
|
-
const authTag = base64UrlDecodeSafe(encodedAuthTag);
|
|
98
|
-
const aad = Buffer.from(encodedHeader);
|
|
99
|
-
const decipher = crypto.createDecipheriv(ENC_ALGORITHM, key, iv);
|
|
100
|
-
decipher.setAAD(aad);
|
|
101
|
-
decipher.setAuthTag(authTag);
|
|
102
|
-
const plaintext = Buffer.concat([decipher.update(encrypted), decipher.final()]);
|
|
103
|
-
const payload = JSON.parse(plaintext.toString());
|
|
104
|
-
if (payload.exp && payload.exp < Math.floor(Date.now() / 1e3)) return null;
|
|
105
|
-
return payload;
|
|
106
|
-
} catch {
|
|
107
|
-
return null;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
function signEncrypted(payload, secret, options = { secret }) {
|
|
111
|
-
return encrypt(payload, secret, options);
|
|
62
|
+
function signEncrypted(payload, privateKey, options = {}) {
|
|
63
|
+
return sign(payload, privateKey, options);
|
|
112
64
|
}
|
|
113
|
-
function verifyEncrypted(token,
|
|
114
|
-
return
|
|
65
|
+
function verifyEncrypted(token, publicKey) {
|
|
66
|
+
return verify(token, publicKey);
|
|
115
67
|
}
|
|
116
|
-
function createTokenPair(payload,
|
|
117
|
-
const accessToken = sign(payload,
|
|
68
|
+
function createTokenPair(payload, privateKey, options = {}) {
|
|
69
|
+
const accessToken = sign(payload, privateKey, options);
|
|
118
70
|
const refreshPayload = { ...payload, type: "refresh" };
|
|
119
|
-
const refreshToken = sign(refreshPayload,
|
|
71
|
+
const refreshToken = sign(refreshPayload, privateKey, { ...options, expiresIn: "7d" });
|
|
120
72
|
return { accessToken, refreshToken };
|
|
121
73
|
}
|
|
122
|
-
function verifyTokenPair(token,
|
|
74
|
+
function verifyTokenPair(token, publicKey) {
|
|
123
75
|
const [accessToken, refreshToken] = token.split(".");
|
|
124
76
|
return {
|
|
125
|
-
accessToken: verify(accessToken,
|
|
126
|
-
refreshToken: verify(refreshToken,
|
|
77
|
+
accessToken: verify(accessToken, publicKey),
|
|
78
|
+
refreshToken: verify(refreshToken, publicKey)
|
|
127
79
|
};
|
|
128
80
|
}
|
|
81
|
+
function getJwtFromEnv() {
|
|
82
|
+
const publicKey = process.env.JWT_PUBLIC_KEY;
|
|
83
|
+
const privateKey = process.env.JWT_PRIVATE_KEY;
|
|
84
|
+
if (!publicKey || !privateKey) {
|
|
85
|
+
throw new Error("JWT keys not configured. Run 'npm run generate:keys' and add the keys to your .env file.");
|
|
86
|
+
}
|
|
87
|
+
return { publicKey, privateKey };
|
|
88
|
+
}
|
|
129
89
|
export {
|
|
130
90
|
createTokenPair,
|
|
131
|
-
|
|
132
|
-
|
|
91
|
+
generateKeyPair,
|
|
92
|
+
getJwtFromEnv,
|
|
133
93
|
sign,
|
|
134
94
|
signEncrypted,
|
|
135
95
|
verify,
|
|
@@ -7,23 +7,25 @@ export interface JWTOptions {
|
|
|
7
7
|
subject?: string;
|
|
8
8
|
audience?: string | string[];
|
|
9
9
|
}
|
|
10
|
-
export interface
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
issuer?: string;
|
|
10
|
+
export interface KeyPair {
|
|
11
|
+
publicKey: string;
|
|
12
|
+
privateKey: string;
|
|
14
13
|
}
|
|
15
|
-
export declare function
|
|
16
|
-
export declare function
|
|
17
|
-
export declare function
|
|
18
|
-
export declare function
|
|
19
|
-
export declare function
|
|
20
|
-
export declare function
|
|
21
|
-
export declare function createTokenPair(payload: JWTPayload, secret: string, options?: JWTOptions): {
|
|
14
|
+
export declare function generateKeyPair(algorithm?: string): KeyPair;
|
|
15
|
+
export declare function sign(payload: JWTPayload, privateKey: string, options?: JWTOptions): string;
|
|
16
|
+
export declare function verify(token: string, publicKey: string): JWTPayload | null;
|
|
17
|
+
export declare function signEncrypted(payload: JWTPayload, privateKey: string, options?: JWTOptions): string;
|
|
18
|
+
export declare function verifyEncrypted(token: string, publicKey: string): JWTPayload | null;
|
|
19
|
+
export declare function createTokenPair(payload: JWTPayload, privateKey: string, options?: JWTOptions): {
|
|
22
20
|
accessToken: string;
|
|
23
21
|
refreshToken: string;
|
|
24
22
|
};
|
|
25
|
-
export declare function verifyTokenPair(token: string,
|
|
23
|
+
export declare function verifyTokenPair(token: string, publicKey: string): {
|
|
26
24
|
accessToken: JWTPayload | null;
|
|
27
25
|
refreshToken: JWTPayload | null;
|
|
28
26
|
};
|
|
27
|
+
export declare function getJwtFromEnv(): {
|
|
28
|
+
publicKey: string;
|
|
29
|
+
privateKey: string;
|
|
30
|
+
};
|
|
29
31
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/jwt/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modules/jwt/index.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,UAAU;IACvB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACvB,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,OAAO;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,eAAe,CAAC,SAAS,GAAE,MAAkB,GAAG,OAAO,CActE;AAED,wBAAgB,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe,GAAG,MAAM,CAqB9F;AAED,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAqB1E;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe,GAAG,MAAM,CAEvG;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAEnF;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,GAAE,UAAe,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAMhJ;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG;IAAE,WAAW,EAAE,UAAU,GAAG,IAAI,CAAC;IAAC,YAAY,EAAE,UAAU,GAAG,IAAI,CAAA;CAAE,CAMrI;AAED,wBAAgB,aAAa,IAAI;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CASzE"}
|