mirror of
https://github.com/fleetdm/fleet
synced 2026-05-24 09:28:54 +00:00
Improve SSO error messaging (#6339)
This commit is contained in:
parent
bfb650a8da
commit
e16010f1f9
9 changed files with 97 additions and 11 deletions
1
changes/issue-3359-improve-sso-messaging
Normal file
1
changes/issue-3359-improve-sso-messaging
Normal file
|
|
@ -0,0 +1 @@
|
|||
- Improve SSO login failure messaging.
|
||||
|
|
@ -47,4 +47,8 @@ describe("SSO Sessions", () => {
|
|||
// Log in should fail
|
||||
cy.contains("Password");
|
||||
});
|
||||
it("displays an error message when status is set", () => {
|
||||
cy.visit("/login?status=account_disabled");
|
||||
cy.getAttached(".flash-message");
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
import React from "react";
|
||||
|
||||
interface IAppProps {
|
||||
children: JSX.Element;
|
||||
}
|
||||
|
||||
export const UnauthenticatedRoutes = ({ children }: IAppProps): JSX.Element => {
|
||||
return <div>{children}</div>;
|
||||
};
|
||||
|
||||
export default UnauthenticatedRoutes;
|
||||
1
frontend/components/UnauthenticatedRoutes/index.ts
Normal file
1
frontend/components/UnauthenticatedRoutes/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { default } from "./UnauthenticatedRoutes";
|
||||
24
frontend/layouts/GatedLayout/GatedLayout.tsx
Normal file
24
frontend/layouts/GatedLayout/GatedLayout.tsx
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import React, { useContext } from "react";
|
||||
import { NotificationContext } from "context/notification";
|
||||
import FlashMessage from "components/FlashMessage";
|
||||
|
||||
interface IGatedLayoutProps {
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
const GatedLayout = ({ children }: IGatedLayoutProps): JSX.Element => {
|
||||
const { notification, hideFlash } = useContext(NotificationContext);
|
||||
|
||||
return (
|
||||
<div className="gated-layout">
|
||||
<FlashMessage
|
||||
fullWidth
|
||||
notification={notification}
|
||||
onRemoveFlash={hideFlash}
|
||||
/>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default GatedLayout;
|
||||
5
frontend/layouts/GatedLayout/_styles.scss
Normal file
5
frontend/layouts/GatedLayout/_styles.scss
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
.gated-layout {
|
||||
.flash-message {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
1
frontend/layouts/GatedLayout/index.ts
Normal file
1
frontend/layouts/GatedLayout/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { default } from "./GatedLayout";
|
||||
|
|
@ -4,6 +4,7 @@ import { size } from "lodash";
|
|||
|
||||
import paths from "router/paths";
|
||||
import { AppContext } from "context/app";
|
||||
import { NotificationContext } from "context/notification";
|
||||
import { RoutingContext } from "context/routing";
|
||||
import { ISSOSettings } from "interfaces/ssoSettings";
|
||||
import local from "utilities/local";
|
||||
|
|
@ -17,6 +18,11 @@ import LoginSuccessfulPage from "pages/LoginSuccessfulPage";
|
|||
|
||||
interface ILoginPageProps {
|
||||
router: InjectedRouter; // v3
|
||||
location: {
|
||||
pathname: string;
|
||||
query: { vulnerable?: boolean };
|
||||
search: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface ILoginData {
|
||||
|
|
@ -24,16 +30,36 @@ interface ILoginData {
|
|||
password: string;
|
||||
}
|
||||
|
||||
const LoginPage = ({ router }: ILoginPageProps) => {
|
||||
interface IStatusMessages {
|
||||
account_disabled: string;
|
||||
account_invalid: string;
|
||||
org_disabled: string;
|
||||
error: string;
|
||||
}
|
||||
|
||||
const statusMessages: IStatusMessages = {
|
||||
account_disabled:
|
||||
"Single sign-on is not enabled on your account. Please contact your Fleet administrator.",
|
||||
account_invalid: "You do not have a Fleet account.",
|
||||
org_disabled: "Single sign-on is not enabled for your organization.",
|
||||
error:
|
||||
"There was an error with single sign-on. Please contact your Fleet administrator.",
|
||||
};
|
||||
|
||||
const LoginPage = ({ router, location }: ILoginPageProps) => {
|
||||
const {
|
||||
currentUser,
|
||||
setAvailableTeams,
|
||||
setCurrentUser,
|
||||
setCurrentTeam,
|
||||
} = useContext(AppContext);
|
||||
const { renderFlash } = useContext(NotificationContext);
|
||||
const { redirectLocation } = useContext(RoutingContext);
|
||||
const [loginVisible, setLoginVisible] = useState<boolean>(true);
|
||||
const [ssoSettings, setSSOSettings] = useState<ISSOSettings>();
|
||||
const [pageStatus, setPageStatus] = useState<string | null>(
|
||||
new URLSearchParams(location.search).get("status")
|
||||
);
|
||||
const [errors, setErrors] = useState<{ [key: string]: string }>({});
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -53,6 +79,10 @@ const LoginPage = ({ router }: ILoginPageProps) => {
|
|||
} else {
|
||||
getSSO();
|
||||
}
|
||||
|
||||
if (pageStatus && pageStatus in statusMessages) {
|
||||
renderFlash("error", statusMessages[pageStatus as keyof IStatusMessages]);
|
||||
}
|
||||
}, [router]);
|
||||
|
||||
const onChange = () => {
|
||||
|
|
|
|||
|
|
@ -18,11 +18,13 @@ import App from "components/App";
|
|||
import AuthenticatedAdminRoutes from "components/AuthenticatedAdminRoutes";
|
||||
import AuthAnyAdminRoutes from "components/AuthAnyAdminRoutes";
|
||||
import AuthenticatedRoutes from "components/AuthenticatedRoutes";
|
||||
import UnauthenticatedRoutes from "components/UnauthenticatedRoutes";
|
||||
import AuthGlobalAdminMaintainerRoutes from "components/AuthGlobalAdminMaintainerRoutes";
|
||||
import AuthAnyMaintainerAnyAdminRoutes from "components/AuthAnyMaintainerAnyAdminRoutes";
|
||||
import ConfirmInvitePage from "pages/ConfirmInvitePage";
|
||||
import ConfirmSSOInvitePage from "pages/ConfirmSSOInvitePage";
|
||||
import CoreLayout from "layouts/CoreLayout";
|
||||
import GatedLayout from "layouts/GatedLayout";
|
||||
import DeviceUserPage from "pages/hosts/details/DeviceUserPage";
|
||||
import EditPackPage from "pages/packs/EditPackPage";
|
||||
import EmailTokenRedirect from "components/EmailTokenRedirect";
|
||||
|
|
@ -76,16 +78,23 @@ const AppWrapper = ({ children, router, location }: IAppWrapperProps) => (
|
|||
const routes = (
|
||||
<Router history={browserHistory}>
|
||||
<Route path={PATHS.ROOT} component={AppWrapper}>
|
||||
<Route path="setup" component={RegistrationPage} />
|
||||
<Route path="previewlogin" component={LoginPreviewPage} />
|
||||
<Route path="login" component={LoginPage} />
|
||||
<Route path="login/invites/:invite_token" component={ConfirmInvitePage} />
|
||||
<Route
|
||||
path="login/ssoinvites/:invite_token"
|
||||
component={ConfirmSSOInvitePage}
|
||||
/>
|
||||
<Route path="login/forgot" component={ForgotPasswordPage} />
|
||||
<Route path="login/reset" component={ResetPasswordPage} />
|
||||
<Route component={UnauthenticatedRoutes as RouteComponent}>
|
||||
<Route component={GatedLayout}>
|
||||
<Route path="setup" component={RegistrationPage} />
|
||||
<Route path="previewlogin" component={LoginPreviewPage} />
|
||||
<Route path="login" component={LoginPage} />
|
||||
<Route
|
||||
path="login/invites/:invite_token"
|
||||
component={ConfirmInvitePage}
|
||||
/>
|
||||
<Route
|
||||
path="login/ssoinvites/:invite_token"
|
||||
component={ConfirmSSOInvitePage}
|
||||
/>
|
||||
<Route path="login/forgot" component={ForgotPasswordPage} />
|
||||
<Route path="login/reset" component={ResetPasswordPage} />
|
||||
</Route>
|
||||
</Route>
|
||||
<Route component={AuthenticatedRoutes as RouteComponent}>
|
||||
<Route path="email/change/:token" component={EmailTokenRedirect} />
|
||||
<Route path="logout" component={LogoutPage} />
|
||||
|
|
|
|||
Loading…
Reference in a new issue