kit icon indicating copy to clipboard operation
kit copied to clipboard

`use:enhance` compatibility on form whose action isn't a SvelteKit action

Open eltigerchino opened this issue 1 year ago • 8 comments

Describe the problem

An unhelpful error message presents itself when we have a form with use:enhance posting to an ordinary POST endpoint instead of a SvelteKit action. This occurs because the enhance action tries to parse the fetch response body as a JSON. If that succeeds, it looks for a data property and attempts to parse it as JSON too, which can throw an error. If there is no data property, submitting the form results in nothing happening, which can be confusing.

https://stackblitz.com/edit/sveltejs-kit-template-default-nmxvlc?file=src%2Froutes%2F%2Blayout.svelte

Describe the proposed solution

Maybe we can handle the outcome better or have a dev-only warning when this occurs?

Alternatives considered

Document that use:enhance should only be used with SvelteKit actions

Importance

nice to have

Additional Information

No response

eltigerchino avatar Oct 10 '23 12:10 eltigerchino

strange that when i downloaded the code and used it inside playgrounds by replacing the @sveltejs/kit and adapter-node versions with workspace:* , the page got redirected to /somewhere instead of showing the error on same page.

Rishab49 avatar Oct 11 '23 08:10 Rishab49

Yes! Can we please go with the option of "Alternatively, we could handle the non-JSON responses better" (mentioned in the StackBlitz link). For me, this actually is somewhat urgent, not a nice-to-have. Here's the background of my use case:

I'm working with an open source authentication solution called SuperTokens, and I'm intentionally doing auth on the backend (not the fronted) because it's more secure. In the authentication model of SuperTokens, they have short-lived Access Tokens that can be refreshed with long-lived Refreshed Tokens when the user makes requests to a server. When supporting users with or without JS in Remix, I'm able to handle this model just fine. In SvelteKit, things break. Here's the specific scenario that breaks...

Note: The /auth/session/refresh route is a regular API route since there's no <form> associated with it.

The Scenario

Imagine a user is on a page that displays new data on the current screen depending on what they submit to the server. This is a perfect use case for use:enhance. Everything will work fine for a non-JS user. And JS users will be able to see the new data on the current screen without a page refresh. However, this page requires authentication.

Where SvelteKit Works

Imagine a non-JS user is on this webpage. They submit a form with an expired Access Token and a valid Refresh Token. (Note that all tokens are expressed as HTTP-only cookies.) Here's what will happen:

  1. The server (with the help of SuperTokens) will see that the Access Token is expired, but a Refresh Token is present. It will redirect with a 307 to /auth/session/refresh. (This is the only route where Refresh Token HTTP-only cookies can be used and/or created, for security reasons. So there's no point in attempting a refresh on any other route.)
  2. The server (with the help of SuperTokens) will see that a valid Refresh Token is present at the /auth/session/refresh route, so a new Access Token and a new Refresh Token (for security reasons) will be returned to the user as HTTP-only cookies. It will redirect back the the original POST route with a 307.
  3. Because the non-JS users was redirected back to the original page with a 307, the POST will continue as normal. Since they are now authenticated with a valid Access Token, they will get to see the new data on the screen without having to inconveniently resubmit their form data.

Where SvelteKit Fails

Now imagine a JS user is on this webpage. They submit a form with an expired Access Token and an expired Refresh Token. Here's what will happen:

  1. The server (with the help of SuperTokens) will see that the Access Token is expired, but a Refresh Token is present. It will redirect with a 307 to /auth/session/refresh.
  2. The server (with the help of SuperTokens) will see that a valid Refresh Token is not present at the /auth/session/refresh route. So all of the user's Auth Cookies will be deleted by the server. Additionally, the user will be redirected to the /login page with a 303. (By redirecting with a 303, there's no risk of inappropriately POST-ing to the /login route. This also removes the unlikely-but-possible scenario where someone could accidentally re-authenticate as a different user during the redirect because of the values in the original form POST.)
  3. Because the JS user was redirected to the the /login page with a 303, the the page's HTML is returned.
  4. SvelteKit errors out (as shown in the StackBlitz link).

ITenthusiasm avatar Jan 10 '24 14:01 ITenthusiasm

I've run into an issue today with redirect form actions and setting headers. I thought form actions would work like post server endpoint and returning a response with redirect settings would redirect but no get an error because form actions only expects data to return and using set headers redirect doesn't keep headers.

So it would be nice even with actions to be able to return redirect responses that are not data to or from form.

JonathonRP avatar Feb 21 '24 04:02 JonathonRP

I've run into an issue today with redirect form actions and setting headers. I thought form actions would work like post server endpoint and returning a response with redirect settings would redirect but no get an error because form actions only expects data to return and using set headers redirect doesn't keep headers.

So it would be nice even with actions to be able to return redirect responses that are not data to or from form.

Do you mind elaborating on this with a code example?

eltigerchino avatar Feb 21 '24 05:02 eltigerchino

I've run into an issue today with redirect form actions and setting headers. I thought form actions would work like post server endpoint and returning a response with redirect settings would redirect but no get an error because form actions only expects data to return and using set headers redirect doesn't keep headers.

So it would be nice even with actions to be able to return redirect responses that are not data to or from form.

Do you mind elaborating on this with a code example?

I can make a repo later and share, no problem.

JonathonRP avatar Feb 21 '24 05:02 JonathonRP

@JonathonRP is the issue that you're talking about similar to what's shown in the StackBlitz link in the OP? Or is it something different?

ITenthusiasm avatar Feb 21 '24 14:02 ITenthusiasm

@ITenthusiasm similar, I'm using form actions with use enhance and also not working if I return a response that doesn't have data prop or is sveltekit redirect(), I'm attempting to have redirect and settings authorization header.

JonathonRP avatar Feb 21 '24 14:02 JonathonRP

@eltigerchino, repo - https://stackblitz.com/~/github.com/JonathonRP/sveltekit-authjs-redirect_demo?initialPath=/linkBuxferAccount check /src/routes/linkBuxferAccount/+page.server.ts

JonathonRP avatar Feb 21 '24 21:02 JonathonRP