Bearer認証についてまとめてみた。
Bearer認証とは
HTTP認証スキームの一つで、ユーザーの身元を確認する手段として利用されます。
この認証スキームではクライアントサイドは所定の形式で
「Bearer」トークンをサーバーに送信します。
Bearerトークンは、基本的には認証情報を持つ長い文字列で
通常はサーバーサイドで生成される。
クライアントが初めてログインしたときに、
または新しいトークンを要求したときに、サーバーからクライアントに「Bearerトークン」
を送信されてその後のリクエストではクライアントがトークンを使用して自身を認証します。
バックエンドAPIとやりとりしてBearer認証を実装する
1 依存関係のインストール
npm install express body-parser jsonwebtoken
2 簡易APIの作成
nodeでAPIをつくりそことのやり取りでBearer認証を使うようにしてみる
const express = require('express');
const bodyParser = require('body-parser');
const jwt = require('jsonwebtoken');
const app = express();
const port = 3000;
const secretKey = 'your_secret_key';
app.use(bodyParser.json());
let users = [{ username: 'user1', password: 'password1' }];
app.post('/login', (req, res) => {
const { username, password } = req.body;
const user = users.find(u => u.username === username && u.password === password);
if (user) {
const token = jwt.sign({ username }, secretKey, { expiresIn: '1h' });
res.json({ token });
} else {
res.status(401).send('無効な資格情報です');
}
});
const authenticateToken = (req, res, next) => {
const token = req.headers['authorization'];
if (!token) return res.sendStatus(403);
jwt.verify(token.split(' ')[1], secretKey, (err, user) => {
if (err) return res.sendStatus(403);
req.user = user;
next();
});
};
app.get('/protected', authenticateToken, (req, res) => {
res.send('保護されたルート');
});
app.listen(port, () => {
console.log(`Server running on port ${port}`);
});
APIと接続する部分に関しては
async
awaitで非同期処理
async
*関数が必ず非同期処理であることを示します。
*asyncが付いた関数は、必ずPromiseを返します。
Promiseとは
主な3つの状態を手に入れる
Pending(保留中): 非同期処理がまだ完了していない、または開始していない状態。
Fulfilled(成功): 非同期処理が成功して、結果が利用可能な状態。
Rejected(失敗): 非同期処理が何らかの理由で失敗した状態。
await
*asnyc関数の中でのみ使用できます。
*Promiseの完了を待つ つまり上の三つと判定が入ったら次の行に進む
*awitは、Promiseが成功した場合にその結果を返します。失敗した場合はエラーをスローします。
ログインコンポーネントの作成
<template>
<div>
<h2>Login</h2>
<form @submit.prevent="login">
<input v-model="username" placeholder="Username" />
<input v-model="password" type="password" placeholder="Password" />
<button type="submit">Login</button>
</form>
</div>
</template>
<script>
import axios from "axios";
export default {
data() {
return {
username: "",
password: "",
};
},
methods: {
async login() {
try {
const response = await axios.post("http://localhost:3000/login", {
username: this.username,
password: this.password,
});
localStorage.setItem("token", response.data.token);
this.$router.push("/protected");
} catch (error) {
console.error("Login failed:", error);
}
},
},
};
</script>
vuexの概念を事前に入れておくといいと思う。
<form @submit.prevent="login">
<input v-model="username" placeholder="Username" />
<input v-model="password" type="password" placeholder="Password" />
<button type="submit">Login</button>
</form>
ボタンクリックがあった時にdispatchされる。
入力された情報
methods: {
async login() {
try {
const response = await axios.post("http://localhost:3000/login", {
username: this.username,
password: this.password,
});
入力された情報がhttp://localhost:3000/loginに渡される
server.js
let users = [{ username: "user1", password: "password1" }];
app.post("/login", (req, res) => {
const { username, password } = req.body;
const user = users.find(
(u) => u.username === username && u.password === password
);
入力されたものが合っているかチェックする場所になります
if (user) {
const token = jwt.sign({ username }, secretKey, { expiresIn: "1h" });
res.json({ token });
} else {
res.status(401).send("Invalid credentials");
}
もしユーザーがあればトークンを発行して
それ以外ではエラーを返す
const authenticateToken = (req, res, next) => {
// Authorization ヘッダーからトークンを抽出します
const token = req.headers["authorization"];
// トークンが存在しない場合は、403 Forbidden 応答を送信します
if (!token) return res.sendStatus(403);
// 秘密鍵を使用してトークンを検証します
jwt.verify(token.split(" ")[1], secretKey, (err, user) => {
// 検証中にエラーが発生した場合は、403 Forbidden 応答を送信します
if (err) return res.sendStatus(403);
// ユーザー情報を req オブジェクトに添付します
req.user = user;
// next() を呼び出して、次のミドルウェアまたはルート ハンドラーに制御を渡します。
next();
生成したtokenが有効なものかどうか確認します。
有効なものであれば
import { createRouter, createWebHistory } from "vue-router";
import Login from "@/components/Login.vue";
import Protected from "@/components/Protected.vue";
const routes = [
{
path: "/login",
name: "Login",
component: Login,
},
{
path: "/protected",
name: "Protected",
component: Protected,
},
];
const router = createRouter({
history: createWebHistory(),
routes,
});
export default router;
ルーティング経由で返却
Bearer認証の流れ
1 クライアントがログイン時にサーバーはアクセストークンを発行
2 発行されたトークンをクライアントのブラウザ内のsessionStrageかlocalStrageに保存
3 ページアクセス時にAuthorizationヘッダにトークンセットし、サーバーにRequest送信
(下記コード参照)
4 サーバーはトークンを確認して有効ならリクエストされたページをレスポンス認証に
失敗すると、サーバーは401Unauthorizedを送信する
(トークンの形式は、token68の形式で指定することを定められています)
*token68
token68は、1文字以上の半角英数字,(-ハイフン),(ドット)_(アンダーバー)
~(チルダ),+(プラス),/(スラッシュ)から構成された文字列
//SessionStrageからトークンを取得して変数に代入
const token = sessionStorage.getItem('access_token');
fetch("/api", {
method: 'GET',
// トークンを送信
headers: {
Authorization: 'Bearer ${token}'
}
}
発行したトークンはクライアント側がlocalStrageか、sessionStrage,
サーバー側はDBにそれぞれ保存します。
まとめ
Bearer認証はトークンをサーバー側に送信するしくみのこと
認証の確認:HTTP認証のスキームの一つで、ユーザーの身元を確認する手段として使用されます。
クライアントは「Bearer」トークンをサーバーに送信し、
サーバーはトークンの有効性を検証する。
「Bearerトークン」は、一般にJWT形式が使われることが多く、ペイロードには、
ユーザー情報や、有効期限などが含まれる
バックエンドAPIとやりとりしてBearer認証を実装する
- JWTの生成と検証には秘密鍵を使用している
* コードの流れ ログインコンポーネント
フォームの送信時に、クライアントからサーバーにログイン情報を送信して、
トークンを受け取ってローカルストレージに保存し保護されたルートにリダイレクトします。
=>ログインが失敗したときのエラーハンドリングも含まれているのが重要です。
- コードの流れ サーバーサイド
ユーザー情報の検証、トークンの生成、トークンの検証、保護されたルートの設定など
手順が含まれています。
Discussion