0
1

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 1 year has passed since last update.

タスク管理ツールをReact + ASP.Coreで作る021認証状態に従って画面表示を制御

Last updated at Posted at 2022-11-10

前回の続きです。

今回は、認証状態に従って、ログイン画面・タスク管理画面などの画面表示を制御する様にしてみます。

ガイド

全体Index:タスク管理ツールをReact + ASP.Coreで作る - Index

認証機能追加の一連の流れ

サーバーサイドに現在の認証情報

基本的なコンセプトは以下です
・認証されていなければ、ログイン画面を出す。認証されている時だけ、タスク管理画面を出す。

もう少し具体的な仕様に落とすと、以下になります。

  • 画面の表示制御
    • 「認証済」ならタスク管理画面を出す
    • 「認証未」ならログイン画面と登録画面を出す。
  • 認証状態の把握
    • 「userInfo」Stateのユーザー名(username)に値が入っているかどうかでログイン状態かどうかを判断する。
  • 「userInfo」Stateの設定方法
    • アプリケーション読み込み時に、ブラウザにトークンが保存されている場合は、トークンを使ってサーバーサイドに問い合わせを行う。(問い合わせの結果成功の場合は、同時に帰ってくるユーザー名をStateに設定する)
    • ログイン処理を行って成功した場合も、ログイン結果の回答と同時に帰ってくるユーザー名をStateに設定する。この時トークンを合わせてブラウザに保存する
    • トークンを使ってサーバーサイドに問い合わせても失敗した場合(有効期限切れ等)はStateを空にし、「認証未」の状態とみなせるようにする

サーバーサイドの変更

有効な認証トークンを渡すとユーザー情報を返す関数を追加します

AccountController.cs
+        [Authorize]
+        [HttpGet]
+        public async Task<ActionResult<UserModel>> GetCurrentUser()
+        {
+            var user = await +_userManager.FindByEmailAsync(User.FindFirstValue(ClaimTypes.Email));
+            return CreateUserObject(user);
+        }

クライアントサイドの変更

APIから現状のユーザー情報を取得する通信機能を追加します

xxx.ts

const Account = {
+   current: () => axios.get<UserInfo>(`/account`).then((response: AxiosResponse<UserInfo>)=>response.data),
    login: (user: UserFormValues) => axios.post<UserInfo>(`/account/login`, user).then((response: AxiosResponse<UserInfo>)=>response.data),
    register: (user: UserFormValues) => axios.post<UserInfo>(`/account/register`, user).then((response: AxiosResponse<UserInfo>)=>response.data),
}

APIから認証状態を把握する機能を追加します。
具体的にはブラウザに保管されているのトークンを取得してユーザー情報取得APIをたたき、返ってきた結果をもとに出し分けを実行します。

App.tsx
+import { useEffect, useState } from 'react';
import './App.css';
+import api from './app/api/api';
+import { UserInfo } from './app/models/Account';
import Login from './components/Login';
import Register  from './components/Register';
import { TaskOperationMain } from './components/TaskOperationMain';

function App() {

  
+  const [userInfo, setUserInfo] = useState<UserInfo>({username: '',email: '',token: ''});
+  
+  useEffect(() => {
+    const token = window.localStorage.getItem('tasket_jwt_token');
+    try{
+      api.Account.current().then(user => {
+        window.localStorage.setItem('tasket_jwt_token', user.token);
+        setUserInfo(user);
+      });
+      
+    } catch (error) {
+      console.log(error);
+    }
+  }, []);

  
  return (
    <div>
+      {userInfo.username==='' ? 
+      <>
+        <Login setUserInfo={setUserInfo}/>
+        <Register /> 
+      </>
+      :
+      <TaskOperationMain />
+      }
      
     </div>
  );
}
export default App;

ログイン機能の変更

認証が成功した時に、取得したトークンをブラウザに保存する機能を追加します

tabc
import { ErrorMessage,  Formik } from 'formik';
import React from 'react';
import { Form } from 'react-bootstrap';
import * as Yup from 'yup';
import api from '../app/api/api';
import TextInputGeneral from '../app/common/TextInputGeneral';
+ import { UserInfo } from '../app/models/Account';



+ interface Props {
+     setUserInfo: React.Dispatch<React.SetStateAction<UserInfo>>;
+ }


const Login = (
+     {setUserInfo}: Props
    ) => 
{
    
    return (
        <>

        <Formik
            initialValues={{email:'', password: '', error: null}}
            onSubmit={async (values, {setErrors}) => {
                const content = await api.Account.login(values).catch(error => 
                    setErrors({error:'Invalid email or password'}));
                    content?.token && window.localStorage.setItem('tasket_jwt_token', content.token);
+                    if(content){
+                        window.localStorage.setItem('tasket_jwt_token', content.token);
+                        setUserInfo(content);
+                    }
                }
            }
            validationSchema={Yup.object({
                email: Yup.string().required().email(),
                password: Yup.string().required(),
            })}
            >
                {({handleSubmit, isSubmitting, errors, isValid, dirty}) =>(
                    <Form className="ui form" onSubmit={handleSubmit} autoComplete='off'>
                        <h3>Login</h3>
                        <TextInputGeneral name='email' placeholder="Email" />
                        <TextInputGeneral name='password' placeholder="Password" type="password" />
                        <ErrorMessage 
                            name='error' render={() => 
                                <Form.Label style = {{marginBottom:10}} basic color='red' >{errors.error}</Form.Label>
                        }
                        />
                        <button disabled={!isValid || !dirty || isSubmitting} type = 'submit' className="btn btn-primary">Login</button>
                    </Form>
                )}
            </Formik>
        </>
    );

}

export default Login;

結果

これまでの反映後再実行すると、以下の様にログイン前の状態ではログイン画面(といいつつユーザー登録画面も出てきてしまってまだちょっとカッコ悪いですが)、ログイン後の状態では認証画面は出ずにタスク管理画面が表示されるようになりました。

今回は以上です。
続きは次回です

0
1
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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?