diff --git a/site/components/SignIn/SignInForm.test.tsx b/site/components/SignIn/SignInForm.test.tsx
index 0f1797de4b606..1306fd43fa0e0 100644
--- a/site/components/SignIn/SignInForm.test.tsx
+++ b/site/components/SignIn/SignInForm.test.tsx
@@ -55,7 +55,29 @@ describe("SignInForm", () => {
act(() => elem.click())
// Then
- // Should redirect because login was successfully
+ // Should redirect because login was successful
await waitFor(() => expect(singletonRouter).toMatchObject({ asPath: "/" }))
})
+
+ it("respects ?redirect query parameter when complete", async () => {
+ // Given
+ const loginHandler = (_email: string, _password: string) => Promise.resolve()
+ // Set a path to redirect to after login is successful
+ mockRouter.setCurrentUrl("/login?redirect=%2Fsome%2Fother%2Fpath")
+
+ // When
+ // Render the component
+ const { container } = render()
+ // Set user / password
+ const inputs = container.querySelectorAll("input")
+ fireEvent.change(inputs[0], { target: { value: "test@coder.com" } })
+ fireEvent.change(inputs[1], { target: { value: "password" } })
+ // Click sign-in
+ const elem = await screen.findByText("Sign In")
+ act(() => elem.click())
+
+ // Then
+ // Should redirect to /some/other/path because ?redirect was specified and login was successful
+ await waitFor(() => expect(singletonRouter).toMatchObject({ asPath: "/some/other/path" }))
+ })
})
diff --git a/site/components/SignIn/SignInForm.tsx b/site/components/SignIn/SignInForm.tsx
index a456cf57ed56e..fdb0e0c43ae3f 100644
--- a/site/components/SignIn/SignInForm.tsx
+++ b/site/components/SignIn/SignInForm.tsx
@@ -1,6 +1,6 @@
import { makeStyles } from "@material-ui/core/styles"
import { FormikContextType, useFormik } from "formik"
-import { useRouter } from "next/router"
+import { NextRouter, useRouter } from "next/router"
import React from "react"
import { useSWRConfig } from "swr"
import * as Yup from "yup"
@@ -9,6 +9,7 @@ import { Welcome } from "./Welcome"
import { FormTextField } from "../Form"
import * as API from "./../../api"
import { LoadingButton } from "./../Button"
+import { firstOrItem } from "../../util/array"
/**
* BuiltInAuthFormValues describes a form using built-in (email/password)
@@ -61,7 +62,9 @@ export const SignInForm: React.FC = ({
await loginHandler(email, password)
// Tell SWR to invalidate the cache for the user endpoint
await mutate("/api/v2/users/me")
- await router.push("/")
+
+ const redirect = getRedirectFromRouter(router)
+ await router.push(redirect)
} catch (err) {
helpers.setFieldError("password", "The username or password is incorrect.")
}
@@ -117,3 +120,12 @@ export const SignInForm: React.FC = ({
>
)
}
+
+const getRedirectFromRouter = (router: NextRouter) => {
+ const defaultRedirect = "/"
+ if (router.query?.redirect) {
+ return firstOrItem(router.query.redirect, defaultRedirect)
+ } else {
+ return defaultRedirect
+ }
+}