show proper login error details (fixes #35)
This commit is contained in:
parent
95bbd633c8
commit
1d00b53aba
|
@ -10,6 +10,7 @@ import { ChatContext } from "../contexts/ChatContext";
|
|||
|
||||
import "react-toastify/dist/ReactToastify.css";
|
||||
import { ToastContainer } from "react-toastify";
|
||||
import { AuthErrors } from "./AuthErrors";
|
||||
|
||||
const Chat = lazy(() => import("./Chat/Chat"));
|
||||
|
||||
|
@ -33,6 +34,7 @@ const AppInner = () => {
|
|||
|
||||
<DebugModal />
|
||||
<SettingsSidebar />
|
||||
<AuthErrors />
|
||||
|
||||
<ToastContainer position="top-left" />
|
||||
</>
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
import {
|
||||
Button,
|
||||
Link,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
} from "@nextui-org/react";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
const Params = {
|
||||
TYPE: "auth_type",
|
||||
ERROR: "auth_error",
|
||||
ERROR_DESC: "auth_error_desc",
|
||||
CAN_RETRY: "auth_retry",
|
||||
};
|
||||
|
||||
/**
|
||||
* Show popups that detail auth error messages
|
||||
* @returns
|
||||
*/
|
||||
export const AuthErrors = () => {
|
||||
const [params, setParams] = useState(
|
||||
new URLSearchParams(window.location.search)
|
||||
);
|
||||
|
||||
const onClose = () => {
|
||||
const url = new URL(window.location.href);
|
||||
url.search = "";
|
||||
// window.history.replaceState({}, "", url.toString());
|
||||
|
||||
setParams(new URLSearchParams(window.location.search));
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<RPError
|
||||
isOpen={params.get(Params.TYPE) === "rp"}
|
||||
onClose={onClose}
|
||||
params={params}
|
||||
/>
|
||||
<OPError
|
||||
isOpen={params.get(Params.TYPE) === "op"}
|
||||
onClose={onClose}
|
||||
params={params}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* This is for RP errors, which can be triggered by modifying data sent in callbacks
|
||||
*
|
||||
* These errors can typically be retried
|
||||
*
|
||||
* @param param0
|
||||
* @returns
|
||||
*/
|
||||
const RPError = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
params,
|
||||
}: {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
params: URLSearchParams;
|
||||
}) => {
|
||||
return (
|
||||
<Modal isOpen={isOpen} onOpenChange={onClose} isDismissable={false}>
|
||||
<ModalContent>
|
||||
{(onClose) => (
|
||||
<>
|
||||
<ModalHeader>Login Error</ModalHeader>
|
||||
<ModalBody>
|
||||
<b>Error:</b> {params.get(Params.ERROR)}
|
||||
<br />
|
||||
<br />
|
||||
<b>Error Description:</b> {params.get(Params.ERROR_DESC)}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button color="primary" href="/api/login" as={Link}>
|
||||
Login
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</>
|
||||
)}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* This is for OP errors, these might not be retryable
|
||||
* @param param0
|
||||
* @returns
|
||||
*/
|
||||
const OPError = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
params,
|
||||
}: {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
params: URLSearchParams;
|
||||
}) => {
|
||||
const canRetry = params.has(Params.CAN_RETRY);
|
||||
const [error, setError] = useState(params.get(Params.ERROR));
|
||||
const [errorDesc, setErrorDesc] = useState(params.get(Params.ERROR_DESC));
|
||||
|
||||
useEffect(() => {
|
||||
switch (params.get(Params.ERROR)) {
|
||||
case "invalid_grant":
|
||||
setErrorDesc("Invalid token, try logging in again");
|
||||
break;
|
||||
}
|
||||
}, [params]);
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onOpenChange={onClose} isDismissable={false}>
|
||||
<ModalContent>
|
||||
{(onClose) => (
|
||||
<>
|
||||
<ModalHeader>Login Error</ModalHeader>
|
||||
<ModalBody>
|
||||
<b>Error:</b> {error}
|
||||
<br />
|
||||
<br />
|
||||
<b>Error Description:</b> {errorDesc}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
{canRetry && (
|
||||
<Button color="primary" href="/api/login" as={Link}>
|
||||
Login
|
||||
</Button>
|
||||
)}
|
||||
</ModalFooter>
|
||||
</>
|
||||
)}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
|
@ -4,6 +4,22 @@ import { OpenID } from "../lib/oidc";
|
|||
import { TokenSet, errors as OIDC_Errors } from "openid-client";
|
||||
import { Logger } from "../lib/Logger";
|
||||
|
||||
const ClientParams = {
|
||||
TYPE: "auth_type",
|
||||
ERROR: "auth_error",
|
||||
ERROR_DESC: "auth_error_desc",
|
||||
CAN_RETRY: "auth_retry",
|
||||
};
|
||||
|
||||
const buildQuery = (obj: { [k in keyof typeof ClientParams]?: string }) => {
|
||||
const params = new URLSearchParams();
|
||||
for (const [k, v] of Object.entries(obj)) {
|
||||
const k_: keyof typeof ClientParams = k as any;
|
||||
params.set(ClientParams[k_], v);
|
||||
}
|
||||
return "?" + params.toString();
|
||||
};
|
||||
|
||||
const app = Router();
|
||||
|
||||
app.get("/login", (req, res) => {
|
||||
|
@ -30,11 +46,14 @@ app.get("/callback", async (req, res) => {
|
|||
if (e instanceof OIDC_Errors.RPError) {
|
||||
// client error
|
||||
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: e.name,
|
||||
error_description: e.message,
|
||||
});
|
||||
res.redirect(
|
||||
"/" +
|
||||
buildQuery({
|
||||
TYPE: "rp",
|
||||
ERROR: e.name,
|
||||
ERROR_DESC: e.message,
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -47,34 +66,46 @@ app.get("/callback", async (req, res) => {
|
|||
Logger.error(
|
||||
"OpenID is improperly configured. Cannot exchange tokens, do I have valid credetials?"
|
||||
);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: "internal server error",
|
||||
error_description: "I'm misconfigured.",
|
||||
});
|
||||
res.redirect(
|
||||
"/" +
|
||||
buildQuery({
|
||||
TYPE: "op",
|
||||
ERROR: "Internal Server Error",
|
||||
ERROR_DESC: "I'm misconfigured.",
|
||||
})
|
||||
);
|
||||
return;
|
||||
case "invalid_grant":
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: "invalid_grant",
|
||||
error_description: "retry /api/login",
|
||||
});
|
||||
res.redirect(
|
||||
"/" +
|
||||
buildQuery({
|
||||
TYPE: "op",
|
||||
ERROR: "invalid_grant",
|
||||
CAN_RETRY: "true",
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: e.error,
|
||||
error_description: e.error_description,
|
||||
});
|
||||
res.redirect(
|
||||
"/" +
|
||||
buildQuery({
|
||||
TYPE: "op",
|
||||
ERROR: e.error,
|
||||
ERROR_DESC: e.error_description,
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: "unknown error",
|
||||
error_description: "report this",
|
||||
});
|
||||
res.redirect(
|
||||
"/" +
|
||||
buildQuery({
|
||||
TYPE: "op",
|
||||
ERROR: "unknown error",
|
||||
ERROR_DESC: "report this",
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue