NextAuth.js服务端如何实现signIn功能?无法进行重定向工作

问题描述 投票:0回答:3

尝试为 React JS / Next JS 项目实现 NextAuth.js。当前运行 React JS 18.2.0、Next JS 13.0.6 和 NextAuth.js 4.18.0。

我正在使用服务器端渲染以及部署在 Vercel 上的 Next API 调用。为了确定会话,我在 getServerSideProps 中使用unstable_getServerSession,效果很好。现在我想使用 signIn 方法通过凭据提供程序登录用户。我的实现不断重定向到同一页面,并在 URL 中添加了一些参数:

网址如下: https://example.com/api/auth/signin?callbackUrl=%2F

显示:

{"boyd":"","query":{"callbackUrl":"/"},"cookies":{"__Host-next-auth.csrf-token":"token...","__Secure-next-auth.callback-url":"url..."}}

没有保存任何会话,我不知道发生了什么。我希望登录方法能够解决这个问题,但我假设服务器端渲染会阻止调用解析。

下面的代码描述了我的实现过程:

所用表格:

<Box component="form" onSubmit={handleSubmit} sx={{ mt: 1 }}>   
    <input name="csrfToken" type="hidden" defaultValue={csrfToken} />
    <TextField
        margin="normal"
        required
        fullWidth
        id="phone"
        label="Phone Number"
        name="phone"
        onChange={(e) => setPhone(e.target.value)}
        autoComplete="phone"
        autoFocus
    />
    <TextField
        margin="normal"
        required
        fullWidth
        name="password"
        label="Password"
        type="password"
        id="password"
        onChange={(e) => setPassword(e.target.value)}
        autoComplete="current-password"
    />
    <Button
        type="submit"
        fullWidth
        variant="contained"
        sx={{ mt: 3, mb: 2 }}
    >
    Sign In
    </Button>
</Box>

表格手柄:

const [phone, setPhone] = useState("");
    const [password, setPassword] = useState("");
    const [submit, setSubmit] = useState(false);

    {/* On submit use Next Auth signIn method */}
    const handleSubmit = (e) => {
        e.preventDefault();
        setSubmit(true);
    }

    useEffect(() => {
        if(submit){
            signIn("credentials", {
                callbackUrl: '/',
                username: phone, 
                password: password,
            })
        }
    }, [submit])

获取CSRF-token:

export async function getServerSideProps(context) {
    return {
      props: {
        csrfToken: await getCsrfToken(context),
      },
    }
}

以及 [...nextauth].js 配置:

export const authOptions = {
    // Authentication providers
    providers: [
        CredentialsProvider({
            // The name to display on the sign in form (e.g. 'Sign in with...')
            id: 'credentials',
            name: 'Credentials',
            // The credentials is used to generate a suitable form on the sign in page.
            // You can specify whatever fields you are expecting to be submitted.
            // e.g. domain, username, password, 2FA token, etc.
            // You can pass any HTML attribute to the <input> tag through the object.
            credentials: {
                username: { label: "Phone", type: "text" },
                password: { label: "Password", type: "password" }
            },
            async authorize(credentials, req){
                const user_data = await checkCredentials(credentials.username, credentials.password);
                if(user_data.success){
                    return user_data.user;
                } else{
                    return null;
                }
            }        
        }),
        // ...add more providers here
    ],
    adapter : PrismaAdapter(prisma),
    session: {
        // Choose how you want to save the user session.
        // The default is `"jwt"`, an encrypted JWT (JWE) stored in the session cookie.
        // If you use an `adapter` however, we default it to `"database"` instead.
        // You can still force a JWT session by explicitly defining `"jwt"`.
        // When using `"database"`, the session cookie will only contain a `sessionToken` value,
        // which is used to look up the session in the database.
        strategy: "jwt",
      
        // Seconds - How long until an idle session expires and is no longer valid.
        maxAge: 30 * 24 * 60 * 60, // 30 days
      
        // Seconds - Throttle how frequently to write to database to extend a session.
        // Use it to limit write operations. Set to 0 to always update the database.
        // Note: This option is ignored if using JSON Web Tokens
        updateAge: 24 * 60 * 60, // 24 hours
        
        // The session token is usually either a random UUID or string, however if you
        // need a more customized session token string, you can define your own generate function.
        generateSessionToken: () => {
          return uid(32);
        }
    },
    callbacks: {
        async signIn({ user, account, profile, email, credentials }) {
            return Promise.resolve(true);
        },
        async redirect({ url, baseUrl }) {
            return Promise.resolve(url);
        },
        async jwt({ token, user, account, profile, isNewUser }) {
            return Promise.resolve(token);
        },
        async session({ session, token, user }) {
            // Send properties to the client, like an access_token from a provider.
            session.user.image = '';
            return Promise.resolve(session);
        },
        async credentials({ user, account, profile }){
            return Promise.resolve(true);
        }
    },
    secret: process.env.NEXTAUTH_SECRET,
    pages: {
        signIn: '/login'
    },
    debug: false
}

如果您有任何关于如何解决此问题的提示,请告诉我:)

reactjs next.js next-auth
3个回答
0
投票

我设法找出问题所在。我的 api 文件夹中有一个 index.js 文件,给出了标准的 ok 回复。删除路由后,Vercel 在访问 /api/auth/* 路由时显示 404 错误。结果我的 vercel.json 配置阻止了这些调用。删除 vercel.json 中的以下行后,一切正常:

"rewrites": [{ "source": "/api/(.*)", "destination": "/api" }]

0
投票

对于那些仍在寻找从

signIn
强制使用
getServerSideProps
的人,您可以将重定向返回到登录端点 -
/api/auth/signin/<provider>

例如。我想在浏览器中没有会话的情况下强制用户登录。

export async function getServerSideProps(ctx) {
  const session = await getSession(ctx);
  if (!session) {
    return {
      redirect: {
        destination: `/api/auth/signin/google`,
        permanent: false,
      },
    };
  } else {
    return {
      props: {},
    }
  }
}

export const Home = () => {
  return (
    <div>Welcome Home!</div>
  )
}

0
投票

我找到了一种从React服务器组件中解决它的方法,代码可能也可以工作并且在涉及

getServerSideProps
时类似。

import { AuthHandler } from "node_modules/next-auth/core";
import { type PageProps } from ".next/types/app/layout";
import { getServerAuthSession } from "@/server/auth";
import { cookies, headers } from "next/headers";
import { authOptions } from "@/server/auth";
import { type AuthAction } from "next-auth";
import { redirect } from "next/navigation";

export default async function Page({ searchParams }: PageProps) {
  const callbackUrl = (searchParams?.callbackUrl as string) ?? "/";
  const session = await getServerAuthSession();
  if (session) {
    redirect(callbackUrl);
  }
  const data = {
    req: {
      body: {
        csrfToken: cookies().get("next-auth.csrf-token")?.value.split("|")[0],
        callbackUrl,
        json: "true",
      },
      method: "POST",
      cookies: Object.fromEntries(
        cookies()
          .getAll()
          .map((c) => [c.name, c.value]),
      ),
      headers: Object.fromEntries(headers()),
      action: "signin" as AuthAction,
      providerId: "provider-id", // Replace to use the id of your own provider
      error: "provider-id", // Replace to use the id of your own provider
    },
    options: authOptions,
  };
  const res = await AuthHandler(data);
  redirect(res.redirect ?? "/");
© www.soinside.com 2019 - 2024. All rights reserved.