34
29

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Next.jsで特定ページに認証(ログインチェック)を書けるシンプルな方法

Last updated at Posted at 2021-09-05

next.jsで特定のページに認証を書ける方法のメモ。
認証を行うAuthコンポーネントを作成し、それで囲めば認証がかかる方法のチェック。

NextAuthとかのライブラリもありますが、一旦、「素の実装方法」を確認しておきたい。

やりたいこと

下記のようなフローを作りたい。

スクリーンショット 2021-09-05 10.48.14.png

準備

検証用のプロジェクト差k末井。

npx create-next-app next-auth-test
cd next-auth-test

今回は認証の方式(ログインの維持)としてCookieに認証情報を書き込んでそれが有るか、無いかでログイン状態を判断する実装としたいと思いますので、Cookieを簡単に扱うライブラリを入れます。

react-cookieってライブラリも試したのですが、未使用の変数や関数を定義する必要がありwarningがウザイのでjs-cookieを利用します。

npm install js-cookie

認証を行うAuthコンポーネントを記述するauth.jsをcomponents以下に作成。

mkdir components
touch components/auth.js

認証画面を担うlogin.jsと認証対象となるprivate.jsを作成。

touch pages/login.js pages/private.js

実装

では実装していきます。

auth.js

Cookieの有無をチェックしてあればそのまま、なければloginにリダイレクトさせるだけ。
Cookieチェックロジックをいろいろな方式に切り替えればいろいろ応用はできる想定。

Cookieに書き込む情報を認証Cookieとわからない名前の方がいいですし、安易に生成できない値がいいですし、できればサーバサイドでhttpOnlyで処理したほうがいいです(クライアントサイドでsignedIn=true等は直ぐに書き込めるので良くないです)。

childrenをpropsで受けとりreturnすれば子コンポーネントのレンダリングに進める。

components/auth.js
import { useRouter } from "next/router";
import Cookies from "js-cookie";

const Auth = ({ children }) => {

    //router
    const router = useRouter();

    //Cookieのチェック(これをいろいろ認証タイプにより変更)
    const signedIn = Cookies.get("signedIn");
    //signedInがtrueじゃなければ/loginへ
    if (signedIn !== "true") router.replace("/login");

    //何もなければ次へ(そのまま処理)
    return children;
}

export default Auth;

login.js

簡易ログインん画面を作成。
ここではログインボタン押すと無条件にログインしちゃいますが、ここでID, Password認証などを行えばいいでしょう。

login.js
import Link from "next/link";
import { useRouter } from "next/router";
import Cookies from "js-cookie";

const Login = () => {

    const router = useRouter();

    //ログイン処理(CookieにsignedIn=trueとする)
    const login = () => {
        Cookies.set("signedIn", "true");
        router.replace("/private");
    }

    return (
        <>
            <h1>Login</h1>
            <button onClick={login}>ログイン</button>
            <div>
                <Link href="/"><a>Homeへ</a></Link>
            </div>
        </>
    );
}

export default Login;

index.js

Homeページ。/privateにリンクを貼っているだけ。

index.js
import Link from "next/link";

const Home = () => {
  return (
    <>
      <h1>Home</h1>
      <div>
        <Link href="/private"><a>Privateへ</a></Link>
      </div>
    </>
  );
}

export default Home;

private.js

Authタグで囲んでアクセス時に認証状態であるかをチェックしています。
OKならそのまま表示、NGなら/loginへリダイレクト。
ログアウトはただCookieを消しているだけ。

private.js
import Link from "next/link";
import { useRouter } from "next/router";
import Cookies from "js-cookie";

//認証コンポーネント読み込み
import Auth from "../components/auth";

const Private = () => {

    const router = useRouter();

    //ログアウト処理
    const logout = () => {
        Cookies.remove("signedIn");
        router.replace("/login");
    }

    return (
        <Auth>
            <h1>Private</h1>
            <button onClick={logout}>ログアウト</button>
            <div>
                <Link href="/"><a>Homeへ</a></Link>
            </div>
        </Auth>
    );
}

export default Private;

react-router-domによるルーティングの方が認証においてはわかりやすいかな。。。

おまけ

cookieの偽装

ここではsignedIn=trueであればログイン状態としました。が、この状態はユーザーやXXS脆弱性があれば直ぐに偽造できます。一番簡単な方法はブラウザの開発者ツールのコンソールでJSを実行することです。

下記のようにコンソールにて、

document.cookie="signedIn=true"

とすることでログイン状態にすることができます。

スクリーンショット 2021-09-05 12.46.28.png

安全策としては、

  • とにかく認証情報をわからないような情報とする
  • 安易に生成できない、かつユーザー依存情報とする(不正が発生しても最悪、個人単位)
    • SPAならtoken情報を取得するのでとらいあえずそれを利用するなど
  • サーバ側管理とする
  • httpOnlyでサーバ側でCookie生成、削除を行う

とかでしょうかね。

34
29
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
34
29

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?