bulky-cat-78746
10/10/2024, 7:39 AM'use client'
import { useEffect, useState } from "react";
import { SubmitButton } from "@/components/SubmitButton";
import Link from "next/link";
import { Configuration, FrontendApi, LoginFlow } from "@ory/client"
import { useRouter } from "next/navigation";
const basePath = process.env.NEXT_PUBLIC_ORY_SDK_URL;
const ory = new FrontendApi(
new Configuration({
basePath: basePath,
baseOptions: {
withCredentials: true,
},
})
);
export default function LoginForm() {
const router = useRouter();
const [userName, setUserName] = useState('');
const [password, setPassword] = useState('');
const [flow, setFlow] = useState<LoginFlow>();
const [error, setError] = useState<string | null>(null);
const [csrfToken, setCsrfToken] = useState<string | null>(null);
const [isFlowCreated, setIsFlowCreated] = useState(false); // Track if flow is created
useEffect(() => {
const createLoginFlow = async () => {
if (flow) {
console.log("flow already exists!");
return;
}
try {
const { data } = await ory.createBrowserLoginFlow({
refresh: false,
aal: undefined,
returnTo: undefined,
});
setFlow(data);
console.log(data);
console.log(data.ui.nodes);
// Retrieve the CSRF token from the flow response
const csrfNode = data.ui.nodes.find(node => node.attributes.name === 'csrf_token');
if (csrfNode) {
console.log(csrfNode);
setCsrfToken(csrfNode.attributes.value as string);
}
} catch (err) {
console.error("Error creating login flow:", err);
setError("Could not create login flow. Please try again.");
}
};
if (!isFlowCreated) {
createLoginFlow();
setIsFlowCreated(true);
}
}, [isFlowCreated, flow]);
const onSubmitLogin = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (!flow || !csrfToken) {
setError("Login flow is not available. Please refresh the page.");
return;
}
// router.push(`/login?flow=${flow?.id}`);
// .then(async () => {
// await router.push(`/login?flow=${flow?.id}`, undefined, { shallow: true });
try {
const response = await ory.updateLoginFlow({
flow: flow?.id,
updateLoginFlowBody: {
csrf_token: csrfToken,
identifier: userName,
password: password,
method: "password"
}
});
console.log("Login success:", response.data);
// Redirect or handle login success
} catch (err: any) {
console.error("Login failed:", err.response?.data || err);
setError("Login failed. Please check your credentials.");
}
// });
};
return (
<form onSubmit={onSubmitLogin} className="flex flex-col space-y-4 bg-gray-50 px-4 py-8 sm:px-16">
{error && <div className="text-red-500">{error}</div>}
<div>
<label
htmlFor="email"
className="block text-xs text-gray-600 uppercase"
>
Email Address
</label>
<input
id="email"
name="email"
type="email"
placeholder="user@example.com"
autoComplete="email"
value={userName}
onChange={(e) => setUserName(e.target.value)}
required
className="mt-1 block w-full appearance-none rounded-md border border-gray-300 px-3 py-2 placeholder-gray-400 shadow-sm focus:border-black focus:outline-none focus:ring-black sm:text-sm"
/>
</div>
<div>
<label
htmlFor="password"
className="block text-xs text-gray-600 uppercase"
>
Password
</label>
<input
id="password"
name="password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
className="mt-1 block w-full appearance-none rounded-md border border-gray-300 px-3 py-2 placeholder-gray-400 shadow-sm focus:border-black focus:outline-none focus:ring-black sm:text-sm"
/>
</div>
<SubmitButton>Sign in</SubmitButton>
<p className="text-center text-sm text-gray-600">
{"Don't have an account? "}
<Link href="/register" className="font-semibold text-gray-800">
Sign up
</Link>
{' for free.'}
</p>
</form>
);
}
// export default LoginForm;
It throws the following exception during login:
{
"error": {
"id": "security_csrf_violation",
"code": 403,
"status": "Forbidden",
"reason": "Please retry the flow and optionally clear your cookies. The request was rejected to protect you from Cross-Site-Request-Forgery (CSRF) which could cause account takeover, leaking personal information, and other serious security issues.",
"details": {
"docs": "<https://www.ory.sh/kratos/docs/debug/csrf>",
"hint": "The anti-CSRF cookie was found but the CSRF token was not included in the HTTP request body (csrf_token) nor in the HTTP Header (X-CSRF-Token).",
"reject_reason": "The HTTP Cookie Header was set and a CSRF token was sent but they do not match. We recommend deleting all cookies for this domain and retrying the flow."
},
"message": "the request was rejected to protect you from Cross-Site-Request-Forgery"
}
}
gentle-xylophone-92591
10/10/2024, 12:16 PMcsrf_token
return by the payload that is in ui nodes, there's also a csrf_token
that kratos set to the browser cookie, afaik those need to be both present and match when you process the requestgentle-xylophone-92591
10/10/2024, 12:16 PMgentle-xylophone-92591
10/10/2024, 12:18 PMbulky-cat-78746
10/10/2024, 3:19 PM