-
Hey all. I'm attempting to get a basic Supabase example setup, using the provided react-auth-supabase example. However, when creating an account via an e-mail invite link, the only way to configure a password is via a subsequent password reset link. The redirect URL for this contains token information in the URL The reason I'm bringing this up is that the only way I've really been able to get data to the action, is through the form. So I add an input for the token itself: <input
value={hashParams["access_token"]}
type="token"
name="token"
id="token"
hidden
aria-hidden
/> This sort of makes my security spidey senses tingle, and I just wanted to see if this is a common pattern, bad security practice, etc.? I figure if it's exposed in the URL, sending it down in the form isn't the worst. Also generally, has anyone else dealt with URL Hash parameters and have suggested patterns for that? Thanks! |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 2 replies
-
You should be able to read anything that is part of the URL on the server inside your loader. I'm using Firebase, but they have similar approaches to Action URLs. You can extract every URL parameter from your URL and return it as LoaderData. Then, if needed, provide them in your form as hidden inputs. With HTTPS, the communication is secure. It does not matter if you use URL-Parameters or POST-Bodies. Here my firebase action route for reference. I hope this helps. import { applyActionCode, confirmPasswordReset } from "firebase/auth";
import {
ActionFunction,
Form,
json,
Link,
LoaderFunction,
redirect,
useActionData,
useLoaderData,
} from "remix";
import { Topbar } from "~/components/Topbar";
import { SubmitButton } from "~/ui/buttons";
import { Input } from "~/ui/inputs";
import { FormContainer } from "~/ui/layout";
import { H1, P } from "~/ui/typography";
import { getErrorMessage } from "~/db/errors";
import { Alert } from "~/ui/feedback";
import { auth } from "~/db/utils.server";
const RESET_PASSWORD = "resetPassword";
const VERIFY_EMAIL = "verifyEmail";
const RECOVER_EMAIL = "recoverEmail";
type ActionType =
| typeof RESET_PASSWORD
| typeof VERIFY_EMAIL
| typeof RECOVER_EMAIL;
interface LoaderData {
mode?: ActionType;
oobCode?: string;
success?: boolean;
}
export const loader: LoaderFunction = async ({ request }) => {
const url = new URL(request.url);
const mode = url.searchParams.get("mode") || undefined;
const oobCode = url.searchParams.get("oobCode") || undefined;
const success = url.searchParams.get("success") === "true";
return json({ mode, oobCode, success });
};
interface ActionData {
error?: string;
}
export const action: ActionFunction = async ({ request }) => {
const form = await request.formData();
const mode = form.get("mode") as ActionType | null;
const oobCode = form.get("oobCode");
if (
(mode !== RESET_PASSWORD &&
mode !== VERIFY_EMAIL &&
mode !== RECOVER_EMAIL) ||
typeof oobCode !== "string"
)
return json({ error: "The link is invalid." }, { status: 400 });
let response: ActionData = {};
try {
switch (mode) {
case RESET_PASSWORD:
const password = form.get("password");
if (typeof password !== "string") {
response.error = "You need to provide a password";
break;
}
await confirmPasswordReset(auth.client, oobCode, password);
break;
case VERIFY_EMAIL:
await applyActionCode(auth.client, oobCode);
break;
case RECOVER_EMAIL:
await applyActionCode(auth.client, oobCode);
break;
default:
break;
}
} catch (error) {
const code = (error as any).code;
response.error = getErrorMessage(code);
return json(response, { status: 400 });
}
redirect(`/action?success=true&mode=${mode}`);
};
export default function Action() {
const { mode, oobCode, success } = useLoaderData<LoaderData>();
const action = useActionData();
const error = action?.error;
return (
<>
<Topbar />
<FormContainer>
{mode && (
<H1>
{
{
[RESET_PASSWORD]: "Reset Password",
[VERIFY_EMAIL]: "Confirm Email",
[RECOVER_EMAIL]: "Reset Email",
}[mode]
}
</H1>
)}
{error && <Alert variant="danger">{error}</Alert>}
{success && <Alert variant="success">Action successfull!</Alert>}
{mode && (
<Form className="space-y-6" method="post">
<input type="hidden" name="oobCode" value={oobCode} />
<input type="hidden" name="mode" value={mode} />
{mode === RECOVER_EMAIL ? (
<SubmitButton id="send-request">Reset</SubmitButton>
) : mode === RESET_PASSWORD ? (
<>
<Input
label={"Password"}
id="password"
name="password"
type="password"
autoComplete="password"
required
placeholder="new Password"
/>
<SubmitButton id="send-request">Send</SubmitButton>
</>
) : mode === VERIFY_EMAIL ? (
<SubmitButton id="send-request">Confirm</SubmitButton>
) : null}
</Form>
)}
<P>Just Sign in?</P>
<Link
prefetch="intent"
to="/"
className="inline-block font-medium text-gray-600 underline hover:text-gray-500"
>
» Login
</Link>
</FormContainer>
</>
);
} |
Beta Was this translation helpful? Give feedback.
You should be able to read anything that is part of the URL on the server inside your loader.
I'm using Firebase, but they have similar approaches to Action URLs. You can extract every URL parameter from your URL and return it as LoaderData. Then, if needed, provide them in your form as hidden inputs. With HTTPS, the communication is secure. It does not matter if you use URL-Parameters or POST-Bodies. Here my firebase action route for reference. I hope this helps.