mirror of
https://github.com/logto-io/logto.git
synced 2025-02-10 21:58:23 -05:00
refactor(core): add OIDCRequestError (#214)
* refactor(core): add OIDCRequestError inplement OIDCRequestError to normalize OIDCProviderError * fix(coer): cr fix code review update
This commit is contained in:
parent
eac74fae40
commit
a5c9bf61d7
4 changed files with 98 additions and 10 deletions
65
packages/core/src/errors/OIDCRequestError/index.ts
Normal file
65
packages/core/src/errors/OIDCRequestError/index.ts
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
import { LogtoErrorCode, LogtoErrorI18nKey } from '@logto/phrases';
|
||||||
|
import { RequestErrorBody } from '@logto/schemas';
|
||||||
|
import decamelize from 'decamelize';
|
||||||
|
import i18next from 'i18next';
|
||||||
|
import pick from 'lodash.pick';
|
||||||
|
import { errors } from 'oidc-provider';
|
||||||
|
|
||||||
|
export default class OIDCRequestError extends Error {
|
||||||
|
code: LogtoErrorCode;
|
||||||
|
status: number;
|
||||||
|
expose: boolean;
|
||||||
|
data: unknown;
|
||||||
|
|
||||||
|
constructor(error: errors.OIDCProviderError) {
|
||||||
|
const {
|
||||||
|
status = 400,
|
||||||
|
message,
|
||||||
|
error_description,
|
||||||
|
error_detail,
|
||||||
|
name,
|
||||||
|
expose = true,
|
||||||
|
constructor,
|
||||||
|
...interpolation
|
||||||
|
} = error;
|
||||||
|
|
||||||
|
super(message);
|
||||||
|
|
||||||
|
switch (constructor) {
|
||||||
|
case errors.InvalidScope:
|
||||||
|
case errors.InvalidTarget:
|
||||||
|
case errors.InvalidToken:
|
||||||
|
case errors.InvalidClientMetadata:
|
||||||
|
case errors.InvalidGrant:
|
||||||
|
this.code = `oidc.${decamelize(name)}` as LogtoErrorCode;
|
||||||
|
this.message = i18next.t<string, LogtoErrorI18nKey>(`errors:${this.code}`, interpolation);
|
||||||
|
break;
|
||||||
|
case errors.SessionNotFound:
|
||||||
|
this.code = 'session.not_found';
|
||||||
|
this.message = i18next.t<string, LogtoErrorI18nKey>(`errors:${this.code}`, interpolation);
|
||||||
|
break;
|
||||||
|
case errors.InsufficientScope:
|
||||||
|
this.code = 'oidc.insufficient_scope';
|
||||||
|
this.message = i18next.t<string, LogtoErrorI18nKey>(`errors:${this.code}`, {
|
||||||
|
scopes: error_detail,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this.code = 'oidc.provider_error';
|
||||||
|
this.message = i18next.t<string, LogtoErrorI18nKey>(`errors:${this.code}`, {
|
||||||
|
message: this.message,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.status = status;
|
||||||
|
this.expose = expose;
|
||||||
|
|
||||||
|
// Original OIDCProvider Error description and details are provided in the data field
|
||||||
|
this.data = { error_description, error_detail };
|
||||||
|
}
|
||||||
|
|
||||||
|
get body(): RequestErrorBody {
|
||||||
|
return pick(this, 'message', 'code', 'data');
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,9 @@
|
||||||
import { LogtoErrorCode } from '@logto/phrases';
|
|
||||||
import { RequestErrorBody } from '@logto/schemas';
|
import { RequestErrorBody } from '@logto/schemas';
|
||||||
import decamelize from 'decamelize';
|
|
||||||
import { Middleware } from 'koa';
|
import { Middleware } from 'koa';
|
||||||
import { errors } from 'oidc-provider';
|
import { errors } from 'oidc-provider';
|
||||||
import { NotFoundError } from 'slonik';
|
import { NotFoundError } from 'slonik';
|
||||||
|
|
||||||
|
import OIDCRequestError from '@/errors/OIDCRequestError';
|
||||||
import RequestError from '@/errors/RequestError';
|
import RequestError from '@/errors/RequestError';
|
||||||
|
|
||||||
export default function koaErrorHandler<StateT, ContextT>(): Middleware<
|
export default function koaErrorHandler<StateT, ContextT>(): Middleware<
|
||||||
|
@ -24,17 +23,14 @@ export default function koaErrorHandler<StateT, ContextT>(): Middleware<
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error instanceof errors.OIDCProviderError) {
|
if (error instanceof errors.OIDCProviderError) {
|
||||||
ctx.status = error.status;
|
const oidcError = new OIDCRequestError(error);
|
||||||
ctx.body = {
|
ctx.status = oidcError.status;
|
||||||
message: error.error_description ?? error.message,
|
ctx.body = oidcError.body;
|
||||||
// Assert error type of OIDCProviderError, code key should all covered in @logto/phrases
|
|
||||||
code: `oidc.${decamelize(error.name)}` as LogtoErrorCode,
|
|
||||||
data: error.error_detail,
|
|
||||||
};
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Slonik Error
|
||||||
if (error instanceof NotFoundError) {
|
if (error instanceof NotFoundError) {
|
||||||
const error = new RequestError({ code: 'entity.not_found', status: 404 });
|
const error = new RequestError({ code: 'entity.not_found', status: 404 });
|
||||||
ctx.status = error.status;
|
ctx.status = error.status;
|
||||||
|
@ -43,6 +39,8 @@ export default function koaErrorHandler<StateT, ContextT>(): Middleware<
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Zod Error
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,8 +27,21 @@ const errors = {
|
||||||
},
|
},
|
||||||
oidc: {
|
oidc: {
|
||||||
aborted: 'The end-user aborted interaction.',
|
aborted: 'The end-user aborted interaction.',
|
||||||
invalid_scope: 'Scope {{scopes}} is not supported.',
|
invalid_scope: 'Scope {{scope}} is not supported.',
|
||||||
invalid_scope_plural: 'Scope {{scopes}} are not supported.',
|
invalid_scope_plural: 'Scope {{scopes}} are not supported.',
|
||||||
|
invalid_token: 'Invalid token provided.',
|
||||||
|
invalid_client_metadata: 'Invalid client metadata provided.',
|
||||||
|
insufficient_scope: 'Access token missing requested scope {{scopes}}.',
|
||||||
|
invalid_request: 'Request is invalid.',
|
||||||
|
invalid_grant: 'Grant request is invalid.',
|
||||||
|
invalid_redirect_uri:
|
||||||
|
"`redirect_uri` did not match any of the client's registered `redirect_uris`.",
|
||||||
|
access_denied: 'Access denied.',
|
||||||
|
invalid_target: 'Invalid resource indicator.',
|
||||||
|
unsupported_grant_type: 'Unsupported `grant_type` requested.',
|
||||||
|
unsupported_response_mode: 'Unsupported `response_mode` requested.',
|
||||||
|
unsupported_response_type: 'Unsupported `response_type` requested.',
|
||||||
|
provider_error: 'OIDC Internal Error: {{message}}.',
|
||||||
},
|
},
|
||||||
user: {
|
user: {
|
||||||
username_exists: 'The username already exists.',
|
username_exists: 'The username already exists.',
|
||||||
|
|
|
@ -31,6 +31,18 @@ const errors = {
|
||||||
aborted: '用户终止了交互。',
|
aborted: '用户终止了交互。',
|
||||||
invalid_scope: '不支持的 scope: {{scopes}}。',
|
invalid_scope: '不支持的 scope: {{scopes}}。',
|
||||||
invalid_scope_plural: '不支持的 scope: {{scopes}}。',
|
invalid_scope_plural: '不支持的 scope: {{scopes}}。',
|
||||||
|
invalid_token: 'token 无效。',
|
||||||
|
invalid_client_metadata: '无效 client metadata。',
|
||||||
|
insufficient_scope: '请求 token 缺少一下权限: {{scopes}}。',
|
||||||
|
invalid_request: '请求失败。',
|
||||||
|
invalid_grant: '授权失败。',
|
||||||
|
invalid_redirect_uri: '无效返回链接, 该 redirect_uri 未被此应用注册。',
|
||||||
|
access_denied: '拒绝访问。',
|
||||||
|
invalid_target: '请求资源无效。',
|
||||||
|
unsupported_grant_type: '不支持的 grant_type。',
|
||||||
|
unsupported_response_mode: '不支持的 response_mode。',
|
||||||
|
unsupported_response_type: '不支持的 response_type。',
|
||||||
|
provider_error: 'OIDC 错误: {{message}}。',
|
||||||
},
|
},
|
||||||
user: {
|
user: {
|
||||||
username_exists: '用户名已存在。',
|
username_exists: '用户名已存在。',
|
||||||
|
|
Loading…
Add table
Reference in a new issue