mirror of
https://github.com/logto-io/logto.git
synced 2025-03-10 22:22:45 -05:00
feat(cli): add key type for rotating command (#4314)
This commit is contained in:
parent
1b983c8c89
commit
ae0ef919fb
3 changed files with 43 additions and 7 deletions
5
.changeset/serious-pants-beg.md
Normal file
5
.changeset/serious-pants-beg.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"@logto/cli": minor
|
||||
---
|
||||
|
||||
Add key type option for the command of rotating oidc.privateKeys
|
|
@ -13,7 +13,7 @@ import { createPoolFromConfig } from '../../database.js';
|
|||
import { getRowsByKeys, updateValueByKey } from '../../queries/logto-config.js';
|
||||
import { consoleLog } from '../../utils.js';
|
||||
|
||||
import { generateOidcCookieKey, generateOidcPrivateKey } from './utils.js';
|
||||
import { PrivateKeyType, generateOidcCookieKey, generateOidcPrivateKey } from './utils.js';
|
||||
|
||||
const validKeysDisplay = chalk.green(logtoConfigKeys.join(', '));
|
||||
|
||||
|
@ -41,8 +41,14 @@ const validRotateKeys = Object.freeze([
|
|||
LogtoOidcConfigKey.CookieKeys,
|
||||
] as const);
|
||||
|
||||
const validPrivateKeyTypes = Object.freeze([PrivateKeyType.RSA, PrivateKeyType.EC] as const);
|
||||
|
||||
type ValidateRotateKeyFunction = (key: string) => asserts key is (typeof validRotateKeys)[number];
|
||||
|
||||
type ValidatePrivateKeyTypeFunction = (
|
||||
key: string
|
||||
) => asserts key is (typeof validPrivateKeyTypes)[number];
|
||||
|
||||
const validateRotateKey: ValidateRotateKeyFunction = (key) => {
|
||||
// Using `.includes()` will result a type error
|
||||
// eslint-disable-next-line unicorn/prefer-includes
|
||||
|
@ -53,6 +59,18 @@ const validateRotateKey: ValidateRotateKeyFunction = (key) => {
|
|||
}
|
||||
};
|
||||
|
||||
const validatePrivateKeyType: ValidatePrivateKeyTypeFunction = (key) => {
|
||||
// Using `.includes()` will result a type error
|
||||
// eslint-disable-next-line unicorn/prefer-includes
|
||||
if (!validPrivateKeyTypes.some((element) => element === key)) {
|
||||
consoleLog.fatal(
|
||||
`Invalid private key type ${chalk.red(
|
||||
key
|
||||
)} found, expected one of ${validPrivateKeyTypes.join(', ')}`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const getConfig: CommandModule<unknown, { key: string; keys: string[]; tenantId: string }> = {
|
||||
command: 'get <key> [keys...]',
|
||||
describe: 'Get config value(s) of the given key(s) in Logto database',
|
||||
|
@ -131,7 +149,7 @@ const setConfig: CommandModule<unknown, { key: string; value: string; tenantId:
|
|||
},
|
||||
};
|
||||
|
||||
const rotateConfig: CommandModule<unknown, { key: string; tenantId: string }> = {
|
||||
const rotateConfig: CommandModule<unknown, { key: string; tenantId: string; type: string }> = {
|
||||
command: 'rotate <key>',
|
||||
describe:
|
||||
'Generate a new private or secret key for the given config key and prepend to the key array',
|
||||
|
@ -146,9 +164,17 @@ const rotateConfig: CommandModule<unknown, { key: string; tenantId: string }> =
|
|||
describe: 'The tenant to operate',
|
||||
type: 'string',
|
||||
default: defaultTenantId,
|
||||
})
|
||||
.option('type', {
|
||||
describe: `The key type for ${
|
||||
LogtoOidcConfigKey.PrivateKeys
|
||||
}, one of ${validPrivateKeyTypes.join(', ')}`,
|
||||
type: 'string',
|
||||
default: 'ec',
|
||||
}),
|
||||
handler: async ({ key, tenantId }) => {
|
||||
handler: async ({ key, tenantId, type }) => {
|
||||
validateRotateKey(key);
|
||||
validatePrivateKeyType(type);
|
||||
|
||||
const pool = await createPoolFromConfig();
|
||||
const { rows } = await getRowsByKeys(pool, tenantId, [key]);
|
||||
|
@ -164,7 +190,7 @@ const rotateConfig: CommandModule<unknown, { key: string; tenantId: string }> =
|
|||
// No need for default. It's already exhaustive
|
||||
switch (key) {
|
||||
case LogtoOidcConfigKey.PrivateKeys: {
|
||||
return [await generateOidcPrivateKey(), ...original];
|
||||
return [await generateOidcPrivateKey(type), ...original];
|
||||
}
|
||||
|
||||
case LogtoOidcConfigKey.CookieKeys: {
|
||||
|
|
|
@ -3,8 +3,13 @@ import { promisify } from 'node:util';
|
|||
|
||||
import { generateStandardId } from '@logto/shared';
|
||||
|
||||
export const generateOidcPrivateKey = async (type: 'rsa' | 'ec' = 'ec') => {
|
||||
if (type === 'rsa') {
|
||||
export enum PrivateKeyType {
|
||||
RSA = 'rsa',
|
||||
EC = 'ec',
|
||||
}
|
||||
|
||||
export const generateOidcPrivateKey = async (type: PrivateKeyType = PrivateKeyType.EC) => {
|
||||
if (type === PrivateKeyType.RSA) {
|
||||
const { privateKey } = await promisify(generateKeyPair)('rsa', {
|
||||
modulusLength: 4096,
|
||||
publicKeyEncoding: {
|
||||
|
@ -21,7 +26,7 @@ export const generateOidcPrivateKey = async (type: 'rsa' | 'ec' = 'ec') => {
|
|||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
if (type === 'ec') {
|
||||
if (type === PrivateKeyType.EC) {
|
||||
const { privateKey } = await promisify(generateKeyPair)('ec', {
|
||||
// https://security.stackexchange.com/questions/78621/which-elliptic-curve-should-i-use
|
||||
namedCurve: 'secp384r1',
|
||||
|
|
Loading…
Add table
Reference in a new issue