Skip to content

Commit

Permalink
fix: show OAuth2 error details on error screen (#277)
Browse files Browse the repository at this point in the history
* fix: show OAuth2 error details on error screen

* chore: review
  • Loading branch information
jonas-jonas authored Aug 16, 2023
1 parent 5f67a1f commit 4937e34
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 34 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

123 changes: 91 additions & 32 deletions src/routes/error.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,114 @@
// Copyright © 2022 Ory Corp
// SPDX-License-Identifier: Apache-2.0
import { FlowError, FrontendApi, GenericError } from "@ory/client"
import { UserErrorCard } from "@ory/elements-markup"
import { AxiosError } from "axios"
import { isAxiosError } from "axios"
import {
defaultConfig,
isQuerySet,
RouteCreator,
RouteRegistrator,
defaultConfig,
isQuerySet,
} from "../pkg"

type OAuth2Error = {
error: string
error_description?: string
error_hint?: string
}

function isOAuth2Error(query: qs.ParsedQs): query is OAuth2Error {
return query.error !== undefined
}

/**
* Returns an error object, either from Ory Identities or an OAuth2 error.
*
* @param frontend the frontend SDK
* @param query the query parameters as received from the request
* @returns a FlowError object
*/
async function fetchError(
frontend: FrontendApi,
query: qs.ParsedQs,
): Promise<FlowError> {
// If the error is an OAuth2 error, its details are encoded into the URL.
// We can simply decode them and return them here.
if (isOAuth2Error(query)) {
return {
id: decodeURIComponent(query.error.toString()),
error: {
status: "OAuth2 Error",
id: decodeURIComponent(query.error.toString()),
message: decodeURIComponent(
query.error_description?.toString() || "No description provided",
),
...(query.error_hint
? { hint: decodeURIComponent(query.error_hint.toString()) }
: {}),
code: 599, // Dummy code to trigger the full error screen
},
}
} else if (isQuerySet(query.id)) {
// If the error comes from Ory Identities/Kratos, we need to fetch its details from the backend.
// Once that's done, we can return the error.
const res = await frontend.getFlowError({ id: query.id })

if (res.status !== 200) {
throw new Error("No error was found")
}

return res.data
}

throw new Error("No error was found")
}

// A simple express handler that shows the error screen.
export const createErrorRoute: RouteCreator =
(createHelpers) => (req, res, next) => {
(createHelpers) => async (req, res) => {
res.locals.projectName = "An error occurred"
const { id } = req.query

// Get the SDK
const { frontend, logoUrl } = createHelpers(req, res)

if (!isQuerySet(id)) {
// No error was send, redirecting back to home.
res.redirect("welcome")
return
}
try {
const error = await fetchError(frontend, req.query)

frontend
.getFlowError({ id })
.then(({ data }) => {
res.status(200).render("error", {
card: UserErrorCard({
error: data,
cardImage: logoUrl,
title: "An error occurred",
backUrl: req.header("Referer") || "welcome",
}),
})
res.status(200).render("error", {
card: UserErrorCard({
error,
cardImage: logoUrl,
title: "An error occurred",
backUrl: req.header("Referer") || "welcome",
}),
})
.catch((err: AxiosError) => {
if (!err.response) {
next(err)
return
}

if (err.response.status === 404) {
// The error could not be found, redirect back to home.
res.redirect("welcome")
return
} catch (err) {
let error: FlowError
if (isAxiosError(err)) {
error = err.response?.data.error
} else {
error = {
id: "Failed to fetch error details",
error: err as GenericError,
}
}

next(err)
res.status(200).render("error", {
card: UserErrorCard({
error: {
id: "Failed to fetch error details",
error: {
...error,
code: 500,
},
},
cardImage: logoUrl,
title: "An error occurred",
backUrl: req.header("Referer") || "welcome",
}),
})
return
}
}

export const registerErrorRoute: RouteRegistrator = (
Expand Down

0 comments on commit 4937e34

Please sign in to comment.