@logto/cli 1.10.0 → 1.10.1

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.
@@ -1,10 +1,10 @@
1
- import { defaultTenantId, LogtoOidcConfigKey, logtoConfigGuards, logtoConfigKeys, } from '@logto/schemas';
1
+ import { defaultTenantId, LogtoOidcConfigKey, logtoConfigGuards, logtoConfigKeys, SupportedSigningKeyAlgorithm, } from '@logto/schemas';
2
2
  import { deduplicate, noop } from '@silverhand/essentials';
3
3
  import chalk from 'chalk';
4
4
  import { createPoolFromConfig } from '../../database.js';
5
5
  import { getRowsByKeys, updateValueByKey } from '../../queries/logto-config.js';
6
6
  import { consoleLog } from '../../utils.js';
7
- import { PrivateKeyType, generateOidcCookieKey, generateOidcPrivateKey } from './utils.js';
7
+ import { generateOidcCookieKey, generateOidcPrivateKey } from './utils.js';
8
8
  const validKeysDisplay = chalk.green(logtoConfigKeys.join(', '));
9
9
  const validateKeys = (keys) => {
10
10
  const invalidKey = (Array.isArray(keys) ? keys : [keys]).find(
@@ -19,7 +19,10 @@ const validRotateKeys = Object.freeze([
19
19
  LogtoOidcConfigKey.PrivateKeys,
20
20
  LogtoOidcConfigKey.CookieKeys,
21
21
  ]);
22
- const validPrivateKeyTypes = Object.freeze([PrivateKeyType.RSA, PrivateKeyType.EC]);
22
+ const validPrivateKeyTypes = Object.freeze([
23
+ SupportedSigningKeyAlgorithm.RSA,
24
+ SupportedSigningKeyAlgorithm.EC,
25
+ ]);
23
26
  const validateRotateKey = (key) => {
24
27
  // Using `.includes()` will result a type error
25
28
  // eslint-disable-next-line unicorn/prefer-includes
@@ -118,8 +121,9 @@ const rotateConfig = {
118
121
  default: 'ec',
119
122
  }),
120
123
  handler: async ({ key, tenantId, type }) => {
124
+ const keyType = type.toUpperCase();
121
125
  validateRotateKey(key);
122
- validatePrivateKeyType(type);
126
+ validatePrivateKeyType(keyType);
123
127
  const pool = await createPoolFromConfig();
124
128
  const { rows } = await getRowsByKeys(pool, tenantId, [key]);
125
129
  if (!rows[0]) {
@@ -131,7 +135,7 @@ const rotateConfig = {
131
135
  // No need for default. It's already exhaustive
132
136
  switch (key) {
133
137
  case LogtoOidcConfigKey.PrivateKeys: {
134
- return [await generateOidcPrivateKey(type), ...original];
138
+ return [await generateOidcPrivateKey(keyType), ...original];
135
139
  }
136
140
  case LogtoOidcConfigKey.CookieKeys: {
137
141
  return [generateOidcCookieKey(), ...original];
@@ -1,11 +1,12 @@
1
1
  import { readFile } from 'node:fs/promises';
2
2
  import { LogtoOidcConfigKey, logtoConfigGuards } from '@logto/schemas';
3
+ import { generateStandardId } from '@logto/shared';
3
4
  import { getEnvAsStringArray } from '@silverhand/essentials';
4
5
  import chalk from 'chalk';
5
6
  import { z } from 'zod';
6
7
  import { getRowsByKeys, updateValueByKey } from '../../../queries/logto-config.js';
7
8
  import { consoleLog } from '../../../utils.js';
8
- import { generateOidcCookieKey, generateOidcPrivateKey } from '../utils.js';
9
+ import { buildOidcKeyFromRawString, generateOidcCookieKey, generateOidcPrivateKey, } from '../utils.js';
9
10
  const isBase64FormatPrivateKey = (key) => !key.includes('-');
10
11
  export const seedOidcConfigs = async (pool, tenantId) => {
11
12
  const tenantPrefix = `[${tenantId}]`;
@@ -67,20 +68,16 @@ export const oidcConfigReaders = {
67
68
  const privateKeys = getEnvAsStringArray('OIDC_PRIVATE_KEYS');
68
69
  if (privateKeys.length > 0) {
69
70
  return {
70
- value: privateKeys.map((key) => {
71
- if (isBase64FormatPrivateKey(key)) {
72
- return Buffer.from(key, 'base64').toString('utf8');
73
- }
74
- return key;
75
- }),
71
+ value: privateKeys.map((key) => buildOidcKeyFromRawString(isBase64FormatPrivateKey(key) ? Buffer.from(key, 'base64').toString('utf8') : key)),
76
72
  fromEnv: true,
77
73
  };
78
74
  }
79
75
  // Read keys from files
80
76
  const privateKeyPaths = getEnvAsStringArray('OIDC_PRIVATE_KEY_PATHS');
81
77
  if (privateKeyPaths.length > 0) {
78
+ const privateKeys = await Promise.all(privateKeyPaths.map(async (path) => readFile(path, 'utf8')));
82
79
  return {
83
- value: await Promise.all(privateKeyPaths.map(async (path) => readFile(path, 'utf8'))),
80
+ value: privateKeys.map((key) => buildOidcKeyFromRawString(key)),
84
81
  fromEnv: true,
85
82
  };
86
83
  }
@@ -91,7 +88,11 @@ export const oidcConfigReaders = {
91
88
  },
92
89
  [LogtoOidcConfigKey.CookieKeys]: async () => {
93
90
  const envKey = 'OIDC_COOKIE_KEYS';
94
- const keys = getEnvAsStringArray(envKey);
91
+ const keys = getEnvAsStringArray(envKey).map((key) => ({
92
+ id: generateStandardId(),
93
+ value: key,
94
+ createdAt: Math.floor(Date.now() / 1000),
95
+ }));
95
96
  return { value: keys.length > 0 ? keys : [generateOidcCookieKey()], fromEnv: keys.length > 0 };
96
97
  },
97
98
  };
@@ -1,6 +1,12 @@
1
- export declare enum PrivateKeyType {
2
- RSA = "rsa",
3
- EC = "ec"
4
- }
5
- export declare const generateOidcPrivateKey: (type?: PrivateKeyType) => Promise<string>;
6
- export declare const generateOidcCookieKey: () => string;
1
+ import { type OidcConfigKey, SupportedSigningKeyAlgorithm } from '@logto/schemas';
2
+ export declare const generateOidcPrivateKey: (type?: SupportedSigningKeyAlgorithm) => Promise<OidcConfigKey>;
3
+ export declare const generateOidcCookieKey: () => {
4
+ id: string;
5
+ value: string;
6
+ createdAt: number;
7
+ };
8
+ export declare const buildOidcKeyFromRawString: (raw: string) => {
9
+ id: string;
10
+ value: string;
11
+ createdAt: number;
12
+ };
@@ -1,13 +1,9 @@
1
1
  import { generateKeyPair } from 'node:crypto';
2
2
  import { promisify } from 'node:util';
3
- import { generateStandardSecret } from '@logto/shared';
4
- export var PrivateKeyType;
5
- (function (PrivateKeyType) {
6
- PrivateKeyType["RSA"] = "rsa";
7
- PrivateKeyType["EC"] = "ec";
8
- })(PrivateKeyType || (PrivateKeyType = {}));
9
- export const generateOidcPrivateKey = async (type = PrivateKeyType.EC) => {
10
- if (type === PrivateKeyType.RSA) {
3
+ import { SupportedSigningKeyAlgorithm } from '@logto/schemas';
4
+ import { generateStandardId, generateStandardSecret } from '@logto/shared';
5
+ export const generateOidcPrivateKey = async (type = SupportedSigningKeyAlgorithm.EC) => {
6
+ if (type === SupportedSigningKeyAlgorithm.RSA) {
11
7
  const { privateKey } = await promisify(generateKeyPair)('rsa', {
12
8
  modulusLength: 4096,
13
9
  publicKeyEncoding: {
@@ -19,10 +15,10 @@ export const generateOidcPrivateKey = async (type = PrivateKeyType.EC) => {
19
15
  format: 'pem',
20
16
  },
21
17
  });
22
- return privateKey;
18
+ return buildOidcKeyFromRawString(privateKey);
23
19
  }
24
20
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
25
- if (type === PrivateKeyType.EC) {
21
+ if (type === SupportedSigningKeyAlgorithm.EC) {
26
22
  const { privateKey } = await promisify(generateKeyPair)('ec', {
27
23
  // https://security.stackexchange.com/questions/78621/which-elliptic-curve-should-i-use
28
24
  namedCurve: 'secp384r1',
@@ -35,8 +31,13 @@ export const generateOidcPrivateKey = async (type = PrivateKeyType.EC) => {
35
31
  format: 'pem',
36
32
  },
37
33
  });
38
- return privateKey;
34
+ return buildOidcKeyFromRawString(privateKey);
39
35
  }
40
36
  throw new Error(`Unsupported private key ${String(type)}`);
41
37
  };
42
- export const generateOidcCookieKey = () => generateStandardSecret();
38
+ export const generateOidcCookieKey = () => buildOidcKeyFromRawString(generateStandardSecret());
39
+ export const buildOidcKeyFromRawString = (raw) => ({
40
+ id: generateStandardId(),
41
+ value: raw,
42
+ createdAt: Math.floor(Date.now() / 1000),
43
+ });
@@ -1,2 +1,3 @@
1
+ import type { BaseRoutes, Router } from '@withtyped/server';
1
2
  import type { ConnectorFactory, ConnectorPackage } from './types.js';
2
- export declare const loadConnectorFactories: (connectorPackages: ConnectorPackage[], ignoreVersionMismatch: boolean) => Promise<ConnectorFactory[]>;
3
+ export declare const loadConnectorFactories: (connectorPackages: ConnectorPackage[], ignoreVersionMismatch: boolean) => Promise<ConnectorFactory<Router<any, BaseRoutes, string>>[]>;
@@ -3,7 +3,7 @@ import { consoleLog } from '../utils.js';
3
3
  import { notImplemented } from './consts.js';
4
4
  import { loadConnector } from './loader.js';
5
5
  import { parseMetadata, validateConnectorModule } from './utils.js';
6
- // eslint-disable-next-line @silverhand/fp/no-let
6
+ // eslint-disable-next-line @silverhand/fp/no-let, @typescript-eslint/no-explicit-any
7
7
  let cachedConnectorFactories;
8
8
  export const loadConnectorFactories = async (connectorPackages, ignoreVersionMismatch) => {
9
9
  if (cachedConnectorFactories) {
@@ -17,6 +17,7 @@ export const loadConnectorFactories = async (connectorPackages, ignoreVersionMis
17
17
  return {
18
18
  metadata: await parseMetadata(rawConnector.metadata, packagePath),
19
19
  type: rawConnector.type,
20
+ configGuard: rawConnector.configGuard,
20
21
  createConnector,
21
22
  path: packagePath,
22
23
  };
@@ -30,6 +31,8 @@ export const loadConnectorFactories = async (connectorPackages, ignoreVersionMis
30
31
  }
31
32
  }));
32
33
  // eslint-disable-next-line @silverhand/fp/no-mutation
33
- cachedConnectorFactories = connectorFactories.filter((connectorFactory) => connectorFactory !== undefined);
34
+ cachedConnectorFactories = connectorFactories.filter(
35
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
36
+ (connectorFactory) => connectorFactory !== undefined);
34
37
  return cachedConnectorFactories;
35
38
  };
@@ -1,9 +1,10 @@
1
1
  import type { AllConnector, CreateConnector } from '@logto/connector-kit';
2
+ import type { BaseRoutes, Router } from '@withtyped/server';
2
3
  /**
3
4
  * Dynamic loaded connector type.
4
5
  */
5
- export type ConnectorFactory<T extends AllConnector = AllConnector> = Pick<T, 'type' | 'metadata'> & {
6
- createConnector: CreateConnector<T>;
6
+ export type ConnectorFactory<T extends Router<any, BaseRoutes, string>, U extends AllConnector = AllConnector> = Pick<U, 'type' | 'metadata' | 'configGuard'> & {
7
+ createConnector: CreateConnector<U, T>;
7
8
  path: string;
8
9
  };
9
10
  export type ConnectorPackage = {
@@ -1,11 +1,12 @@
1
1
  import type { AllConnector, BaseConnector, ConnectorMetadata, GetConnectorConfig, GetCloudServiceClient } from '@logto/connector-kit';
2
2
  import { ConnectorType } from '@logto/connector-kit';
3
+ import type { BaseRoutes, Router } from '@withtyped/server';
3
4
  import type { ConnectorFactory } from './types.js';
4
5
  export declare function validateConnectorModule(connector: Partial<BaseConnector<ConnectorType>>): asserts connector is BaseConnector<ConnectorType>;
5
6
  export declare const readUrl: (url: string, baseUrl: string, type: 'text' | 'image') => Promise<string>;
6
7
  export declare const parseMetadata: (metadata: AllConnector['metadata'], packagePath: string) => Promise<AllConnector['metadata']>;
7
- export declare const buildRawConnector: <T extends AllConnector = AllConnector>(connectorFactory: ConnectorFactory<T>, getConnectorConfig?: GetConnectorConfig, getCloudServiceClient?: GetCloudServiceClient) => Promise<{
8
- rawConnector: T;
8
+ export declare const buildRawConnector: <T extends Router<any, BaseRoutes, string>, U extends AllConnector = AllConnector>(connectorFactory: ConnectorFactory<T, U>, getConnectorConfig?: GetConnectorConfig, getCloudServiceClient?: GetCloudServiceClient<T> | undefined) => Promise<{
9
+ rawConnector: U;
9
10
  rawMetadata: ConnectorMetadata;
10
11
  }>;
11
12
  export declare const isKeyInObject: <Key extends string>(object: unknown, key: Key) => object is Record<string, unknown> & Record<Key, unknown>;
package/lib/database.d.ts CHANGED
@@ -9,4 +9,4 @@ export declare const createPoolFromConfig: () => Promise<import("slonik").Databa
9
9
  * @returns A new database pool with the database URL in config.
10
10
  */
11
11
  export declare const createPoolAndDatabaseIfNeeded: () => Promise<import("slonik").DatabasePool>;
12
- export declare const insertInto: <T extends SchemaLike>(object: T, table: string) => import("slonik").TaggedTemplateLiteralInvocation<import("slonik").QueryResultRow>;
12
+ export declare const insertInto: <T extends SchemaLike<string>>(object: T, table: string) => import("slonik").TaggedTemplateLiteralInvocation<import("slonik").QueryResultRow>;
@@ -73,6 +73,7 @@ export declare const packageJson: {
73
73
  devDependencies: {
74
74
  "@silverhand/eslint-config": string;
75
75
  "@silverhand/ts-config": string;
76
+ "@withtyped/server": string;
76
77
  "@types/inquirer": string;
77
78
  "@types/jest": string;
78
79
  "@types/node": string;
@@ -1,6 +1,6 @@
1
1
  export const packageJson = {
2
2
  "name": "@logto/cli",
3
- "version": "1.10.0",
3
+ "version": "1.10.1",
4
4
  "description": "Logto CLI.",
5
5
  "author": "Silverhand Inc. <contact@silverhand.io>",
6
6
  "homepage": "https://github.com/logto-io/logto#readme",
@@ -43,12 +43,12 @@ export const packageJson = {
43
43
  "url": "https://github.com/logto-io/logto/issues"
44
44
  },
45
45
  "dependencies": {
46
- "@logto/connector-kit": "workspace:^1.1.1",
46
+ "@logto/connector-kit": "workspace:^2.0.0",
47
47
  "@logto/core-kit": "workspace:^2.2.0",
48
48
  "@logto/language-kit": "workspace:^1.0.0",
49
- "@logto/phrases": "workspace:^1.5.0",
49
+ "@logto/phrases": "workspace:^1.6.0",
50
50
  "@logto/phrases-experience": "workspace:^1.3.1",
51
- "@logto/schemas": "workspace:1.10.0",
51
+ "@logto/schemas": "workspace:1.10.1",
52
52
  "@logto/shared": "workspace:^3.0.0",
53
53
  "@silverhand/essentials": "^2.8.4",
54
54
  "chalk": "^5.0.0",
@@ -57,7 +57,7 @@ export const packageJson = {
57
57
  "got": "^13.0.0",
58
58
  "hpagent": "^1.2.0",
59
59
  "inquirer": "^9.0.0",
60
- "nanoid": "^4.0.0",
60
+ "nanoid": "^5.0.1",
61
61
  "ora": "^6.1.2",
62
62
  "p-limit": "^4.0.0",
63
63
  "p-queue": "^7.3.4",
@@ -71,11 +71,12 @@ export const packageJson = {
71
71
  "tar": "^6.1.11",
72
72
  "typescript": "^5.0.0",
73
73
  "yargs": "^17.6.0",
74
- "zod": "^3.20.2"
74
+ "zod": "^3.22.3"
75
75
  },
76
76
  "devDependencies": {
77
77
  "@silverhand/eslint-config": "4.0.1",
78
78
  "@silverhand/ts-config": "4.0.0",
79
+ "@withtyped/server": "^0.12.9",
79
80
  "@types/inquirer": "^9.0.0",
80
81
  "@types/jest": "^29.4.0",
81
82
  "@types/node": "^18.11.18",
@@ -85,9 +86,9 @@ export const packageJson = {
85
86
  "@types/yargs": "^17.0.13",
86
87
  "eslint": "^8.44.0",
87
88
  "jest": "^29.5.0",
88
- "lint-staged": "^14.0.0",
89
+ "lint-staged": "^15.0.0",
89
90
  "prettier": "^3.0.0",
90
- "sinon": "^16.0.0"
91
+ "sinon": "^17.0.0"
91
92
  },
92
93
  "eslintConfig": {
93
94
  "extends": "@silverhand",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logto/cli",
3
- "version": "1.10.0",
3
+ "version": "1.10.1",
4
4
  "description": "Logto CLI.",
5
5
  "author": "Silverhand Inc. <contact@silverhand.io>",
6
6
  "homepage": "https://github.com/logto-io/logto#readme",
@@ -28,12 +28,12 @@
28
28
  "url": "https://github.com/logto-io/logto/issues"
29
29
  },
30
30
  "dependencies": {
31
- "@logto/connector-kit": "^1.1.1",
31
+ "@logto/connector-kit": "^2.0.0",
32
32
  "@logto/core-kit": "^2.2.0",
33
33
  "@logto/language-kit": "^1.0.0",
34
- "@logto/phrases": "^1.5.0",
34
+ "@logto/phrases": "^1.6.0",
35
35
  "@logto/phrases-experience": "^1.3.1",
36
- "@logto/schemas": "1.10.0",
36
+ "@logto/schemas": "1.10.1",
37
37
  "@logto/shared": "^3.0.0",
38
38
  "@silverhand/essentials": "^2.8.4",
39
39
  "chalk": "^5.0.0",
@@ -42,7 +42,7 @@
42
42
  "got": "^13.0.0",
43
43
  "hpagent": "^1.2.0",
44
44
  "inquirer": "^9.0.0",
45
- "nanoid": "^4.0.0",
45
+ "nanoid": "^5.0.1",
46
46
  "ora": "^6.1.2",
47
47
  "p-limit": "^4.0.0",
48
48
  "p-queue": "^7.3.4",
@@ -56,11 +56,12 @@
56
56
  "tar": "^6.1.11",
57
57
  "typescript": "^5.0.0",
58
58
  "yargs": "^17.6.0",
59
- "zod": "^3.20.2"
59
+ "zod": "^3.22.3"
60
60
  },
61
61
  "devDependencies": {
62
62
  "@silverhand/eslint-config": "4.0.1",
63
63
  "@silverhand/ts-config": "4.0.0",
64
+ "@withtyped/server": "^0.12.9",
64
65
  "@types/inquirer": "^9.0.0",
65
66
  "@types/jest": "^29.4.0",
66
67
  "@types/node": "^18.11.18",
@@ -70,9 +71,9 @@
70
71
  "@types/yargs": "^17.0.13",
71
72
  "eslint": "^8.44.0",
72
73
  "jest": "^29.5.0",
73
- "lint-staged": "^14.0.0",
74
+ "lint-staged": "^15.0.0",
74
75
  "prettier": "^3.0.0",
75
- "sinon": "^16.0.0"
76
+ "sinon": "^17.0.0"
76
77
  },
77
78
  "eslintConfig": {
78
79
  "extends": "@silverhand",