前回:Laravelに入門したい!(Laravel APIサーバー+React SPA)vol.1
今回は、ログイン/ログアウト/ユーザーデータ取得/メールベリファイ/パスワード忘れを確認してみる。
ログイン
前回同様、テスト用のコンポーネントを作っていく
import { Box, Button, Stack, TextField } from "@mui/material";
import axios from "axios";
import React from "react";
export default function TestLogin(): JSX.Element {
const handleLogin = React.useCallback((e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
const formData = new FormData(e.currentTarget)
axios.post("/login", {
"email": formData.get("email"),
"password": formData.get("password"),
}, {
withCredentials: true
}).then(res => {
console.log(res.data)
}).catch(e => {
console.error(e)
})
}, [])
return (
<Box
p={2}
>
<Box
component={"form"}
p={3}
onSubmit={handleLogin}
>
<Stack
spacing={2}
>
<TextField
label="email"
name="email"
type="email"
size='small'
required
/>
<TextField
label="password"
name="password"
type='password'
size='small'
required
/>
<Button
variant='outlined'
fullWidth
type='submit'
>Login!</Button>
</Stack>
</Box>
</Box>
)
}
ログインするだけ!結果はコンソールに表示。
ログアウト
import { Box, Button } from "@mui/material";
import axios from "axios";
import React from "react";
export default function TestLogout(): JSX.Element {
const handleLogout = React.useCallback((e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault()
axios.post("http://localhost:8000/logout", undefined, {
withCredentials: true
}).then(res => {
console.log(res.data)
}).catch(e => {
console.error(e)
})
}, [])
return (
<Box
p={3}
>
<Button
variant='outlined'
fullWidth
onClick={handleLogout}
>Logout!</Button>
</Box>
)
}
こちらもログアウトするだけ。結果はコンソールに表示。
ユーザーデータ取得
import { Box, Button, Paper, Typography } from "@mui/material";
import axios from "axios";
import React from "react";
interface UserData {
id: number
name: string
email: string
created_at: string
updated_at: string
email_vefified_at: string
}
export default function TestUser(): JSX.Element {
const [userdata, setUserdata] = React.useState<UserData>()
const handleUser = React.useCallback((e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault()
axios.get("http://localhost:8000/api/user", {
withCredentials: true
}).then(res => {
console.log(res.data)
setUserdata(res.data)
}).catch(e => {
console.error(e)
setUserdata(undefined)
})
}, [])
return (
<Box
p={3}
>
<Button
variant='outlined'
fullWidth
onClick={handleUser}
>User!</Button>
<Paper
variant="outlined"
sx={{
mt: 2
}}
>
{userdata ? (
<Box
p={2}
textAlign={"left"}
>
<Typography>id: {userdata?.id}</Typography>
<Typography>name: {userdata?.name}</Typography>
<Typography>email: {userdata?.email}</Typography>
<Typography>created_at: {userdata?.created_at}</Typography>
<Typography>updated_at: {userdata?.updated_at}</Typography>
<Typography>email_vefified_at: {userdata?.email_vefified_at}</Typography>
</Box>
) : (
<Box
p={2}
textAlign={"left"}
>
<Typography>not login</Typography>
</Box>
)}
</Paper>
</Box>
)
}
ユーザーデータを取得して表示する。
メールベリファイ
import { Box, Button } from "@mui/material";
import axios from "axios";
import React from "react";
export default function TestEmailVerificationNotification(): JSX.Element {
const handleEmailVerificationNotification = React.useCallback((e: React.MouseEvent<HTMLButtonElement>) => {
e.preventDefault()
axios.post("http://localhost:8000/email/verification-notification", undefined, {
withCredentials: true
}).then(res => {
console.log(res.data)
}).catch(e => {
console.error(e)
})
}, [])
return (
<Box
p={2}
>
<Button
variant='outlined'
fullWidth
onClick={handleEmailVerificationNotification}
>EmailVerificationNotification!</Button>
</Box>
)
}
メールベリファイ通知を送るだけ。結果はコンソールに表示。
パスワード忘れ・リセット
import { Box, Button, Stack, TextField } from "@mui/material";
import axios from "axios";
import React from "react";
export default function TestForgotPassword(): JSX.Element {
const handleForgotPassword = React.useCallback((e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
const formData = new FormData(e.currentTarget)
axios.post("http://localhost:8000/forgot-password", {
"email" : formData.get("email")
}, {
withCredentials: true
}).then(res => {
console.log(res.data)
}).catch(e => {
console.error(e)
})
}, [])
return (
<Box
component={"form"}
p={3}
onSubmit={handleForgotPassword}
>
<Stack
spacing={2}
>
<TextField
label="email"
name="email"
type="email"
size='small'
required
/>
<Button
variant='outlined'
fullWidth
type='submit'
>ForgotPassword!</Button>
</Stack>
</Box>
)
}
パスワードを忘れたよーを送信するだけ。結果はコンソールに表示。
バックエンドからメールが送信される。
import { Box, Button, Stack, TextField } from "@mui/material";
import React from "react";
import axios from "axios";
export default function TestResetPassword(): JSX.Element {
const handleResetPassword = React.useCallback((e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
const formData = new FormData(e.currentTarget)
axios.post("http://localhost:8000/reset-password", {
token: formData.get("token"),
email: formData.get("email"),
password: formData.get("password"),
password_confirmation: formData.get("password_confirmation"),
}, {
withCredentials: true
}).then(res => {
console.log(res.data)
}).catch(e => {
console.error(e)
})
}, [])
return (
<Box
component={"form"}
p={3}
onSubmit={handleResetPassword}
>
<Stack
spacing={2}
>
<TextField
label="token"
name="token"
type="text"
size='small'
required
/>
<TextField
label="email"
name="email"
type="email"
size='small'
required
/>
<TextField
label="password"
name="password"
type="password"
size='small'
required
/>
<TextField
label="pasword(confirm)"
name="password_confirmation"
type='password'
size='small'
required
/>
<Button
variant='outlined'
fullWidth
type='submit'
>ResetPassword!</Button>
</Stack>
</Box>
)
}
パスワード忘れたよーのメールに記載されたtokenを使って、パスワードをリセットする。結果はコンソールに表示。
各コンポーネントを表示
import { Paper, Stack } from '@mui/material';
import './App.css';
import TestCSRF from './test/TestCSRF';
import TestCreateUser from './test/TestCreateUser';
import TestEmailVerificationNotification from './test/TestEmailVerificationNotification';
import TestForgotPassword from './test/TestForgotPassword';
import TestLogin from './test/TestLogin';
import TestLogout from './test/TestLogout';
import TestResetPassword from './test/TestResetPassword';
import TestUser from './test/TestUser';
function App() {
return (
<div className="App">
<Stack
spacing={3}
p={3}
mx={"auto"}
maxWidth={"sm"}
>
<Paper
variant='outlined'
>
<TestCSRF />
</Paper>
<Paper
variant='outlined'
>
<TestCreateUser />
</Paper>
<Paper
variant='outlined'
>
<TestLogin />
</Paper>
<Paper
variant='outlined'
>
<TestLogout />
</Paper>
<Paper
variant='outlined'
>
<TestUser />
</Paper>
<Paper
variant='outlined'
>
<TestEmailVerificationNotification />
</Paper>
<Paper
variant='outlined'
>
<TestForgotPassword />
</Paper>
<Paper
variant='outlined'
>
<TestResetPassword />
</Paper>
</Stack>
</div>
);
}
export default App;
表示してみる
表示するとこんな感じ

