0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-01-27 21:39:16 -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:
simeng-li 2022-02-08 15:31:16 +08:00 committed by GitHub
parent eac74fae40
commit a5c9bf61d7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 98 additions and 10 deletions

View 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');
}
}

View file

@ -1,10 +1,9 @@
import { LogtoErrorCode } from '@logto/phrases';
import { RequestErrorBody } from '@logto/schemas';
import decamelize from 'decamelize';
import { Middleware } from 'koa';
import { errors } from 'oidc-provider';
import { NotFoundError } from 'slonik';
import OIDCRequestError from '@/errors/OIDCRequestError';
import RequestError from '@/errors/RequestError';
export default function koaErrorHandler<StateT, ContextT>(): Middleware<
@ -24,17 +23,14 @@ export default function koaErrorHandler<StateT, ContextT>(): Middleware<
}
if (error instanceof errors.OIDCProviderError) {
ctx.status = error.status;
ctx.body = {
message: error.error_description ?? error.message,
// Assert error type of OIDCProviderError, code key should all covered in @logto/phrases
code: `oidc.${decamelize(error.name)}` as LogtoErrorCode,
data: error.error_detail,
};
const oidcError = new OIDCRequestError(error);
ctx.status = oidcError.status;
ctx.body = oidcError.body;
return;
}
// TODO: Slonik Error
if (error instanceof NotFoundError) {
const error = new RequestError({ code: 'entity.not_found', status: 404 });
ctx.status = error.status;
@ -43,6 +39,8 @@ export default function koaErrorHandler<StateT, ContextT>(): Middleware<
return;
}
// TODO: Zod Error
throw error;
}
};

View file

@ -27,8 +27,21 @@ const errors = {
},
oidc: {
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_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: {
username_exists: 'The username already exists.',

View file

@ -31,6 +31,18 @@ const errors = {
aborted: '用户终止了交互。',
invalid_scope: '不支持的 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: {
username_exists: '用户名已存在。',