【React(SPA) + Laravel】  login認証の状態をブラウザ再リロード後も維持する

## 前提（利用技術）

・フロント・・・React 18系 + typescript + contextを利用
・バック・・・Laravel 8系 + Laravel Sanctum（サンクタム）を利用（SPA認証）

フロントエンドでReactをSPA（シングルページアプリケーション）として作成。
バックエンドはLaravelで、APIサーバーとして利用しています。

###  ちなみに Laravel sanctumとは？
新し目のlaravelにはデフォルトでついている認証関連のミドルウェア。
sanctumを使うことでユーザー登録、ログインなどを簡単に実装できます。

これを使ったログイン手法は公式や、またいろいろな記事があるので、本記事では省略いたします。
https://readouble.com/laravel/8.x/ja/sanctum.html
https://qiita.com/ucan-lab/items/3e7045e49658763a9566


## やりたいこと

Reactでユーザー登録、ログイン機能を実装します。
ざっくりとした登録時、ログイン時の流れとしては以下。

【1】Reactからユーザーのメアド、パスワードをバックエンド（Laravel）側に送り、
認証できた場合はユーザー情報を返します。(ユーザー登録時も同様)
※この辺りの実装方法は前述の参考記事などをご参考ください。

【2】受け取ったユーザー情報を、React側ではグローバルな値として持ちます。
あまりグローバルにたくさんの情報を持ちたくないので、よくあるものとして、
ヘッダー部分に表示する
「ユーザー名」
「user_id」
「ログイン有無」
とかあれば十分でしょうか。

（※それ以外の詳細情報（メアドとか生年月日とか）は必要なページごとにuser_idから改めて取得すれば良いと思います。）

・未ログイン状態
![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287983/8bf1fa2b-02f2-4edb-9be5-d43b41e790ea.png)

・ログイン状態
![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287983/313b9911-6d9d-8769-dbbe-747d2baebffd.png)


## ようやく問題点
前述の方法でReact(SPA) + Laravel環境でのログインや会員登録は可能です。
ですが、ReactはSPAで作成しています。ので
#### ブラウザの再リロードをすると、contextに保管した「名前」、「ログインしているよという状態」、「id」などを失うことになります。

ログイン状態でいろんなページをlinkタグで遷移していると問題なくログイン状態のままですが、
ブラウザの再リロードをしたり、一度閉じて、urlを直に開くとログイン状態が消えてしまう、ということになります。

spaについてはこちらあたりがわかりやすいです！
https://qiita.com/shinkai_/items/79e539b614ac52e48ca4

## 解決方法
sactumには、ユーザーの「ログイン状態チェック」用のミドルウェアがあります。
https://readouble.com/laravel/8.x/ja/sanctum.html
の「ルートの保護」の箇所です。
下記のルーティングを作っておけば、reactから下記にリクエストを投げることで、
そのブラウザ自体にログイン認証有無の状態をチェックさせ、ログインしていればuser情報を返すことができます。

```php
//こちらをroutesのapi.phpに記述
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});

```
sanctumのログイン状態については、専用の情報を持たせた一意のクッキーを通信に持たせて、ブラウザからの通信にそのクッキーがあれば認証を行なっています。
※基本的に下記のlaravel標準の認証機能を使っている様子。
https://readouble.com/laravel/master/ja/authentication.html

なので、この「ブラウザにuserがログインしているか」状態を、
アプリケーションの最初に一度だけチェックすれば、リロード時にも再度ログインチェックをすることができます。
（今回はそれをcontextで利用。）

## 具体的な調整内容（コード記述内容）

◼Laravel側
routes.api
```php
//前述のコード
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});

```

◼React側
関数の定義

```tsx

import axios, { AxiosResponse } from "axios";


/**
 * ブラウザリロード時に利用。関数定義がわ
 */
export const LogInCheck = ():Promise<AxiosResponse> =>{
	axios.get(
         'サーバーurl/sanctum/csrf-cookie',
         { withCredentials: true }) // CSRFトークンの初期化
    	const response = axios.get(
    		'サーバーurl/user',
    		{withCredentials:true}	
	)
	return response;
}


```

プロバイダーを定義（ファイル箇所などは任意）

```tsx

import  {useState,createContext,useEffect} from "react";


//型定義
//ここでは名前、ログイン有無、user_idだけ渡します。)
type LoggedInContextType ={
    setUserAuth:(value:boolean) => void;
    setUserID: (value: number) => void;
    setUserName: (value: string) => void;
    userAuth:boolean;
    userid: number;
    username: string;
}

export const LoggedInContext = createContext<LoggedInContextType>({} as LoggedInContextType);

  
//認証情報とセットするコンテキスト
export const LoggedInProvider = (props) => {
    const { children } = props;

     // globalなstateのデフォルトの値を作成
     const [username, setUserName] = useState<string>('');
     const [userid, setUserID] = useState<number>(0);     
     const [userAuth, setUserAuth] = useState<boolean>(false);

    //初回だけ、ログインチェックを行う
	useEffect(()=>{
		LogInCheck().
			then((res) => {
				setUserName(res.data.name);
				setUserID(res.data.user_id);
				setUseremail(res.data.email);
				setUserAuth(true);
			})
	},[])


    return (
        <LoggedInContext.Provider value={{username,setUserName,userid,setUserID,userAuth,setUserAuth}}>
            {children}
        </LoggedInContext.Provider>
    )
}

```



大元のアプリ定義
ここでプロバイダーを使用することで、全ルートで一度だけログインチェックが走ります

App.tsx
```tsx

import { BrowserRouter } from "react-router-dom";
import { MainRoute } from "../src/router/MainRoute";
import { LoggedInProvider} from "./provider/LoggedInProvider";


function App() {
  return (
    <>
    	<BrowserRouter>
            <LoggedInProvider>
                 <MainRoute />
            </LoggedInProvider>
    	</BrowserRouter>
    </>
  );
}

export default App;

```


## まとめ
以上の記述で、ブラウザを再リロードしてもきちんとログイン状態を保持できるようになりました。
※もちろんログアウトしクッキーを捨てればログアウトされます。

ログイン、登録情報はネットに転がっていたのですが、
リロード後にどうその情報を残すか？ということを明言している記事があまり見当たらなかったので、勉強になりました！