@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 CHANGED
@@ -1,23 +1,19 @@
1
1
  {
2
2
  "name": "@logto/node",
3
- "version": "2.5.8",
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.8.7",
31
+ "@silverhand/essentials": "^2.9.2",
36
32
  "js-base64": "^3.7.4",
37
- "@logto/client": "^2.8.1"
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": "^20.11.19",
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": "node test.cjs && vitest --silent --coverage"
61
+ "test:coverage": "vitest --silent --coverage"
66
62
  }
67
63
  }
@@ -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;
@@ -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;
@@ -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;