もうちょっと見やすく作ればよかったと思う後悔はおいといて、これで、デフォルトで追加されているルートはだいたい利用できる。
残ったルートは、
GET|HEAD /
POST _ignition/execute-solution
GET|HEAD _ignition/health-check
POST _ignition/update-config
GET|HEAD verify-email/{id}/{hash}
くらい。
「/」はバージョンを表示させるだけのルート
「_ignition/・・・」はデバッグ用のルート
「verify-email/{id}/{hash}」はメールベリファイ用のルート(メールに載るURL)
動作を確認してみる
長々と確認したことを書いてみる。
まずは、ログインしているかしていないのかよくわからない状態なので、「USER!」ボタンを押してみる
「not login」のままの場合と、ユーザーデータが表示される場合がある。
これは、ユーザーを作成したあとすぐに「USER!」ボタンを押すと、ユーザーデータが表示される模様。
おそらくユーザー作成時、自動的にログインする仕様になっているということであろう。
ログインできていない場合は、emailとpasswordを入力して、「LOGIN!」を押してみる。
そのあと、「USER!」ボタンを押すと、ユーザーデータが表示される。
あれ、、、?CSRFトークンは?と思ったけど、ログインできていなくても、「/api/user」にアクセスしたときに、CSRFトークンがセットされている模様。
試しに、バージョンを取得する「/」にアクセスしてみた場合もCSRFトークンがセットされていたので、
単純にGETの場合は、CSRFトークンがセットされるのかな?
とにかく、ログインできたので、そのままログアウトを試す。
「LOGOUT!」ボタンからの「USER!」ボタンで、「not login」が表示される。
「EmailVerificationNotification!」ボタンはログインが必要なので、ログインしてから、「EmailVerificationNotification!」ボタンを押す。
すると、backendがメールを送信する。メールの確認はMailDevを使う。docker-compose.ymlをLaravelに入門したい!(Laravel APIサーバー+React SPA)vol.1で設定したものと同じにしてあれば、http://localhost:1080/ で確認ができる。
メールのVerify Email Addressをクリックすると、メール確認ができる。
メールベリファイが完了したら、ユーザーデータを取得したとき「email_verified_at」にメールベリファイ完了日時が入る。
パスワード忘れによるパスワードリセットは、ログアウトしている状態である必要があるので、いったんログアウトする。
そのあと、emailを入力して、「FORGOT PASSWORD!」をクリックすると、メールが送信される。(ログインしている場合は、/dashboardにリダイレクト、メールアドレスが見つからないときは、「422」エラー)
本当は、メールのリンクをクリックするとtokenをSPA内で取得するようにすべきだけど、今回は、そんなことしていないので、メールに記載されているURL「http://localhost:3000/password-reset/〇〇?email=△△」の〇〇の部分がtokenなので、そこをコピペして、token、email、password、password(confirm)を入力して、「RESETPASSWORD!」をクリック。
新しいパスワードでログインができるか確認してみる。
ここまでやってみて、、、
パスワードリセットが最初から使えるのはありがたい!
でも、メールアドレスが見つからないとき「422」返す仕様とか、やたら、dashboardにリダイレクトしたがる仕様とか、(あたりまえだけど)実際に使うにはいろいろとカスタマイズしないといけないなと。
あと、個人的には、正常完了時、dataに何も入っていないので、{status: “ok”}くらいは返しときたいと思ったりする。
次回は、ここから仕様を変更していこうかな。

コメントを残す