diff --git a/coderd/httpapi/httpapi.go b/coderd/httpapi/httpapi.go
index cd55a09d51525..a9687d58a0604 100644
--- a/coderd/httpapi/httpapi.go
+++ b/coderd/httpapi/httpapi.go
@@ -154,6 +154,7 @@ func ResourceNotFound(rw http.ResponseWriter) {
func Forbidden(rw http.ResponseWriter) {
Write(context.Background(), rw, http.StatusForbidden, codersdk.Response{
Message: "Forbidden.",
+ Detail: "You don't have permission to view this content. If you believe this is a mistake, please contact your administrator or try signing in with different credentials.",
})
}
diff --git a/site/e2e/setup/addUsersAndLicense.spec.ts b/site/e2e/setup/addUsersAndLicense.spec.ts
index f6817e0fd423d..bcaa8c9281cf8 100644
--- a/site/e2e/setup/addUsersAndLicense.spec.ts
+++ b/site/e2e/setup/addUsersAndLicense.spec.ts
@@ -1,6 +1,6 @@
import { expect, test } from "@playwright/test";
import { API } from "api/api";
-import { Language } from "pages/CreateUserPage/CreateUserForm";
+import { Language } from "pages/CreateUserPage/Language";
import { coderPort, license, premiumTestsRequired, users } from "../constants";
import { expectUrl } from "../expectUrl";
import { createUser } from "../helpers";
diff --git a/site/src/api/errors.ts b/site/src/api/errors.ts
index f1e63d1e39caf..873163e11a68d 100644
--- a/site/src/api/errors.ts
+++ b/site/src/api/errors.ts
@@ -133,6 +133,14 @@ export const getErrorDetail = (error: unknown): string | undefined => {
return undefined;
};
+export const getErrorStatus = (error: unknown): number | undefined => {
+ if (isApiError(error)) {
+ return error.status;
+ }
+
+ return undefined;
+};
+
export class DetailedError extends Error {
constructor(
message: string,
diff --git a/site/src/components/Alert/ErrorAlert.tsx b/site/src/components/Alert/ErrorAlert.tsx
index 73d9c62480ab8..0198ea4e99540 100644
--- a/site/src/components/Alert/ErrorAlert.tsx
+++ b/site/src/components/Alert/ErrorAlert.tsx
@@ -1,6 +1,7 @@
import AlertTitle from "@mui/material/AlertTitle";
-import { getErrorDetail, getErrorMessage } from "api/errors";
+import { getErrorDetail, getErrorMessage, getErrorStatus } from "api/errors";
import type { FC } from "react";
+import { Link } from "../Link/Link";
import { Alert, AlertDetail, type AlertProps } from "./Alert";
export const ErrorAlert: FC<
@@ -8,6 +9,7 @@ export const ErrorAlert: FC<
> = ({ error, ...alertProps }) => {
const message = getErrorMessage(error, "Something went wrong.");
const detail = getErrorDetail(error);
+ const status = getErrorStatus(error);
// For some reason, the message and detail can be the same on the BE, but does
// not make sense in the FE to showing them duplicated
@@ -15,14 +17,28 @@ export const ErrorAlert: FC<
return (
- {detail ? (
- <>
- {message}
- {shouldDisplayDetail && {detail}}
- >
- ) : (
- message
- )}
+ {
+ // When the error is a Forbidden response we include a link for the user to
+ // go back to a known viewable page.
+ status === 403 ? (
+ <>
+ {message}
+
+ {detail}{" "}
+
+ Go to workspaces
+
+
+ >
+ ) : detail ? (
+ <>
+ {message}
+ {shouldDisplayDetail && {detail}}
+ >
+ ) : (
+ message
+ )
+ }
);
};
diff --git a/site/src/pages/CreateUserPage/CreateUserForm.tsx b/site/src/pages/CreateUserPage/CreateUserForm.tsx
index aebdd36e45adc..be8b4a15797b5 100644
--- a/site/src/pages/CreateUserPage/CreateUserForm.tsx
+++ b/site/src/pages/CreateUserPage/CreateUserForm.tsx
@@ -19,18 +19,7 @@ import {
onChangeTrimmed,
} from "utils/formUtils";
import * as Yup from "yup";
-
-export const Language = {
- emailLabel: "Email",
- passwordLabel: "Password",
- usernameLabel: "Username",
- nameLabel: "Full name",
- emailInvalid: "Please enter a valid email address.",
- emailRequired: "Please enter an email address.",
- passwordRequired: "Please enter a password.",
- createUser: "Create",
- cancel: "Cancel",
-};
+import { Language } from "./Language";
export const authMethodLanguage = {
password: {
diff --git a/site/src/pages/CreateUserPage/CreateUserPage.test.tsx b/site/src/pages/CreateUserPage/CreateUserPage.test.tsx
index f8b256e2d0cbb..ec75fc9a8e244 100644
--- a/site/src/pages/CreateUserPage/CreateUserPage.test.tsx
+++ b/site/src/pages/CreateUserPage/CreateUserPage.test.tsx
@@ -4,8 +4,8 @@ import {
renderWithAuth,
waitForLoaderToBeRemoved,
} from "testHelpers/renderHelpers";
-import { Language as FormLanguage } from "./CreateUserForm";
import { CreateUserPage } from "./CreateUserPage";
+import { Language as FormLanguage } from "./Language";
const renderCreateUserPage = async () => {
renderWithAuth(, {
diff --git a/site/src/pages/CreateUserPage/Language.ts b/site/src/pages/CreateUserPage/Language.ts
new file mode 100644
index 0000000000000..d449829aea89d
--- /dev/null
+++ b/site/src/pages/CreateUserPage/Language.ts
@@ -0,0 +1,11 @@
+export const Language = {
+ emailLabel: "Email",
+ passwordLabel: "Password",
+ usernameLabel: "Username",
+ nameLabel: "Full name",
+ emailInvalid: "Please enter a valid email address.",
+ emailRequired: "Please enter an email address.",
+ passwordRequired: "Please enter a password.",
+ createUser: "Create",
+ cancel: "Cancel",
+};