diff --git a/packages/core/src/app/init.ts b/packages/core/src/app/init.ts index 4608672c3..8fdae5037 100644 --- a/packages/core/src/app/init.ts +++ b/packages/core/src/app/init.ts @@ -9,7 +9,7 @@ import mount from 'koa-mount'; import envSet, { MountedApps } from '@/env-set'; import koaCheckDemoApp from '@/middleware/koa-check-demo-app'; -import koaConnectorErrorHandler from '@/middleware/koa-connector-error-handle'; +import koaConnectorErrorHandler from '@/middleware/koa-connector-error-handler'; import koaErrorHandler from '@/middleware/koa-error-handler'; import koaI18next from '@/middleware/koa-i18next'; import koaLog from '@/middleware/koa-log'; diff --git a/packages/core/src/middleware/koa-connector-error-handler.test.ts b/packages/core/src/middleware/koa-connector-error-handler.test.ts new file mode 100644 index 000000000..8749afdb7 --- /dev/null +++ b/packages/core/src/middleware/koa-connector-error-handler.test.ts @@ -0,0 +1,169 @@ +import { ConnectorError, ConnectorErrorCodes } from '@logto/connector-types'; + +import RequestError from '@/errors/RequestError'; +import { createContextWithRouteParameters } from '@/utils/test-utils'; + +import koaConnectorErrorHandler from './koa-connector-error-handler'; + +describe('koaConnectorErrorHandler middleware', () => { + const next = jest.fn(); + const ctx = createContextWithRouteParameters(); + + it('should throw no errors if no errors are caught', async () => { + await expect(koaConnectorErrorHandler()(ctx, next)).resolves.not.toThrow(); + }); + + it('should throw original error if error type is not ConnectorError', async () => { + const error = new Error('err'); + + next.mockImplementationOnce(() => { + throw error; + }); + + await expect(koaConnectorErrorHandler()(ctx, next)).rejects.toMatchError(error); + }); + + it('Insufficient Request Parameters', async () => { + const message = 'Mock Insufficient Request Parameters'; + const error = new ConnectorError(ConnectorErrorCodes.InsufficientRequestParameters, message); + next.mockImplementationOnce(() => { + throw error; + }); + + await expect(koaConnectorErrorHandler()(ctx, next)).rejects.toMatchError( + new RequestError( + { + code: 'connector.insufficient_request_parameters', + status: 400, + }, + { message } + ) + ); + }); + + it('Invalid Config', async () => { + const message = 'Mock Invalid Config'; + const error = new ConnectorError(ConnectorErrorCodes.InvalidConfig, message); + next.mockImplementationOnce(() => { + throw error; + }); + + await expect(koaConnectorErrorHandler()(ctx, next)).rejects.toMatchError( + new RequestError( + { + code: 'connector.invalid_config', + status: 400, + }, + { message } + ) + ); + }); + + it('Invalid Response', async () => { + const message = 'Mock Invalid Response'; + const error = new ConnectorError(ConnectorErrorCodes.InvalidResponse, message); + next.mockImplementationOnce(() => { + throw error; + }); + + await expect(koaConnectorErrorHandler()(ctx, next)).rejects.toMatchError( + new RequestError( + { + code: 'connector.invalid_response', + status: 400, + }, + { message } + ) + ); + }); + + it('Template Not Found', async () => { + const message = 'Mock Template Not Found'; + const error = new ConnectorError(ConnectorErrorCodes.TemplateNotFound, message); + next.mockImplementationOnce(() => { + throw error; + }); + + await expect(koaConnectorErrorHandler()(ctx, next)).rejects.toMatchError( + new RequestError( + { + code: 'connector.template_not_found', + status: 500, + }, + { message } + ) + ); + }); + + it('Social Auth Code Invalid', async () => { + const message = 'Mock Social Auth Code Invalid'; + const error = new ConnectorError(ConnectorErrorCodes.SocialAuthCodeInvalid, message); + next.mockImplementationOnce(() => { + throw error; + }); + + await expect(koaConnectorErrorHandler()(ctx, next)).rejects.toMatchError( + new RequestError( + { + code: 'connector.oauth_code_invalid', + status: 401, + }, + { message } + ) + ); + }); + + it('Social Access Token Invalid', async () => { + const message = 'Mock Social Access Token Invalid'; + const error = new ConnectorError(ConnectorErrorCodes.SocialAccessTokenInvalid, message); + next.mockImplementationOnce(() => { + throw error; + }); + + await expect(koaConnectorErrorHandler()(ctx, next)).rejects.toMatchError( + new RequestError( + { + code: 'connector.invalid_access_token', + status: 401, + }, + { message } + ) + ); + }); + + it('Social Id Token Invalid', async () => { + const message = 'Mock Social Id Token Invalid'; + const error = new ConnectorError(ConnectorErrorCodes.SocialIdTokenInvalid, message); + next.mockImplementationOnce(() => { + throw error; + }); + + await expect(koaConnectorErrorHandler()(ctx, next)).rejects.toMatchError( + new RequestError( + { + code: 'connector.invalid_id_token', + status: 401, + }, + { message } + ) + ); + }); + + it('General connector errors', async () => { + const message = 'Mock General connector errors'; + const error = new ConnectorError(ConnectorErrorCodes.General, message); + next.mockImplementationOnce(() => { + throw error; + }); + + await expect(koaConnectorErrorHandler()(ctx, next)).rejects.toMatchError( + new RequestError( + { + code: 'connector.general', + status: 500, + }, + { message } + ) + ); + }); +}); diff --git a/packages/core/src/middleware/koa-connector-error-handle.ts b/packages/core/src/middleware/koa-connector-error-handler.ts similarity index 69% rename from packages/core/src/middleware/koa-connector-error-handle.ts rename to packages/core/src/middleware/koa-connector-error-handler.ts index def135165..bfa3feb40 100644 --- a/packages/core/src/middleware/koa-connector-error-handle.ts +++ b/packages/core/src/middleware/koa-connector-error-handler.ts @@ -18,6 +18,14 @@ export default function koaConnectorErrorHandler(): Middleware const data = { message }; switch (code) { + case ConnectorErrorCodes.InsufficientRequestParameters: + throw new RequestError( + { + code: 'connector.insufficient_request_parameters', + status: 400, + }, + data + ); case ConnectorErrorCodes.InvalidConfig: throw new RequestError( { @@ -26,11 +34,19 @@ export default function koaConnectorErrorHandler(): Middleware }, data ); - case ConnectorErrorCodes.SocialAccessTokenInvalid: + case ConnectorErrorCodes.InvalidResponse: throw new RequestError( { - code: 'connector.access_token_invalid', - status: 401, + code: 'connector.invalid_response', + status: 400, + }, + data + ); + case ConnectorErrorCodes.TemplateNotFound: + throw new RequestError( + { + code: 'connector.template_not_found', + status: 500, }, data ); @@ -42,11 +58,19 @@ export default function koaConnectorErrorHandler(): Middleware }, data ); - case ConnectorErrorCodes.TemplateNotFound: + case ConnectorErrorCodes.SocialAccessTokenInvalid: throw new RequestError( { - code: 'connector.template_not_found', - status: 500, + code: 'connector.invalid_access_token', + status: 401, + }, + data + ); + case ConnectorErrorCodes.SocialIdTokenInvalid: + throw new RequestError( + { + code: 'connector.invalid_id_token', + status: 401, }, data ); diff --git a/packages/core/src/middleware/koa-oidc-error-handler.test.ts b/packages/core/src/middleware/koa-oidc-error-handler.test.ts index 4bbc0b601..632518083 100644 --- a/packages/core/src/middleware/koa-oidc-error-handler.test.ts +++ b/packages/core/src/middleware/koa-oidc-error-handler.test.ts @@ -9,11 +9,11 @@ describe('koaOIDCErrorHandler middleware', () => { const next = jest.fn(); const ctx = createContextWithRouteParameters(); - it('should throw no errors if no errors are catched', async () => { + it('should throw no errors if no errors are caught', async () => { await expect(koaOIDCErrorHandler()(ctx, next)).resolves.not.toThrow(); }); - it('should throw original error if error type is no OIDCProviderError', async () => { + it('should throw original error if error type is not OIDCProviderError', async () => { const error = new Error('err'); next.mockImplementationOnce(() => { diff --git a/packages/core/src/middleware/koa-slonik-error-handler.test.ts b/packages/core/src/middleware/koa-slonik-error-handler.test.ts index c83276ac2..e276f9081 100644 --- a/packages/core/src/middleware/koa-slonik-error-handler.test.ts +++ b/packages/core/src/middleware/koa-slonik-error-handler.test.ts @@ -11,7 +11,7 @@ describe('koaSlonikErrorHandler middleware', () => { const next = jest.fn(); const ctx = createContextWithRouteParameters(); - it('should throw no errors if no errors are catched', async () => { + it('should throw no errors if no errors are caught', async () => { await expect(koaSlonikErrorHandler()(ctx, next)).resolves.not.toThrow(); }); diff --git a/packages/phrases/src/locales/en.ts b/packages/phrases/src/locales/en.ts index d9eb5f929..4a4b1fbcd 100644 --- a/packages/phrases/src/locales/en.ts +++ b/packages/phrases/src/locales/en.ts @@ -639,9 +639,13 @@ const errors = { general: 'An unexpected error occurred in connector.', not_found: 'Cannot find any available connector for type: {{type}}.', not_enabled: 'The connector is not enabled.', + insufficient_request_parameters: 'The request might miss some input parameters.', invalid_config: "The connector's config is invalid.", + invalid_response: "The connector's response is invalid.", template_not_found: 'Unable to find correct template in connector config.', - access_token_invalid: "Connector's access token is invalid.", + invalid_access_token: "The connector's access token is invalid.", + invalid_auth_code: "The connector's auth code is invalid.", + invalid_id_token: "The connector's id token is invalid.", oauth_code_invalid: 'Unable to get access token, please check authorization code.', more_than_one_sms: 'The number of SMS connectors is larger then 1.', more_than_one_email: 'The number of Email connectors is larger then 1.', diff --git a/packages/phrases/src/locales/zh-cn.ts b/packages/phrases/src/locales/zh-cn.ts index 7733b917d..5dbfe3425 100644 --- a/packages/phrases/src/locales/zh-cn.ts +++ b/packages/phrases/src/locales/zh-cn.ts @@ -621,9 +621,13 @@ const errors = { general: '连接器发生未知错误。', not_found: '找不到可用的 {{type}} 类型的连接器。', not_enabled: '连接器尚未启用。', + insufficient_request_parameters: '请求参数缺失。', invalid_config: '连接器配置错误。', + invalid_response: '连接器错误响应。', template_not_found: '无法从连接器配置中找到对应的模板。', - access_token_invalid: '当前连接器的 access_token 无效。', + invalid_access_token: '当前连接器的 access_token 无效。', + invalid_auth_code: '当前连接器的授权码无效。', + invalid_id_token: '当前连接器的 id_token 无效。', oauth_code_invalid: '无法获取 access_token,请检查授权 code 是否有效。', more_than_one_sms: '同时存在超过 1 个短信连接器。', more_than_one_email: '同时存在超过 1 个邮件连接器。',