M16SOGEKI
@M16SOGEKI

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

Next.jsとSupabaseを用いたログイン機能の作成

解決したいこと

Next.jsとsupabaseのデータベースの接続

例)
Next.jsでsupabaseのPostgreSQLを用いて生徒側のログインと管理者側のログイン機能を作成しようとしていますが、データベースに登録されている値と全く同じにもかかわらず認証できません。

発生している問題・エラー

GET /api/auth/providers 200 in 30ms
GET /api/auth/csrf 200 in 17ms
POST /api/auth/callback/credentials 401 in 161ms

スクリーンショット 2024-11-17 012840.png

スクリーンショット 2024-11-17 013852.png

該当するソースコード

LoginModal.tsx

'use client';

import React, { useState } from "react";
import { Button, Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Input, Checkbox, Link, Spacer } from '@nextui-org/react';
import { useRouter } from 'next/navigation';
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm, SubmitHandler } from "react-hook-form";
import { InputsType, inputs } from "@/schema/LoginSchema";
import { signIn } from 'next-auth/react';

interface StudentModalProps {
    showFlag: boolean;
    ChangeFlag: () => void;
}

export const StudentLoginModal: React.FC<StudentModalProps> = (props) => {

    const [studentId, setStudentId] = useState('');
    const [password, setPassword] = useState('');
    const router = useRouter();

    const {
        register,
        handleSubmit,
        reset,
        formState: { errors },
    } = useForm<InputsType>({
        resolver: zodResolver(inputs)
    });

    const onChangeModal = () => {
        props.ChangeFlag();
        reset();
        setStudentId('');
        setPassword('');
    };

    const onSubmit: SubmitHandler<InputsType> = async (data) => {
        try {
            const result = await signIn("credentials", {
                redirect: false,
                student_id: data.student_id,
                password: data.password,
            });
    
            if (result?.ok) {
                router.push('/dashboard');
            } else {
                alert('ログインに失敗しました。IDまたはパスワードを確認してください。');
            }
        } catch (error) {
            console.error('認証中にエラーが発生しました:', error);
            alert('エラーが発生しました。再度お試しください。');
        }
    };
    

    return (
        <>
            <Modal backdrop="blur" isOpen={props.showFlag} onOpenChange={onChangeModal}>
                <ModalContent>
                    {(onClose) => (
                        <form onSubmit={handleSubmit(onSubmit)}>
                            <ModalHeader className="flex flex-col gap-1">ログイン情報を入力</ModalHeader>
                            <ModalBody className="items-center justify-center">
                                <Input
                                    {...register("student_id")}
                                    autoFocus
                                    label="Student ID"
                                    placeholder="Enter your student ID"
                                    variant="bordered"
                                    value={studentId}
                                    onChange={(e) => setStudentId(e.target.value)}
                                />
                                {errors.student_id && <p className="text-red-500 text-xs mt-1">{errors.student_id.message}</p>}

                                <Input
                                    {...register("password")}
                                    label="Password"
                                    placeholder="Enter your password"
                                    type="password"
                                    variant="bordered"
                                    value={password}
                                    onChange={(e) => setPassword(e.target.value)}
                                />
                                {errors.password && <p className="text-red-500 text-xs mt-1">{errors.password.message}</p>}

                                <div className="flex py-2 px-1 justify-between">
                                    <Checkbox
                                        classNames={{
                                            label: "text-small",
                                        }}
                                    >
                                        ログインを記憶する
                                    </Checkbox>
                                    <Spacer className="px-14" />
                                    <Link color="primary" href="#" size="sm">
                                        Forgot password?
                                    </Link>
                                </div>
                            </ModalBody>
                            <ModalFooter>
                                <Button color="danger" variant="light" onPress={onClose}>
                                    キャンセル
                                </Button>
                                <Button color="primary" type="submit" onClick={handleSubmit(onSubmit)}>
                                    ログイン
                                </Button>
                            </ModalFooter>
                        </form>
                    )}
                </ModalContent>
            </Modal>
        </>
    );
};

AuthOption.tsx

import CredentialsProvider from 'next-auth/providers/credentials';
import { NextAuthOptions } from 'next-auth';
import { supabase } from '@/lib/supabaseClient';
import bcrypt from 'bcrypt';

export const authOptions: NextAuthOptions = {
  providers: [
    CredentialsProvider({
      name: 'Credential',
      credentials: {
        student_id: { label: 'student_id', type: 'text' },
        password: { label: 'password', type: 'text' },
      },
      async authorize(credentials) {
        if (!credentials) throw new Error('No credentials provided');

        const { data, error } = await supabase
          .from('Student')
          .select('student_id, password')
          .eq('student_id', credentials.student_id)
          .single();

        if (!data) {
          console.error('User not found:', error);
          return null;
        }
        else if(error){
          console.error('Supabase error:', error);
          return null;
        }

        alert(data.password)
        //const isPasswordValid = await bcrypt.compare(credentials.password, data.password);
        if (credentials.password == data.password) {
          console.error('Invalid password');
          return null;
        }

        return { id: data.student_id };
      },
    }),
  ],
  session: { strategy: 'jwt' },
  callbacks: {
    async jwt({ token, user }) {
      if (user) token.id = user.id;
      return token;
    },
    async session({ session, token }) {
      session.user = { ...session.user, id: token.id as string };
      return session;
    },
  },
  pages: { signIn: '/login' },
  debug: true,
};

※bcryptはデータベースと参照のため無効にしてあります。

route.ts

import NextAuth from 'next-auth';
import { authOptions } from '@/lib/authOptions';

const handler = NextAuth(authOptions);

export { handler as GET, handler as POST };

ダイレクト接続も試みましたがダメでした。

0

1Answer

        if (credentials.password == data.password) {
          console.error('Invalid password');
          return null;
        }

パスワードが一致してるのにnullを返してるせいでは?

0Like

Comments

  1. @M16SOGEKI

    Questioner

    気が付きませんでした...

            if (credentials.password != data.password) {
              console.error('Invalid password');
              return null;
            }
    
            return { id: data.student_id };
    

    このように書き換えたのですが、やはり結果は同じでした。

    [next-auth][warn][DEBUG_ENABLED]
    https://next-auth.js.org/warnings#debug_enabled
     GET /api/auth/session 200 in 8154ms
     GET /api/auth/providers 200 in 58ms
     GET /api/auth/csrf 200 in 23ms
     POST /api/auth/callback/credentials 401 in 464ms
    (node:12516) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
    (Use `node --trace-deprecation ...` to show where the warning was created)
    

Your answer might help someone💌