TypeScriptとExpressでJWT認証機能を実装する方法!初心者向けバックエンド開発チュートリアル
生徒
「Webサイトやアプリでよく見るログイン機能を作りたいです。TypeScriptとExpressを使ったバックエンドの開発で、安全にユーザーを管理する仕組みはありますか?」
先生
「それなら、ジェイウェブトークンと呼ばれる仕組みを使うのが主流ですね。これを使うと、鍵がかかった会員専用のページや、仕組みを簡単に作ることができます。」
生徒
「初心者でもプログラムのコードを書くことはできますか?」
先生
「基本から順番に進めれば大丈夫です。開発の準備から実際の動かし方まで、仕組みを詳しく解説していきますね!」
1. バックエンド開発におけるJWT認証とは何か?
ウェブサイトやスマートフォンのアプリを開発するとき、ログインしている人だけが見られる秘密のページを作りたいことがあります。例えば、SNSのマイページや、ネットショッピングの購入履歴などです。このように、利用者が本人であるかを確認し、特定の行動を許可する仕組みを認証と呼びます。
今回学習する仕組みは、日本語で「ジェイウェブトークン」と読みます。これは、サーバーから発行される「デジタル会員証」のようなものです。利用者が最初にユーザー名とパスワードを入力してログインに成功すると、サーバーはこのデジタル会員証を暗号の技術を使って作成し、利用者のパソコンやスマートフォンに渡します。
二回目以降の通信では、利用者がこのデジタル会員証をサーバーに見せるだけで、ログイン状態が維持されます。これにより、何度もパスワードを入力する手間が省けます。この仕組みは、情報を暗号化して安全にやり取りできるため、現在のシステム開発で非常に広く使われています。
2. 開発に必要な環境とNode.jsやExpressの基礎知識
プログラムを作成する前に、パソコンでプログラムを動かすための基礎知識と道具を確認しましょう。今回は、サーバー側で動くシステムである「バックエンド」の開発を行います。バックエンドとは、データの保存や計算など、画面の裏側で働く仕組みのことです。
まず、パソコンの上でプログラミング言語のJavaScriptを動かすための環境が必要です。これを「ノードジェーエス」と呼びます。そして、この環境の上で、ウェブサーバーの機能を簡単に作れるようにした部品の集まりを「エクスプレス」と呼びます。エクスプレスを使うことで、特定のURLにアクセスがあったときに、特定の処理を返す仕組みを短いコードで書くことができます。
さらに、今回は型と呼ばれる安全装置がついた「タイプスクリプト」という言語を使います。タイプスクリプトを使うことで、プログラムの書き間違いを事前にパソコンが教えてくれるため、バグの少ない安全なバックエンドシステムを開発することができます。
3. 認証機能の実装に必要な準備と設定ファイルの作成
開発を始めるためには、プロジェクトの初期設定を行う必要があります。まず、パソコンのコマンドを入力する画面で、必要な道具をダウンロードします。今回は、プログラム内でデジタル会員証を作成するために、専用の外部部品をインストールします。
また、タイプスクリプトの設定ファイルを用意する必要があります。この設定ファイルは、どのような規則でプログラムを変換するかをパソコンに指示するためのものです。ここでは、最もシンプルで分かりやすい設定の例を確認しましょう。以下のコードは、プロジェクトを開始するための基本的な設定を記述したファイルの内容です。
// tsconfig.json という設定ファイルのイメージです
{
"compilerOptions": {
"target": "es2022",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
この設定を行うことで、これから書くタイプスクリプトのコードが、パソコンで正しく解釈されて動くようになります。準備ができたら、具体的なプログラムの記述に進んでいきましょう。
4. ユーザーログインとデジタル会員証を発行するプログラム
それでは、最初にユーザーがログインを試みたときに、デジタル会員証を発行する処理を作ります。利用者が送ってきたユーザー名とパスワードが、あらかじめ登録されているものと一致しているかを判定します。
一致していた場合は、秘密の鍵を使ってデジタル会員証を作成します。この会員証には、ユーザーの識別番号や、有効期限などの情報が含まれます。有効期限が切れると、その会員証は使えなくなるため、安全性が高まります。まずは、ログインを受け付けて、会員証を文字のデータとして返却するシンプルなプログラムを見てみましょう。
import express from 'express';
import jwt from 'jsonwebtoken';
const app = express();
app.use(express.json());
// 秘密の鍵(本番では他人に教えてはいけません)
const SECRET_KEY = "my_super_secret_key";
app.post('/login', (req, res) => {
const { username, password } = req.body;
// 簡易的なユーザー確認(実際はデータベースを確認します)
if (username === "taro" && password === "password123") {
// 会員証に含めるデータ
const payload = { user: username };
// 会員証を発行(有効期限を1時間にする)
const token = jwt.sign(payload, SECRET_KEY, { expiresIn: '1h' });
res.json({ message: "ログイン成功", token: token });
} else {
res.status(401).json({ message: "ユーザー名またはパスワードが違います" });
}
});
app.listen(3000, () => {
console.log("サーバーがポート3000で起動しました");
});
このプログラムを起動して、正しい情報が送られてくると、サーバーは長い文字列を返します。その文字列こそが、次回以降の通信で本人確認のために使用するデジタル会員証となります。
5. デジタル会員証を確認してアクセスを制限する仕組み
次に、利用者が秘密のページにアクセスしてきたときに、持っているデジタル会員証が本物かどうかを確認する仕組みを作ります。これを「ミドルウェア」と呼びます。ミドルウェアとは、目的の処理を実行する前に、途中で割り込んでチェックを行うプログラムのことです。
利用者は、通信のヘッダーと呼ばれる見えない情報欄に、先ほどのデジタル会員証を添付して送ってきます。サーバーは、受け取った会員証が、自分が発行した本物であるか、また有効期限が切れていないかを厳しくチェックします。もし偽物だったり、期限切れだったりした場合は、そこで処理を中断してエラーを返します。具体的なチェックプログラムは以下のようになります。
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
const SECRET_KEY = "my_super_secret_key";
export const verifyToken = (req: Request, res: Response, next: NextFunction) => {
// 通信のヘッダーから会員証を取り出す
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
// 会員証が存在しない場合
if (!token) {
return res.status(401).json({ message: "会員証がありません。アクセスできません。" });
}
try {
// 会員証が正しいか鍵を使って検証する
const decoded = jwt.verify(token, SECRET_KEY);
// 検証が成功したら次の処理に進む
next();
} catch (error) {
// 検証が失敗した場合(偽物や期限切れ)
return res.status(403).json({ message: "無効な会員証です。" });
}
};
このプログラムがあるおかげで、不正なアクセスを完全にブロックし、安全な会員限定エリアを守ることができるようになります。
6. 認証を通過した人だけが見られる秘密のページを作成する
これまでに作った「ログイン機能」と「会員証の確認機能」を組み合わせて、実際に鍵のかかったページを作成しましょう。エクスプレスでは、特定のURLの処理を書くときに、先ほど作成した確認用のプログラムを挟み込むことができます。
これにより、認証を通過した人だけがデータを取得できる仕組みが完成します。今回は、ログインに成功した人だけが閲覧できる、秘密のプロフィール情報を返すページを実装してみます。全体の動きをまとめたコードを確認してください。
import express from 'express';
import { verifyToken } from './authMiddleware'; // 先ほど作ったチェック機能
const app = express();
app.use(express.json());
// 秘密のページ(verifyTokenを入れることで鍵をかける)
app.get('/profile', verifyToken, (req, res) => {
res.json({
message: "認証に成功しました。これは秘密の情報です。",
secretData: "あなたは特別な会員です!"
});
});
app.listen(3000, () => {
console.log("会員限定サーバーが起動しました");
});
これで、会員証を持っていない人がこのページを開こうとしても、エラーが表示されて中身を見ることはできなくなりました。セキュリティの基本となる形がこれで完成です。
7. 完成したシステムの動作確認を行う手順
プログラムが完成したら、本当に正しく動くかテストをしてみましょう。パソコンの画面でサーバーを起動すると、接続を待ち受ける状態になります。通常、開発中のバックエンドの動作確認には、専用のツールやコマンドラインを使用します。
まず、ログインのURLに対して、間違ったパスワードを送ってみます。すると、サーバーはログイン拒否のメッセージを返します。次に、正しいユーザー名とパスワードを送ると、長い文字の羅列であるトークンが返ってきます。最後に、そのトークンを添付して秘密のページにアクセスすると、会員限定の情報が表示されます。以下は、正しく通信が成功したときに画面に表示される出力のイメージです。
{
"message": "認証に成功しました。これは秘密の情報です。",
"secretData": "あなたは特別な会員です!"
}
このように、意図した通りの文字が返ってくれば、システムは正常に機能しています。もしエラーが出た場合は、文字の打ち間違いや、秘密の鍵の名前が一致しているかをもう一度確認してみましょう。
8. 開発時の注意点とセキュリティを高めるための工夫
今回作成したシステムは基本的なものですが、実際のサービスとしてインターネット上に公開するときには、さらにいくつかの注意点があります。プログラムをより安全にするための大切な知識を学びましょう。
まず、プログラム内に直接書いた「秘密の鍵」の文字列は、絶対に他人に知られてはいけません。もしこの鍵が漏れてしまうと、誰でも偽物の会員証を自由に作ることができるようになってしまい、大変危険です。そのため、本番の開発では、プログラムとは別の独立した設定ファイルに鍵を保存し、それを読み込む方法が使われます。
また、デジタル会員証の有効期限は、あまり長くしすぎないのが鉄則です。万が一、会員証のデータが盗まれてしまった場合でも、有効期限が短ければ、被害を最小限に抑えることができます。初心者のうちは、まずは今回学んだプログラム全体の流れをしっかりと理解し、少しずつ高度な設定に挑戦していくのが上達の近道です。