@@ -28,6 +28,15 @@ export const LoginPage: FC = () => {
2828 const navigate = useNavigate ( ) ;
2929 const { metadata } = useEmbeddedMetadata ( ) ;
3030 const buildInfoQuery = useQuery ( buildInfo ( metadata [ "build-info" ] ) ) ;
31+ let redirectError : Error | null = null ;
32+ let redirectUrl : URL | null = null ;
33+ try {
34+ redirectUrl = new URL ( redirectTo ) ;
35+ } catch {
36+ // Do nothing
37+ }
38+
39+ const isApiRouteRedirect = redirectTo . startsWith ( "/api/v2" ) ;
3140
3241 useEffect ( ( ) => {
3342 if ( ! buildInfoQuery . data || isSignedIn ) {
@@ -42,41 +51,24 @@ export const LoginPage: FC = () => {
4251 } , [ isSignedIn , buildInfoQuery . data , user ?. id ] ) ;
4352
4453 if ( isSignedIn ) {
45- if ( buildInfoQuery . data ) {
46- // This uses `navigator.sendBeacon`, so window.href
47- // will not stop the request from being sent!
48- sendDeploymentEvent ( buildInfoQuery . data , {
49- type : "deployment_login" ,
50- user_id : user ?. id ,
51- } ) ;
54+ // The reason we need `window.location.href` for api redirects is that
55+ // we need the page to reload and make a request to the backend. If we
56+ // use `<Navigate>`, react would handle the redirect itself and never
57+ // request the page from the backend.
58+ if ( isApiRouteRedirect ) {
59+ const sanitizedUrl = new URL ( redirectTo , window . location . origin ) ;
60+ window . location . href = sanitizedUrl . pathname + sanitizedUrl . search ;
61+ // Setting the href should immediately request a new page. Show an
62+ // error state if it doesn't.
63+ redirectError = new Error ( "unable to redirect" ) ;
64+ } else {
65+ return (
66+ < Navigate
67+ to = { redirectUrl ? redirectUrl . pathname : redirectTo }
68+ replace
69+ />
70+ ) ;
5271 }
53-
54- // If the redirect is going to a workspace application, and we
55- // are missing authentication, then we need to change the href location
56- // to trigger a HTTP request. This allows the BE to generate the auth
57- // cookie required. Similarly for the OAuth2 exchange as the authorization
58- // page is served by the backend.
59- // If no redirect is present, then ignore this branched logic.
60- if ( redirectTo !== "" && redirectTo !== "/" ) {
61- try {
62- // This catches any absolute redirects. Relative redirects
63- // will fail the try/catch. Subdomain apps are absolute redirects.
64- const redirectURL = new URL ( redirectTo ) ;
65- if ( redirectURL . host !== window . location . host ) {
66- window . location . href = redirectTo ;
67- return null ;
68- }
69- } catch {
70- // Do nothing
71- }
72- // Path based apps and OAuth2.
73- if ( redirectTo . includes ( "/apps/" ) || redirectTo . includes ( "/oauth2/" ) ) {
74- window . location . href = redirectTo ;
75- return null ;
76- }
77- }
78-
79- return < Navigate to = { redirectTo } replace /> ;
8072 }
8173
8274 if ( isConfiguringTheFirstUser ) {
@@ -90,14 +82,15 @@ export const LoginPage: FC = () => {
9082 </ Helmet >
9183 < LoginPageView
9284 authMethods = { authMethodsQuery . data }
93- error = { signInError }
85+ error = { signInError ?? redirectError }
9486 isLoading = { isLoading || authMethodsQuery . isLoading }
9587 buildInfo = { buildInfoQuery . data }
9688 isSigningIn = { isSigningIn }
9789 onSignIn = { async ( { email, password } ) => {
9890 await signIn ( email , password ) ;
9991 navigate ( "/" ) ;
10092 } }
93+ redirectTo = { redirectTo }
10194 />
10295 </ >
10396 ) ;
0 commit comments