@logto/node 2.5.8 → 3.0.3
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/package.json +5 -9
- package/lib/edge/generators.cjs +0 -44
- package/lib/edge/index.cjs +0 -39
- package/lib/src/client.cjs +0 -80
- package/lib/src/index.cjs +0 -89
- package/lib/src/utils/cookie-storage.cjs +0 -63
- package/lib/src/utils/promise-queue.cjs +0 -46
- package/lib/src/utils/session.cjs +0 -60
package/package.json
CHANGED
|
@@ -1,23 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logto/node",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.3",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"main": "./lib/src/index.cjs",
|
|
6
5
|
"module": "./lib/src/index.js",
|
|
7
6
|
"types": "./lib/src/index.d.ts",
|
|
8
7
|
"exports": {
|
|
9
8
|
".": {
|
|
10
|
-
"require": "./lib/src/index.cjs",
|
|
11
9
|
"import": "./lib/src/index.js",
|
|
12
10
|
"types": "./lib/src/index.d.ts"
|
|
13
11
|
},
|
|
14
12
|
"./edge": {
|
|
15
|
-
"require": "./lib/edge/index.cjs",
|
|
16
13
|
"import": "./lib/edge/index.js",
|
|
17
14
|
"types": "./lib/edge/index.d.ts"
|
|
18
15
|
},
|
|
19
16
|
"./exports": {
|
|
20
|
-
"require": "./lib/exports/index.cjs",
|
|
21
17
|
"import": "./lib/exports/index.js",
|
|
22
18
|
"types": "./lib/exports/index.d.ts"
|
|
23
19
|
}
|
|
@@ -32,15 +28,15 @@
|
|
|
32
28
|
"directory": "packages/node"
|
|
33
29
|
},
|
|
34
30
|
"dependencies": {
|
|
35
|
-
"@silverhand/essentials": "^2.
|
|
31
|
+
"@silverhand/essentials": "^2.9.2",
|
|
36
32
|
"js-base64": "^3.7.4",
|
|
37
|
-
"@logto/client": "^
|
|
33
|
+
"@logto/client": "^3.0.3"
|
|
38
34
|
},
|
|
39
35
|
"devDependencies": {
|
|
40
36
|
"@silverhand/eslint-config": "^6.0.1",
|
|
41
37
|
"@silverhand/ts-config": "^6.0.0",
|
|
42
38
|
"@types/cookie": "^0.6.0",
|
|
43
|
-
"@types/node": "^
|
|
39
|
+
"@types/node": "^22.0.0",
|
|
44
40
|
"@vitest/coverage-v8": "^1.6.0",
|
|
45
41
|
"eslint": "^8.57.0",
|
|
46
42
|
"lint-staged": "^15.0.0",
|
|
@@ -62,6 +58,6 @@
|
|
|
62
58
|
"build": "rm -rf lib/ && tsc -p tsconfig.build.json --noEmit && rollup -c",
|
|
63
59
|
"lint": "eslint --ext .ts src",
|
|
64
60
|
"test": "vitest",
|
|
65
|
-
"test:coverage": "
|
|
61
|
+
"test:coverage": "vitest --silent --coverage"
|
|
66
62
|
}
|
|
67
63
|
}
|
package/lib/edge/generators.cjs
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var BaseClient = require('@logto/client');
|
|
4
|
-
var jsBase64 = require('js-base64');
|
|
5
|
-
|
|
6
|
-
/** @link [Proof Key for Code Exchange by OAuth Public Clients](https://datatracker.ietf.org/doc/html/rfc7636) */
|
|
7
|
-
/**
|
|
8
|
-
* @param length The length of the raw random data.
|
|
9
|
-
*/
|
|
10
|
-
const generateRandomString = (length = 64) => jsBase64.fromUint8Array(crypto.getRandomValues(new Uint8Array(length)), true);
|
|
11
|
-
/**
|
|
12
|
-
* Generates random string for state and encodes them in url safe base64
|
|
13
|
-
*/
|
|
14
|
-
const generateState = () => generateRandomString();
|
|
15
|
-
/**
|
|
16
|
-
* Generates code verifier
|
|
17
|
-
*
|
|
18
|
-
* @link [Client Creates a Code Verifier](https://datatracker.ietf.org/doc/html/rfc7636#section-4.1)
|
|
19
|
-
*/
|
|
20
|
-
const generateCodeVerifier = () => generateRandomString();
|
|
21
|
-
/**
|
|
22
|
-
* Calculates the S256 PKCE code challenge for an arbitrary code verifier and encodes it in url safe base64
|
|
23
|
-
*
|
|
24
|
-
* @param {String} codeVerifier Code verifier to calculate the S256 code challenge for
|
|
25
|
-
* @link [Client Creates the Code Challenge](https://datatracker.ietf.org/doc/html/rfc7636#section-4.2)
|
|
26
|
-
*/
|
|
27
|
-
const generateCodeChallenge = async (codeVerifier) => {
|
|
28
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
29
|
-
if (crypto.subtle === undefined) {
|
|
30
|
-
/**
|
|
31
|
-
* `crypto.subtle` is available only in secure contexts (HTTPS) in some or all supporting browsers,
|
|
32
|
-
* https://developer.mozilla.org/en-US/docs/Web/API/Crypto/subtle
|
|
33
|
-
* https://www.chromium.org/blink/webcrypto/#accessing-it
|
|
34
|
-
*/
|
|
35
|
-
throw new BaseClient.LogtoError('crypto_subtle_unavailable');
|
|
36
|
-
}
|
|
37
|
-
const encodedCodeVerifier = new TextEncoder().encode(codeVerifier);
|
|
38
|
-
const codeChallenge = new Uint8Array(await crypto.subtle.digest('SHA-256', encodedCodeVerifier));
|
|
39
|
-
return jsBase64.fromUint8Array(codeChallenge, true);
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
exports.generateCodeChallenge = generateCodeChallenge;
|
|
43
|
-
exports.generateCodeVerifier = generateCodeVerifier;
|
|
44
|
-
exports.generateState = generateState;
|
package/lib/edge/index.cjs
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
-
|
|
5
|
-
var BaseClient = require('@logto/client');
|
|
6
|
-
var client = require('../src/client.cjs');
|
|
7
|
-
var generators = require('./generators.cjs');
|
|
8
|
-
|
|
9
|
-
// Used for edge runtime, currently only NextJS.
|
|
10
|
-
class LogtoClient extends client.default {
|
|
11
|
-
constructor(config, adapter) {
|
|
12
|
-
super(config, {
|
|
13
|
-
...adapter,
|
|
14
|
-
requester: BaseClient.createRequester(config.appSecret
|
|
15
|
-
? async (...args) => {
|
|
16
|
-
const [input, init] = args;
|
|
17
|
-
// Encode to base64 using btoa
|
|
18
|
-
const base64Credentials = btoa(`${config.appId}:${config.appSecret ?? ''}`);
|
|
19
|
-
return fetch(input, {
|
|
20
|
-
...init,
|
|
21
|
-
headers: {
|
|
22
|
-
Authorization: `Basic ${base64Credentials}`,
|
|
23
|
-
...init?.headers,
|
|
24
|
-
},
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
: fetch),
|
|
28
|
-
generateCodeChallenge: generators.generateCodeChallenge,
|
|
29
|
-
generateCodeVerifier: generators.generateCodeVerifier,
|
|
30
|
-
generateState: generators.generateState,
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
Object.defineProperty(exports, "PersistKey", {
|
|
36
|
-
enumerable: true,
|
|
37
|
-
get: function () { return BaseClient.PersistKey; }
|
|
38
|
-
});
|
|
39
|
-
exports.default = LogtoClient;
|
package/lib/src/client.cjs
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
-
|
|
5
|
-
var BaseClient = require('@logto/client');
|
|
6
|
-
var essentials = require('@silverhand/essentials');
|
|
7
|
-
|
|
8
|
-
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
9
|
-
|
|
10
|
-
var BaseClient__default = /*#__PURE__*/_interopDefault(BaseClient);
|
|
11
|
-
|
|
12
|
-
class LogtoNodeBaseClient extends BaseClient__default.default {
|
|
13
|
-
constructor() {
|
|
14
|
-
super(...arguments);
|
|
15
|
-
this.getContext = async ({ getAccessToken, resource, fetchUserInfo, getOrganizationToken, organizationId, } = {}) => {
|
|
16
|
-
const isAuthenticated = await this.isAuthenticated();
|
|
17
|
-
if (!isAuthenticated) {
|
|
18
|
-
return {
|
|
19
|
-
isAuthenticated,
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
const claims = await this.getIdTokenClaims();
|
|
23
|
-
const { accessToken, accessTokenClaims } = getAccessToken
|
|
24
|
-
? {
|
|
25
|
-
accessToken: await essentials.trySafe(async () => this.getAccessToken(resource, organizationId)),
|
|
26
|
-
accessTokenClaims: await essentials.trySafe(async () => this.getAccessTokenClaims(resource)),
|
|
27
|
-
}
|
|
28
|
-
: { accessToken: undefined, accessTokenClaims: undefined };
|
|
29
|
-
if (getAccessToken && !accessToken) {
|
|
30
|
-
// Failed to get access token, the user is not authenticated
|
|
31
|
-
return {
|
|
32
|
-
isAuthenticated: false,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
const organizationTokens = essentials.conditional(getOrganizationToken &&
|
|
36
|
-
claims.organizations &&
|
|
37
|
-
Object.fromEntries(await Promise.all(claims.organizations.map(async (organizationId) => [
|
|
38
|
-
organizationId,
|
|
39
|
-
await this.getOrganizationToken(organizationId),
|
|
40
|
-
]))));
|
|
41
|
-
return {
|
|
42
|
-
isAuthenticated,
|
|
43
|
-
claims,
|
|
44
|
-
userInfo: essentials.conditional(fetchUserInfo && (await this.fetchUserInfo())),
|
|
45
|
-
...essentials.conditional(getAccessToken && { accessToken, scopes: accessTokenClaims?.scope?.split(' ') }),
|
|
46
|
-
organizationTokens,
|
|
47
|
-
};
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
Object.defineProperty(exports, "LogtoClientError", {
|
|
53
|
-
enumerable: true,
|
|
54
|
-
get: function () { return BaseClient.LogtoClientError; }
|
|
55
|
-
});
|
|
56
|
-
Object.defineProperty(exports, "LogtoError", {
|
|
57
|
-
enumerable: true,
|
|
58
|
-
get: function () { return BaseClient.LogtoError; }
|
|
59
|
-
});
|
|
60
|
-
Object.defineProperty(exports, "LogtoRequestError", {
|
|
61
|
-
enumerable: true,
|
|
62
|
-
get: function () { return BaseClient.LogtoRequestError; }
|
|
63
|
-
});
|
|
64
|
-
Object.defineProperty(exports, "OidcError", {
|
|
65
|
-
enumerable: true,
|
|
66
|
-
get: function () { return BaseClient.OidcError; }
|
|
67
|
-
});
|
|
68
|
-
Object.defineProperty(exports, "Prompt", {
|
|
69
|
-
enumerable: true,
|
|
70
|
-
get: function () { return BaseClient.Prompt; }
|
|
71
|
-
});
|
|
72
|
-
Object.defineProperty(exports, "ReservedScope", {
|
|
73
|
-
enumerable: true,
|
|
74
|
-
get: function () { return BaseClient.ReservedScope; }
|
|
75
|
-
});
|
|
76
|
-
Object.defineProperty(exports, "UserScope", {
|
|
77
|
-
enumerable: true,
|
|
78
|
-
get: function () { return BaseClient.UserScope; }
|
|
79
|
-
});
|
|
80
|
-
exports.default = LogtoNodeBaseClient;
|
package/lib/src/index.cjs
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
-
|
|
5
|
-
var BaseClient = require('@logto/client');
|
|
6
|
-
var generators = require('../edge/generators.cjs');
|
|
7
|
-
var client = require('./client.cjs');
|
|
8
|
-
var session = require('./utils/session.cjs');
|
|
9
|
-
var cookieStorage = require('./utils/cookie-storage.cjs');
|
|
10
|
-
|
|
11
|
-
class LogtoClient extends client.default {
|
|
12
|
-
constructor(config, adapter, buildJwtVerifier) {
|
|
13
|
-
super(config, {
|
|
14
|
-
requester: BaseClient.createRequester(config.appSecret
|
|
15
|
-
? async (...args) => {
|
|
16
|
-
const [input, init] = args;
|
|
17
|
-
return fetch(input, {
|
|
18
|
-
...init,
|
|
19
|
-
headers: {
|
|
20
|
-
Authorization: `Basic ${Buffer.from(`${config.appId}:${config.appSecret}`, 'utf8').toString('base64')}`,
|
|
21
|
-
...init?.headers,
|
|
22
|
-
},
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
: fetch),
|
|
26
|
-
generateCodeChallenge: generators.generateCodeChallenge,
|
|
27
|
-
generateCodeVerifier: generators.generateCodeVerifier,
|
|
28
|
-
generateState: generators.generateState,
|
|
29
|
-
...adapter,
|
|
30
|
-
}, buildJwtVerifier);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
Object.defineProperty(exports, "LogtoClientError", {
|
|
35
|
-
enumerable: true,
|
|
36
|
-
get: function () { return BaseClient.LogtoClientError; }
|
|
37
|
-
});
|
|
38
|
-
Object.defineProperty(exports, "LogtoError", {
|
|
39
|
-
enumerable: true,
|
|
40
|
-
get: function () { return BaseClient.LogtoError; }
|
|
41
|
-
});
|
|
42
|
-
Object.defineProperty(exports, "LogtoRequestError", {
|
|
43
|
-
enumerable: true,
|
|
44
|
-
get: function () { return BaseClient.LogtoRequestError; }
|
|
45
|
-
});
|
|
46
|
-
Object.defineProperty(exports, "OidcError", {
|
|
47
|
-
enumerable: true,
|
|
48
|
-
get: function () { return BaseClient.OidcError; }
|
|
49
|
-
});
|
|
50
|
-
Object.defineProperty(exports, "PersistKey", {
|
|
51
|
-
enumerable: true,
|
|
52
|
-
get: function () { return BaseClient.PersistKey; }
|
|
53
|
-
});
|
|
54
|
-
Object.defineProperty(exports, "Prompt", {
|
|
55
|
-
enumerable: true,
|
|
56
|
-
get: function () { return BaseClient.Prompt; }
|
|
57
|
-
});
|
|
58
|
-
Object.defineProperty(exports, "ReservedResource", {
|
|
59
|
-
enumerable: true,
|
|
60
|
-
get: function () { return BaseClient.ReservedResource; }
|
|
61
|
-
});
|
|
62
|
-
Object.defineProperty(exports, "ReservedScope", {
|
|
63
|
-
enumerable: true,
|
|
64
|
-
get: function () { return BaseClient.ReservedScope; }
|
|
65
|
-
});
|
|
66
|
-
Object.defineProperty(exports, "StandardLogtoClient", {
|
|
67
|
-
enumerable: true,
|
|
68
|
-
get: function () { return BaseClient.StandardLogtoClient; }
|
|
69
|
-
});
|
|
70
|
-
Object.defineProperty(exports, "UserScope", {
|
|
71
|
-
enumerable: true,
|
|
72
|
-
get: function () { return BaseClient.UserScope; }
|
|
73
|
-
});
|
|
74
|
-
Object.defineProperty(exports, "buildOrganizationUrn", {
|
|
75
|
-
enumerable: true,
|
|
76
|
-
get: function () { return BaseClient.buildOrganizationUrn; }
|
|
77
|
-
});
|
|
78
|
-
Object.defineProperty(exports, "getOrganizationIdFromUrn", {
|
|
79
|
-
enumerable: true,
|
|
80
|
-
get: function () { return BaseClient.getOrganizationIdFromUrn; }
|
|
81
|
-
});
|
|
82
|
-
Object.defineProperty(exports, "organizationUrnPrefix", {
|
|
83
|
-
enumerable: true,
|
|
84
|
-
get: function () { return BaseClient.organizationUrnPrefix; }
|
|
85
|
-
});
|
|
86
|
-
exports.unwrapSession = session.unwrapSession;
|
|
87
|
-
exports.wrapSession = session.wrapSession;
|
|
88
|
-
exports.CookieStorage = cookieStorage.CookieStorage;
|
|
89
|
-
exports.default = LogtoClient;
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var promiseQueue = require('./promise-queue.cjs');
|
|
4
|
-
var session = require('./session.cjs');
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* A storage that persists data in cookies with encryption.
|
|
8
|
-
*/
|
|
9
|
-
class CookieStorage {
|
|
10
|
-
get cookieOptions() {
|
|
11
|
-
return Object.freeze({
|
|
12
|
-
httpOnly: true,
|
|
13
|
-
path: '/',
|
|
14
|
-
sameSite: 'lax',
|
|
15
|
-
secure: this.config.isSecure ?? false,
|
|
16
|
-
maxAge: 14 * 24 * 3600, // 14 days
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
get cookieKey() {
|
|
20
|
-
return this.config.cookieKey ?? 'logtoCookies';
|
|
21
|
-
}
|
|
22
|
-
get data() {
|
|
23
|
-
return this.sessionData;
|
|
24
|
-
}
|
|
25
|
-
constructor(config) {
|
|
26
|
-
this.config = config;
|
|
27
|
-
this.sessionData = {};
|
|
28
|
-
this.saveQueue = new promiseQueue.PromiseQueue();
|
|
29
|
-
if (!config.encryptionKey) {
|
|
30
|
-
throw new TypeError('The `encryptionKey` string is required for `CookieStorage`');
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
async init() {
|
|
34
|
-
const { encryptionKey } = this.config;
|
|
35
|
-
this.sessionData = await session.unwrapSession(this.config.getCookie(this.cookieKey) ?? '', encryptionKey);
|
|
36
|
-
}
|
|
37
|
-
async getItem(key) {
|
|
38
|
-
return this.sessionData[key] ?? null;
|
|
39
|
-
}
|
|
40
|
-
async setItem(key, value) {
|
|
41
|
-
this.sessionData[key] = value;
|
|
42
|
-
await this.save();
|
|
43
|
-
}
|
|
44
|
-
async removeItem(key) {
|
|
45
|
-
// eslint-disable-next-line @silverhand/fp/no-delete, @typescript-eslint/no-dynamic-delete
|
|
46
|
-
delete this.sessionData[key];
|
|
47
|
-
await this.save();
|
|
48
|
-
}
|
|
49
|
-
async destroy() {
|
|
50
|
-
this.sessionData = {};
|
|
51
|
-
await this.save();
|
|
52
|
-
}
|
|
53
|
-
async save() {
|
|
54
|
-
return this.saveQueue.enqueue(async () => this.write());
|
|
55
|
-
}
|
|
56
|
-
async write(data = this.sessionData) {
|
|
57
|
-
const { encryptionKey } = this.config;
|
|
58
|
-
const value = await session.wrapSession(data, encryptionKey);
|
|
59
|
-
this.config.setCookie(this.cookieKey, value, this.cookieOptions);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
exports.CookieStorage = CookieStorage;
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* A simple promise queue that processes tasks sequentially in the order they are enqueued.
|
|
5
|
-
*/
|
|
6
|
-
class PromiseQueue {
|
|
7
|
-
constructor() {
|
|
8
|
-
this.queue = [];
|
|
9
|
-
this.processing = false;
|
|
10
|
-
}
|
|
11
|
-
async enqueue(task) {
|
|
12
|
-
return new Promise((resolve, reject) => {
|
|
13
|
-
// Wrap the task along with its resolve and reject callbacks
|
|
14
|
-
const wrappedTask = async () => {
|
|
15
|
-
try {
|
|
16
|
-
resolve(await task());
|
|
17
|
-
}
|
|
18
|
-
catch (error) {
|
|
19
|
-
reject(error instanceof Error ? error : new Error(String(error)));
|
|
20
|
-
}
|
|
21
|
-
};
|
|
22
|
-
// eslint-disable-next-line @silverhand/fp/no-mutating-methods
|
|
23
|
-
this.queue.push(wrappedTask);
|
|
24
|
-
if (!this.processing) {
|
|
25
|
-
void this.processQueue();
|
|
26
|
-
}
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
async processQueue() {
|
|
30
|
-
if (this.processing) {
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
this.processing = true;
|
|
34
|
-
while (this.queue.length > 0) {
|
|
35
|
-
// eslint-disable-next-line @silverhand/fp/no-mutating-methods
|
|
36
|
-
const task = this.queue.shift();
|
|
37
|
-
if (task) {
|
|
38
|
-
// eslint-disable-next-line no-await-in-loop
|
|
39
|
-
await task();
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
this.processing = false;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
exports.PromiseQueue = PromiseQueue;
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
async function getKeyFromPassword(password, crypto) {
|
|
4
|
-
const encoder = new TextEncoder();
|
|
5
|
-
const data = encoder.encode(password);
|
|
6
|
-
const hash = await crypto.subtle.digest('SHA-256', data);
|
|
7
|
-
// Convert the hash to a hex string
|
|
8
|
-
return Array.from(new Uint8Array(hash))
|
|
9
|
-
.map((byte) => byte.toString(16).padStart(2, '0'))
|
|
10
|
-
.join('');
|
|
11
|
-
}
|
|
12
|
-
async function encrypt(text, password, crypto) {
|
|
13
|
-
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
14
|
-
const encodedPlaintext = new TextEncoder().encode(text);
|
|
15
|
-
const secretKey = await crypto.subtle.importKey('raw', Buffer.from(await getKeyFromPassword(password, crypto), 'hex'), {
|
|
16
|
-
name: 'AES-GCM',
|
|
17
|
-
length: 256,
|
|
18
|
-
}, true, ['encrypt', 'decrypt']);
|
|
19
|
-
const ciphertext = await crypto.subtle.encrypt({
|
|
20
|
-
name: 'AES-GCM',
|
|
21
|
-
iv,
|
|
22
|
-
}, secretKey, encodedPlaintext);
|
|
23
|
-
return {
|
|
24
|
-
ciphertext: Buffer.from(ciphertext).toString('base64'),
|
|
25
|
-
iv: Buffer.from(iv).toString('base64'),
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
async function decrypt(ciphertext, iv, password, crypto) {
|
|
29
|
-
const secretKey = await crypto.subtle.importKey('raw', Buffer.from(await getKeyFromPassword(password, crypto), 'hex'), {
|
|
30
|
-
name: 'AES-GCM',
|
|
31
|
-
length: 256,
|
|
32
|
-
}, true, ['encrypt', 'decrypt']);
|
|
33
|
-
const cleartext = await crypto.subtle.decrypt({
|
|
34
|
-
name: 'AES-GCM',
|
|
35
|
-
iv: Buffer.from(iv, 'base64'),
|
|
36
|
-
}, secretKey, Buffer.from(ciphertext, 'base64'));
|
|
37
|
-
return new TextDecoder().decode(cleartext);
|
|
38
|
-
}
|
|
39
|
-
const unwrapSession = async (cookie, secret) => {
|
|
40
|
-
try {
|
|
41
|
-
const [ciphertext, iv] = cookie.split('.');
|
|
42
|
-
if (!ciphertext || !iv) {
|
|
43
|
-
return {};
|
|
44
|
-
}
|
|
45
|
-
const decrypted = await decrypt(ciphertext, iv, secret, crypto);
|
|
46
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
47
|
-
return JSON.parse(decrypted);
|
|
48
|
-
}
|
|
49
|
-
catch {
|
|
50
|
-
// Ignore invalid session
|
|
51
|
-
}
|
|
52
|
-
return {};
|
|
53
|
-
};
|
|
54
|
-
const wrapSession = async (session, secret) => {
|
|
55
|
-
const { ciphertext, iv } = await encrypt(JSON.stringify(session), secret, crypto);
|
|
56
|
-
return `${ciphertext}.${iv}`;
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
exports.unwrapSession = unwrapSession;
|
|
60
|
-
exports.wrapSession = wrapSession;
|