認証は、バックエンド開発において最も重要であるにもかかわらず誤解されがちな側面の 1 つです。その複雑さのため、開発者は Auth0 や Supabase などのサードパーティ ソリューションを頻繁に利用します。これらは優れたツールですが、独自の認証システムを構築すると、柔軟性と制御が向上します。
このガイドでは、依存関係を最小限に抑えた Express.js API サービス用のシンプルな認証ミドルウェアを実装する方法を学習します。最後には次のものが得られます:
このガイドはシンプルさに重点を置き、複雑さを軽減するためにパスポート.js などのパッケージの使用を避けています。
まず、ユーザー アカウントを保存するための PostgreSQL テーブルを作成します。
CREATE TABLE users ( "id" SERIAL PRIMARY KEY, "username" VARCHAR(255) UNIQUE NOT NULL, "password" VARCHAR(255) NOT NULL, "email" VARCHAR(255) UNIQUE, "created_at" TIMESTAMP NOT NULL DEFAULT NOW() );
次に、API エンドポイントを保護するための JWT 認証ミドルウェアを作成します。この例では対称暗号化を使用します。マイクロサービス アーキテクチャの場合は、公開キーと秘密キーのペアを使用した非対称暗号化の使用を検討してください。
import jwt from "jsonwebtoken"; const JWT_SECRET_KEY = process.env.JWT_SECRET_KEY as string; // Randomly generated. Min length: 64 characters export const protectedRoute: RequestHandler = async (req, _, next) => { const authHeader = req.header("authorization"); if (!authHeader) { return next(notAuthenticated()); } const accessToken = authHeader.replace(new RegExp("\b[Bb]earer\s"), ""); try { const { userId } = validateJWT(accessToken); const user = await userRepository.getUserById(parseInt(userId)); if (user) { req.user = user; next(); } else { next(invalidAccessToken()); } } catch (err) { next(invalidAccessToken()); } }; const validateJWT = (token: string, verifyOptions?: jwt.VerifyOptions) => { const jwtVerifyOptions = Object.assign( { algorithms: "HS256" }, verifyOptions, { issuer: "yourAPI.com", audience: "yourAPI.com:client", } ); return jwt.verify(token, JWT_SECRET_KEY, jwtVerifyOptions) as T; };
ミドルウェアを使用してルートを保護します:
import { protectedRoute } from "@/middleware/jwt"; router.get("/user", protectedRoute, async (req, res, next) => { const user = req.user!; res.json({ user }); });
次に、サインアップとログイン用のコントローラーを実装します。
import argon from "argon2"; const signup = async (props) => { const { username, password, email } = props; await userRepo.getUser(username).then((res) => { if (res !== null) throw usernameNotAvailable(); }); const hashedPass = await argon.hash(password, { timeCost: 2, parallelism: 1, memoryCost: 19456, }); const newUser = await createUser({ username, hashedPass, email, }); const refreshToken = await generateRefreshToken(newUser.userId); const accessToken = generateAccessToken(newUser.userId); const { password: _, ...userRes } = newUser; return { user: userRes, accessToken, refreshToken }; };
const login = async (props) => { const { username, password } = props; const user = await getUser(username).then((res) => { if (res === null) throw invalidLoginCredentials(); return res; }); const isOk = await argon.verify(user.password, password); if (isOk) { const refreshToken = await generateRefreshToken(user.userId); const accessToken = generateAccessToken(user.userId); const { password: _, ...userRes } = user; return { user: userRes, accessToken, refreshToken }; } throw invalidLoginCredentials(); };
リフレッシュ トークンは長期的な認証を提供します。それらを保存するデータベース テーブルを作成しましょう:
CREATE TABLE refresh_tokens ( "id" SERIAL PRIMARY KEY, "token" UUID NOT NULL DEFAULT gen_random_uuid(), "token_family" UUID NOT NULL DEFAULT gen_random_uuid(), "user_id" INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, "active" BOOLEAN DEFAULT true, "expires_at" TIMESTAMP NOT NULL, "created_at" TIMESTAMP NOT NULL DEFAULT NOW() );
import jwt from "jsonwebtoken"; const JWT_SECRET_KEY = process.env.JWT_SECRET_KEY as string; // Randomly generated. Min length: 64 characters const generateAccessToken = (userId: number) => { const jwtSignOptions = Object.assign( { algorithm: "HS256" }, {}, { issuer: "yourAPI.com", audience: "yourAPI.com:client", } ); return jwt.sign({ userId: userId.toString() }, JWT_SECRET_KEY, jwtSignOptions); }; const generateRefreshToken = async (userId: number, tokenFamily?: string) => { const expAt = new Date(new Date().getTime() + 31 * 24 * 60 * 60 * 1000); // Expire in 31 days const refreshTokenExp = expAt.toISOString(); const token = await createTokenQuery({ userId, tokenFamily, expiresAt: refreshTokenExp, }); return token; };
トークンのリフレッシュを安全に処理するロジックを実装します:
const refreshToken = async ({ token }: RefreshTokenSchema) => { const tokenData = await getRefreshToken(token); if (!tokenData) throw forbiddenError(); const { userId, tokenFamily, active } = tokenData; if (active) { // Token is valid and hasn't been used yet const newRefreshToken = await generateRefreshToken(userId, tokenFamily); const accessToken = generateAccessToken(userId); return { accessToken, refreshToken: newRefreshToken }; } else { // Previously refreshed token used, invalidate all tokens in family await invalidateRefreshTokenFamily(tokenFamily); throw forbiddenError(); } };
リフレッシュ トークンと自動再利用検出について詳しくは、この Auth0 の記事をご覧ください。
このガイドに従うことで、依存関係を最小限に抑えた Node.js API 用のシンプルで安全な認証システムを構築できました。このアプローチにより、セキュリティを完全に制御し、最新のベスト プラクティスに準拠することが保証されます。
時間と労力を節約したい場合は、Vratix をチェックしてください。当社のオープンソース CLI は、認証を備えた完全に機能する Node.js プロジェクトを数秒でセットアップできます。 GitHub で完全に実装された認証モジュールをご覧ください。
このガイドは役に立ちましたか?コメント欄でお知らせいただくか、X でご連絡ください!
以上がNode.js で認証を行う正しい方法 [uide]の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。