diff --git a/changes/issue-3359-improve-sso-messaging b/changes/issue-3359-improve-sso-messaging
new file mode 100644
index 0000000000..56b1179f00
--- /dev/null
+++ b/changes/issue-3359-improve-sso-messaging
@@ -0,0 +1 @@
+- Improve SSO login failure messaging.
diff --git a/cypress/integration/all/sessions/sso.spec.ts b/cypress/integration/all/sessions/sso.spec.ts
index a38742d457..c884100717 100644
--- a/cypress/integration/all/sessions/sso.spec.ts
+++ b/cypress/integration/all/sessions/sso.spec.ts
@@ -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");
+ });
});
diff --git a/frontend/components/UnauthenticatedRoutes/UnauthenticatedRoutes.tsx b/frontend/components/UnauthenticatedRoutes/UnauthenticatedRoutes.tsx
new file mode 100644
index 0000000000..58ade9b646
--- /dev/null
+++ b/frontend/components/UnauthenticatedRoutes/UnauthenticatedRoutes.tsx
@@ -0,0 +1,11 @@
+import React from "react";
+
+interface IAppProps {
+ children: JSX.Element;
+}
+
+export const UnauthenticatedRoutes = ({ children }: IAppProps): JSX.Element => {
+ return
{children}
;
+};
+
+export default UnauthenticatedRoutes;
diff --git a/frontend/components/UnauthenticatedRoutes/index.ts b/frontend/components/UnauthenticatedRoutes/index.ts
new file mode 100644
index 0000000000..b9642b9ed0
--- /dev/null
+++ b/frontend/components/UnauthenticatedRoutes/index.ts
@@ -0,0 +1 @@
+export { default } from "./UnauthenticatedRoutes";
diff --git a/frontend/layouts/GatedLayout/GatedLayout.tsx b/frontend/layouts/GatedLayout/GatedLayout.tsx
new file mode 100644
index 0000000000..9dbf25b2c5
--- /dev/null
+++ b/frontend/layouts/GatedLayout/GatedLayout.tsx
@@ -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 (
+
+
+ {children}
+
+ );
+};
+
+export default GatedLayout;
diff --git a/frontend/layouts/GatedLayout/_styles.scss b/frontend/layouts/GatedLayout/_styles.scss
new file mode 100644
index 0000000000..a1e1fc5ed9
--- /dev/null
+++ b/frontend/layouts/GatedLayout/_styles.scss
@@ -0,0 +1,5 @@
+.gated-layout {
+ .flash-message {
+ border: 0;
+ }
+}
\ No newline at end of file
diff --git a/frontend/layouts/GatedLayout/index.ts b/frontend/layouts/GatedLayout/index.ts
new file mode 100644
index 0000000000..3ceb094e11
--- /dev/null
+++ b/frontend/layouts/GatedLayout/index.ts
@@ -0,0 +1 @@
+export { default } from "./GatedLayout";
diff --git a/frontend/pages/LoginPage/LoginPage.tsx b/frontend/pages/LoginPage/LoginPage.tsx
index 90c34fdb43..6b946bb847 100644
--- a/frontend/pages/LoginPage/LoginPage.tsx
+++ b/frontend/pages/LoginPage/LoginPage.tsx
@@ -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(true);
const [ssoSettings, setSSOSettings] = useState();
+ const [pageStatus, setPageStatus] = useState(
+ 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 = () => {
diff --git a/frontend/router/index.tsx b/frontend/router/index.tsx
index e658572b5b..e6b5e18bf5 100644
--- a/frontend/router/index.tsx
+++ b/frontend/router/index.tsx
@@ -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 = (
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+