この記事では、Express.js アプリケーションに AWS Cognito を使用し、アクセストークンを検証する認証ミドルウェアの実装方法を説明する。AWS Cognito はセキュリティが強固で広く使用されている認証サービスであり、Webアプリケーションやモバイルアプリケーションでのユーザー認証に適している。ここでは、JWT (JSON Web Tokens) の署名を検証し、有効なトークンのみがリソースへのアクセスを許可されるようにする手順を解説する。
前回の記事: 【Express.js】AWS SDKを使用したCognito認証処理
必要なライブラリと開発環境
まず、以下のライブラリをインストールする。
npm install jsonwebtoken jwks-rsa
jsonwebtoken
ライブラリは、JWTの生成、署名、および検証を行うためのツールキット。jwks-rsa
ライブラリは、公開鍵のセットを提供するJWKS (JSON Web Key Set) エンドポイントから特定の公開鍵を取得し、JWTの署名を検証するために使用される。
実装
設定ファイル: src/config/aws/cognito.ts
まず、AWS Cognito の設定を管理するためのコンフィグクラスを作成する。これには、Cognito ユーザープールのリージョン、ユーザープールID、アプリクライアントIDが含まれる。
export class AWSCognitoConfig {
private static instance: AWSCognitoConfig;
private readonly region: string;
private readonly userPoolId: string;
private readonly appClientId: string;
private constructor() {
// リージョン
this.region = "ap-northeast-1";
// ユーザープールID
this.userPoolId = "xxxxxxxxxxxxxx";
// アプリクライアントID
this.appClientId = "xxxxxxxxxxxxxx";
}
public static getInstance(): AWSCognitoConfig {
if (!AWSCognitoConfig.instance) {
AWSCognitoConfig.instance = new AWSCognitoConfig();
}
return AWSCognitoConfig.instance;
}
public get getRegion(): string {
return this.region;
}
public get getUserPoolId(): string {
return this.userPoolId;
}
public get getAppClientId(): string {
return this.appClientId;
}
}
トークン検証ミドルウェアの実装: src/middleware/authMiddleware.ts
次に、JWTの署名を検証するためのミドルウェアを実装する。このミドルウェアは、リクエストの Authorization
ヘッダーからトークンを取得し、そのトークンの署名が AWS Cognito の公開鍵によって検証されるかどうかを確認する。
import { Request, Response, NextFunction } from "express";
import jwt from "jsonwebtoken";
import jwksClient from "jwks-rsa";
import { AWSCognitoConfig } from "../config/aws/cognito";
// Cognito設定情報
const cognitoConfig = AWSCognitoConfig.getInstance();
/**
* 指定された JWKS URL から公開鍵を取得するためのクライアントを作成
* このクライアントは、JWTが持っている kid(Key ID)ヘッダーを使って、
* トークンの署名に使用された正確な公開鍵を見つけ出す
*/
const client = jwksClient({
// AWS Cognito ユーザープールの JWKS (公開鍵セット) の場所
jwksUri: `https://cognito-idp.${cognitoConfig.getRegion}.amazonaws.com/${cognitoConfig.getUserPoolId}/.well-known/jwks.json`,
});
/**
* JWTのkid(キーID)を使用して適切な公開鍵を取得
*/
function getKey(header: jwt.JwtHeader, callback: jwt.SigningKeyCallback) {
// 指定された kid を持つキーを JWKS から検索
client.getSigningKey(header.kid!, (err, key) => {
const signingKey = key?.getPublicKey();
callback(null, signingKey);
});
}
/**
* このミドルウェアはJWTのkid(キーID)を使用して適切な公開鍵を取得し、トークンの署名を検証する。
* トークンが有効であれば、リクエストオブジェクトにユーザー情報を追加し、処理を次のミドルウェアに渡す。
* トークンが無効であれば、401 Unauthorizedエラーを返します。
*/
export const checkAuth = (req: Request, res: Response, next: NextFunction) => {
// リクエストヘッダーからaccessTokenを取得
const token = req.headers.authorization?.split(" ")[1];
if (!token) {
// トークンがセットされていない場合はエラーを返す
return res.status(401).send({ message: "Authentication token is missing" });
}
// トークンの署名を検証
jwt.verify(
token,
getKey,
{
algorithms: ["RS256"],
},
(err, decoded) => {
if (err) {
// トークンが無効な場合は401 Unauthorizedエラーにする
return res.status(401).send({ message: "Unauthorized: Invalid token" });
}
// トークンが有効な場合、デコードされたユーザー情報をリクエストに追加
req.user = decoded;
// 次のミドルウェアへ処理を渡す
next();
}
);
};
ルーティング設定: src/routes/jobTypes.ts
認証ミドルウェアを使用して、各エンドポイントにアクセス制限を設ける。今回は事例として、職種情報を登録する処理でトークンの検証を実施することにする。
import express from "express";
import {
createJobType,
} from "../controllers/JobTypeController";
import { checkAuth } from "../middleware/authMiddleware";
const router = express.Router();
// リソースへのアクセス前に、認証ミドルウェアを実行する
router.post("/job-type/create", checkAuth, createJobType);
export default router;
これで、AWS Cognito を使用してアクセストークンの検証を行う認証ミドルウェアが正しく設定され、Express.js アプリケーションで安全にデータ管理ができるようになった。この実装により、不正なアクセスを防ぎつつ、適切なユーザーのみがリソースにアクセスできるようになる。
コメント