Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(core): refactor oidc error response query param #6525

Merged
merged 1 commit into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions .changeset/tiny-fishes-bake.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,22 @@
"@logto/core": patch
---

introduce new `error_code_key` query parameter in the `koaErrorHandler`.
introduce new `parse_error` query parameter flag. The value of `parse_error` can only be `false`.

By default, Logto uses `code` as the error code key in the error response body.
For some third-party connectors, like Google, `code` is considered as a reserved OIDC key,
can't be used as the error code key in the error response body. Any oidc error response body containing `code` will be rejected by Google.
By default, Logto returns the parsed error code and error description in all the `RequestError` error responses. This is to ensure the error responses are consistent and easy to understand.

To workaround this, we introduce a new `error_code_key` query parameter to customize the error code key in the error response body.
In the oidc requests, if the `error_code_key` is present in the query string, we will use the value of `error_code_key` as the error code key in the error response body.
However, when integrating Logto with Google OAuth, the error response body containing `code` will be rejected by Google. `code` is considered as a reserved OIDC key, can't be used as the error code key in the error response body.

To workaround this, we add a new `parse_error` query parameter flag. When parsing the OIDC error body, if the `parse_error` is set to false, only oidc error body will be returned.

example:

```curl
curl -X POST "http://localhost:3001/oidc/token?error_code_key=error_code"
curl -X POST "http://localhost:3001/oidc/token?parse_error=false"
```

```json
{
"error_code": "oidc.invalid_grant",
"error": "invalid_grant",
"error_description": "Invalid value for parameter 'code': 'invalid_code'."
}
Expand Down
37 changes: 19 additions & 18 deletions packages/core/src/middleware/koa-oidc-error-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,22 @@ export default function koaOidcErrorHandler<StateT, ContextT>(): Middleware<Stat
ctx.body = errorOut(error);
}

// Parse the `parse_error` from the query string.
// If the `parse_error` is set to false, only returns the original oidc error body.
// For some third-party connectors, like Google, `code` is considered as a reserved OIDC key,
// we can't return the error body containing `code` in the error response.
const queryParametersResult = z
.object({
parse_error: z.literal('false').optional(),
})
.safeParse(ctx.query);

const returnRawError =
queryParametersResult.success && queryParametersResult.data.parse_error === 'false';

// This is the only way we can check if the error is handled by the oidc-provider, because
// oidc-provider doesn't call `renderError` when the request prefers JSON response.
if (ctx.status >= 400 && isObject(ctx.body)) {
if (ctx.status >= 400 && isObject(ctx.body) && !returnRawError) {
const parsed = z
.object({
error: z.string(),
Expand All @@ -113,26 +126,14 @@ export default function koaOidcErrorHandler<StateT, ContextT>(): Middleware<Stat
const code = isSessionNotFound(data.error_description)
? 'session.not_found'
: `oidc.${data.error}`;
const uri = errorUris[data.error];

// Parse the `error_code_key` from the query string.
// This is used to customize the error key in the response body.
// For some third-party connectors, like Google, `code` is considered as a reserved OIDC key,
// can't be used as the error code key in the error response body.
// We add `error_code_key` to the query string to customize the error key in the response body.
const errorKeyQueryResult = z
.object({
error_code_key: z.string().optional(),
})
.safeParse(ctx.query);

const errorKey = errorKeyQueryResult.success
? errorKeyQueryResult.data.error_code_key ?? 'code'
: 'code';
const uri = errorUris[data.error];

ctx.body = {
[errorKey]: code,
message: i18next.t(['errors:' + code, 'errors:oidc.provider_error_fallback'], { code }),
code,
message: i18next.t(['errors:' + code, 'errors:oidc.provider_error_fallback'], {
code,
}),
error_uri: uri,
...ctx.body,
};
Expand Down
Loading