web-dev-qa-db-ja.com

Next.jsサンプル認証-認証に基づいた再ルーティング、他の関数のラップ

小さなプロジェクトで、認証付きのnext.jsを使用しようとしています。現在、認証は機能していますが、ナビゲーションバーにデータを表示できません。

私はもともとfirebaseでそれを使用していましたが、それ以上ではありません!!次に、以下で個別に認証を設定します。

これはサンプルリポジトリです。authとnext.jsのAPIが含まれています。これらを統合して、ログインとログアウトでAPI呼び出しのヘッダーセットを使用できるようにしています。

https://github.com/Hewlbern/example

基本的なログインおよびログアウト機能を取得するだけで、自分のWebサイトへのユーザーアクセスを制御できます。私はこれが本当に簡単であることを知っています-next.jsでそれを行う方法と、アプリのドキュメントページがどのように機能するかをかなり混乱しています:S

このAPIからの出力の表を表示し、出力されたjsonを(CSVなどに)ダウンロードする機能を提供しようとしています。したがって、クエリパラメータを使用した検索後に、ユーザーがログインした後のページでのみ使用できるようにすることがポイントです:)

以下は、私が使用しているログイン機能の例です。

import { useRef, useState } from 'react';
import React from 'react'
import PropTypes from 'prop-types'

import Layout from "../components/Layout";




export default function Login() {
  const emailRef = useRef<HTMLInputElement>(null);
  const passRef = useRef<HTMLInputElement>(null);
  const [message, setMessage] = useState<any>(null);
  async function handleLogin() {
    const resp = await fetch('http://localhost:3001/auth/login', {
      method: 'POST',
      headers: {
        'Content-Type': "application/x-www-form-urlencoded"
      },
      body: JSON.stringify({
        email: emailRef.current?.value,
        password: passRef.current?.value
      })
    });
    const json = await resp.json();
    setMessage(json);
  }

  return (
    <Layout>
      {JSON.stringify(message)}
      <input type="text" placeholder="email" ref={emailRef} />
      <input type="password" placeholder="password" ref={passRef} />
      <button onClick={handleLogin}>Login</button>
    </Layout>
  );
}

これはこのAPIリクエストへの投稿です

router.post('/login', (req, res) => {

// console.log(req.body)

    let email = req.body.email;
    let password = req.body.password;

    console.log(email,password)

    DatabaseService.GetUser(email).then(user => {
            if(user===null){
                res.sendStatus(404);
            }
            else{
                if(bcrypt.compareSync(password, user[0].password)) {
                    jwt.sign({user}, 'secretkey', { expiresIn: '30d' }, (err, token) => {
                        DatabaseService.SetSession(token,JSON.stringify(user[0].user_id)).then(inserted=>{
                            res.json({
                                token
                            });
                        });
                    });
                } else {
                    res.sendStatus(500);
                }
            }
        });
});

この小さな例だけで、リクエストを現在どのように送信しているのかが間違っていますか? (ログインが要求を受け取る形式だと思いますか?)

誰かが同様のことをしたか、これらの問題を解決する方法を知っているなら、私は本当にそれを感謝します:)

乾杯!

7
Michael Holborn

ここでお勧めするのは、認証状態の変化を「監視」するためにReactのコンテキストAPIを利用するカスタムフックを作成することです。次に、そのプロバイダーでアプリをラップすると、新しいカスタムフックを使用して、その認証状態で必要なことを柔軟に実行できます。

次に、Firebaseでの認証を使用してそのカスタムフックがどのように見えるかの例を示します。

import React, { createContext, useContext, useState } from 'react'

import { auth } from './services' // this is just firebase.auth()

const UserContext = createContext()

export const UserProvider = ({ children }) => {
  const [user, setUser] = useState(undefined)

  auth.onAuthStateChanged(setUser)

  return <UserContext.Provider value={user}>{children}</UserContext.Provider>
}

export const useUser = () => useContext(UserContext)

次に、アプリをUserProviderでラップするだけです。

このような:

import React, { StrictMode } from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter } from 'react-router-dom'

import App from './app'
import { UserProvider } from './hooks'

const rootElement = document.getElementById('root')
ReactDOM.render(
  <StrictMode>
    <BrowserRouter>
      <UserProvider>
        <App />
      </UserProvider>
    </BrowserRouter>
  </StrictMode>,
  rootElement
)

次に、例として、使用状況がログに記録された場合に、ログインページから自動的に離れるようにしたいとします。ユーザーがログインしている場合は、useEffectフックとuseHistoryフックを使用して/に移動できます。

このような何かが行います:

import React, { useEffect } from 'react'
import { useHistory } from 'react-router-dom'

import { useUser } from './hooks'

const LoginPage = () => {
  const history = useHistory()
  const user = useUser() // user is undefined if not logged in

  useEffect(() => {
    if (user) { // will run the condition if user exists
      history.Push('/')
    }

  }, [user])

  ...
}

次のようなものを使用して、ナビゲーションバーで実際にuserデータを使用することができます。

import React from 'react'
import { Link } from 'react-router-dom'

import { useUser } from './hooks'

const NavBar = () => {
  const user = useUser()

  return (
    <div>
      {user ?
        <Link to="/profile">Welcome, {user.displayName}</Link> :
        <Link to="/login">Login</Link>
      }
    </div>
  )
}

言うまでもなく、必要に応じてこれを変更することができますが、これらすべてにより、認証状態をクリーンで堅牢な方法で操作する方法が得られるはずです。

3