#Next auth. Please
70 messages · Page 1 of 1 (latest)
🔎 This post has been indexed in our web forum and will be seen by search engines so other users can find it outside Discord
🕵️ Your user profile is private by default and won't be visible to users outside Discord, if you want to be visible in the web forum you can add the "Public Profile" role in id:customize
✅ You can mark a message as the answer for your post with Right click -> Apps -> Mark Solution
(if you don't see the option, try refreshing Discord with Ctrl + R)
i spent whole 2 days for simple thing
i just want the method to get oauth erros on custom login page
i dont belive nobody needed that
or i think nobody builds pages by next auth
Once the signin process is set up with NextAuth, we’ll want to handle user errors. By default, any login errors will be redirected to NextAuth’s default login page with an error message.
this not working
i give up
This question has been marked as answered! If you have any other questions, feel free to create another post
[Click here](#1144343879097786569 message)
register the signin page as an error page inside your NextAuth configuration and then get the error from the URL parameter
Can you share your NextAuth config please?
import { prisma } from "@/lib/prisma";
import { compare } from "bcryptjs";
import type { NextAuthOptions } from "next-auth";
import GithubProvider from "next-auth/providers/github";
import CredentialsProvider from "next-auth/providers/credentials";
import GoogleProvider from "next-auth/providers/google";
import { PrismaAdapter } from "@next-auth/prisma-adapter";
export const authOptions: NextAuthOptions = {
session: {
strategy: "jwt",
},
secret: process.env.NEXTAUTH_SECRET as string,
adapter: PrismaAdapter(prisma),
providers: [
CredentialsProvider({
name: "Sign in",
credentials: {
email: { label: "Email", type: "email", placeholder: "jsmith" },
password: { label: "Password", type: "password" },
},
async authorize(credentials) {
const { email, password } = credentials as {
email: string;
password: string;
};
if (!credentials || !email || !password) {
throw new Error("Invalid credentials");
}
const user = await prisma.user.findUnique({
where: {
email: email,
},
});
if (
!user ||
!user.password ||
!(await compare(password, user.password))
) {
throw new Error("Email or password is incorrect");
}
return {
id: user.id,
email: user.email,
};
},
}),
GithubProvider({
clientId: process.env.GITHUB_ID as string,
clientSecret: process.env.GITHUB_SECRET as string,
}),
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID as string,
clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
}),
],
pages: {
signIn: "/login",
error: "/login",
signOut: "/login",
},
};```
import Link from "next/link";
import LoginForm from "./form";
import { redirectIfAuthenticated, signOutIfBlocked } from "@/lib/protected";
export default async function Login({
params: { lang },
}: {
params: { lang: string };
}) {
await redirectIfAuthenticated();
// get query error
return (
<div>
<div className="p-5 flex flex-col gap-3 items-start">
<h2 className="font-bold text-2xl">Welcome back!</h2>
<p className="text-tsecondary">
{"Don't have an account? "}
<Link className="underline" href="/register">
Create an account
</Link>
</p>
<LoginForm />
</div>
</div>
);
}
next/router doesnt work in new nextjs
Yeah, use next/navigation instead
because when you sign in google
its redirected to google page then from google page
redirected to some link like /api/auth/providers/google
i dont remember
That's fine so far
It gets a token from Google, then uses that token to get the user data in the callback
i dont understand why signIn function doesnt have error handler itself
yes ans as we see in network page it actually gets this error in url
okay
What does await redirectIfAuthenticated(); do?
export async function redirectIfAuthenticated() {
const session = await getServerSession(authOptions);
session && redirect("/");
}```
Can you comment that line out and test?
Because something is redirecting you
And that is why you are not getting passed the URL parameters
yes and its same
That's the full code?
"use client";
import { AiOutlineLoading3Quarters } from "react-icons/ai";
import React, { useState } from "react";
import { Formik, Form, Field, ErrorMessage } from "formik";
import { signIn } from "next-auth/react";
import {
useParams,
usePathname,
useRouter,
useSearchParams,
} from "next/navigation";
import { FaGithub, FaGoogle } from "react-icons/fa";
const CALLBACK_URL = "/";
const LoginForm = () => {
const [error, setError] = useState<null | string>(null);
const [loading, setLoading] = useState(false);
const router = useRouter();
const validateForm = (values: { email: string; password: string }) => {
const errors: any = {};
if (!values.email) {
errors.email = "Required";
} else if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)) {
errors.email = "Invalid email address";
}
if (!values.password) {
errors.password = "Required";
}
return errors;
};
const handleSubmit = (
values: {
email: string;
password: string;
},
{ setSubmitting }: any
) => {
setLoading(true);
setError(null);
const { email, password } = values;
signIn("credentials", {
redirect: false,
email,
password,
})
.then((res) => {
router.push(CALLBACK_URL);
})
.catch((err) => {
setSubmitting(false);
setLoading(false);
setError(err.message);
});
};
return (
<>
{error && <div className="text-red-500">{error}</div>}
<Formik
initialValues={{ email: "", password: "" }}
validate={validateForm}
onSubmit={handleSubmit}
>
{({ isSubmitting }) => (
<Form className="flex flex-col gap-3 items-start max-w-xs w-full">
<Field
className="border border-secondary p-3 bg-transparent outline-none w-full rounded-sm"
placeholder="Please enter your email..."
type="email"
name="email"
/>
<ErrorMessage name="email" component="div" />
<Field
className="border border-secondary p-3 bg-transparent outline-none w-full rounded-sm"
placeholder="Please enter your password..."
type="password"
name="password"
/>
<ErrorMessage name="password" component="div" />
<button type="submit" disabled={isSubmitting}>
{loading ? (
<AiOutlineLoading3Quarters className="animate-spin text-2xl" />
) : (
"Sign In"
)}
</button>
</Form>
)}
</Formik>
{/* or sign in with google or github */}
<button
type="button"
onClick={() => {
signIn("google", {
redirect: false,
});
}}
>
<FaGoogle />
google
</button>
<button
type="button"
onClick={() => {
signIn("github", {
redirect: false,
});
}}
>
<FaGithub />
github
</button>
</>
);
};
export default LoginForm;
Ik
And you can see in the response headers that it redirects you without parameters
So seems the be the localization
Can you show me how that's implemented?
import { NextRequest, NextResponse } from "next/server";
import { match } from "@formatjs/intl-localematcher";
import Negotiator from "negotiator";
import { defaultLocale, locales } from "./locals";
function getLocale(request: NextRequest) {
const headers = new Headers(request.headers);
const acceptLanguage = headers.get("accept-language");
if (acceptLanguage) {
headers.set("accept-language", acceptLanguage.replaceAll("_", "-"));
}
const headersObject = Object.fromEntries(headers.entries());
const languages = new Negotiator({
headers: headersObject,
}).languages();
return match(languages, locales, defaultLocale);
}
export function middleware(request: NextRequest) {
const pathname = request.nextUrl.pathname;
const pathnameIsMissingLocale = locales.every(
(locale) => !pathname.startsWith(`/${locale}/`) && pathname !== `/${locale}`
);
if (pathnameIsMissingLocale) {
const locale = getLocale(request);
return NextResponse.redirect(
new URL(`/${locale}/${pathname}`, request.url)
);
}
}
export const config = {
matcher: ["/((?!_next|api|favicon.ico).*)"],
};
Alright, one sec
So the issue is that pathname does not include URL parameters. So when redirecting the user to their locale, your middleware dumps all query parameters.
To fix this, make the following changes in your middleware function:
// add this line where you also define your `pathname` variable:
const searchParams = request.nextUrl.search;
// edit your redirect line like this:
return NextResponse.redirect(
new URL(`/${locale + pathname + searchParams}`, request.url);
);
Here's a screenshot of the structure of the request.nextUrl object to better understand why this error occurred and how we fix it
This question has been marked as answered! If you have any other questions, feel free to create another post
[Click here](#1144343879097786569 message)
Oh, btw the two slashes are there because pathname already includes a slash. I edited my answer accordingly
okay
and i have one question if you have time
const errors = {
Signin: "Try signing with a different account.",
OAuthSignin: "Try signing with a different account.",
OAuthCallback: "Try signing with a different account.",
OAuthCreateAccount: "Try signing with a different account.",
EmailCreateAccount: "Try signing with a different account.",
Callback: "Try signing with a different account.",
OAuthAccountNotLinked:
"To confirm your identity, sign in with the same account you used originally.",
EmailSignin: "Check your email address.",
CredentialsSignin:
"Sign in failed. Check the details you provided are correct.",
default: "Unable to sign in.",
};
const SignInError = ({ error }: any) => {
const errorMessage =
error && (errors[error as keyof typeof errors] ?? errors.default);
return <div className="text-red-500">{errorMessage}</div>;
};```
is this right way?
It's not a wrong way so you should be fine using this