Claude Codeで認証システムを構築する方法【JWT・OAuth対応】
Claude Codeを使ってJWT認証やOAuth連携を含む認証システムを効率的に構築する方法を解説。セキュリティのベストプラクティスも紹介します。
認証システム構築にClaude Codeを使うメリット
認証はセキュリティに直結する機能であり、実装ミスは重大な脆弱性につながります。Claude Codeはセキュリティのベストプラクティスを踏まえた実装を生成でき、見落としがちな攻撃ベクトルへの対策も提案してくれます。
JWT認証の実装
> Express + TypeScriptでJWT認証を実装して。
> アクセストークン(15分)とリフレッシュトークン(7日)の2トークン方式で。
> リフレッシュトークンはhttpOnlyクッキーで管理して。
認証サービス
import jwt from 'jsonwebtoken';
import bcrypt from 'bcrypt';
import { prisma } from './db';
const ACCESS_TOKEN_SECRET = process.env.ACCESS_TOKEN_SECRET!;
const REFRESH_TOKEN_SECRET = process.env.REFRESH_TOKEN_SECRET!;
interface TokenPayload {
userId: string;
email: string;
}
export class AuthService {
async register(email: string, password: string, name: string) {
const existing = await prisma.user.findUnique({ where: { email } });
if (existing) throw new Error('Email already registered');
const hashedPassword = await bcrypt.hash(password, 12);
const user = await prisma.user.create({
data: { email, password: hashedPassword, name },
});
return this.generateTokens({ userId: user.id, email: user.email });
}
async login(email: string, password: string) {
const user = await prisma.user.findUnique({ where: { email } });
if (!user) throw new Error('Invalid credentials');
const valid = await bcrypt.compare(password, user.password);
if (!valid) throw new Error('Invalid credentials');
return this.generateTokens({ userId: user.id, email: user.email });
}
async refreshToken(token: string) {
const payload = jwt.verify(token, REFRESH_TOKEN_SECRET) as TokenPayload;
// DBでリフレッシュトークンの有効性を確認
const stored = await prisma.refreshToken.findFirst({
where: { token, userId: payload.userId, revoked: false },
});
if (!stored) throw new Error('Invalid refresh token');
// 古いトークンを無効化(トークンローテーション)
await prisma.refreshToken.update({
where: { id: stored.id },
data: { revoked: true },
});
return this.generateTokens(payload);
}
private async generateTokens(payload: TokenPayload) {
const accessToken = jwt.sign(payload, ACCESS_TOKEN_SECRET, {
expiresIn: '15m',
});
const refreshToken = jwt.sign(payload, REFRESH_TOKEN_SECRET, {
expiresIn: '7d',
});
await prisma.refreshToken.create({
data: { token: refreshToken, userId: payload.userId },
});
return { accessToken, refreshToken };
}
}
認証ミドルウェア
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
export function authMiddleware(req: Request, res: Response, next: NextFunction) {
const header = req.headers.authorization;
if (!header?.startsWith('Bearer ')) {
return res.status(401).json({ error: 'No token provided' });
}
const token = header.slice(7);
try {
const payload = jwt.verify(token, process.env.ACCESS_TOKEN_SECRET!);
req.user = payload as TokenPayload;
next();
} catch (err) {
return res.status(401).json({ error: 'Invalid or expired token' });
}
}
ルーター設定
import { Router } from 'express';
import { AuthService } from './auth-service';
const router = Router();
const auth = new AuthService();
router.post('/register', async (req, res) => {
try {
const { accessToken, refreshToken } = await auth.register(
req.body.email, req.body.password, req.body.name
);
res.cookie('refreshToken', refreshToken, {
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 7 * 24 * 60 * 60 * 1000,
});
res.json({ accessToken });
} catch (err) {
res.status(400).json({ error: (err as Error).message });
}
});
router.post('/login', async (req, res) => {
try {
const { accessToken, refreshToken } = await auth.login(
req.body.email, req.body.password
);
res.cookie('refreshToken', refreshToken, {
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 7 * 24 * 60 * 60 * 1000,
});
res.json({ accessToken });
} catch (err) {
res.status(401).json({ error: 'Invalid credentials' });
}
});
router.post('/refresh', async (req, res) => {
try {
const token = req.cookies.refreshToken;
const { accessToken, refreshToken } = await auth.refreshToken(token);
res.cookie('refreshToken', refreshToken, {
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 7 * 24 * 60 * 60 * 1000,
});
res.json({ accessToken });
} catch (err) {
res.status(401).json({ error: 'Invalid refresh token' });
}
});
export default router;
OAuth連携(Google)
> PassportJSでGoogleログインを追加して。既存のJWT認証と統合して。
import passport from 'passport';
import { Strategy as GoogleStrategy } from 'passport-google-oauth20';
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
callbackURL: '/auth/google/callback',
}, async (accessToken, refreshToken, profile, done) => {
let user = await prisma.user.findFirst({
where: { providerId: profile.id, provider: 'google' },
});
if (!user) {
user = await prisma.user.create({
data: {
email: profile.emails![0].value,
name: profile.displayName,
provider: 'google',
providerId: profile.id,
},
});
}
done(null, user);
}));
セキュリティチェックリスト
Claude Codeに以下のプロンプトでセキュリティ監査を依頼できます。
> 認証まわりのコードをセキュリティ観点でレビューして。
> OWASP Top 10に基づいてチェックして。
主な確認項目は以下の通りです。
- パスワードハッシュにbcryptを使用しているか
- JWTのシークレットキーが十分に長いか
- リフレッシュトークンのローテーションが実装されているか
- CSRF対策が施されているか
- レートリミットが設定されているか
セキュリティを含むコード品質の維持にはリファクタリングの自動化も効果的です。また、認証の設定方針をCLAUDE.mdに記述しておくと、Claude Codeが一貫したコードを生成します。
まとめ
Claude Codeを使えば、JWT認証やOAuth連携を含む堅牢な認証システムを効率的に構築できます。セキュリティのベストプラクティスを踏まえたコードが生成されるため、見落としがちな脆弱性にも対処しやすくなります。必ず本番環境ではシークレットキーの安全な管理とHTTPS通信を徹底してください。
詳しくはAnthropic公式ドキュメントをご覧ください。
#Claude Code
#認証
#JWT
#OAuth
#セキュリティ
関連記事
Use Cases
Use Cases
Claude CodeでCORS設定完全ガイド:クロスオリジン通信の実践解説
Claude Codeを活用したCORS設定の完全ガイド。プリフライトリクエスト、認証付きリクエスト、トラブルシューティングまで実践的に解説します。
Use Cases
Use Cases
Claude Codeで通貨フォーマットを正しく実装する
Claude Codeを使って、多通貨対応・ロケール別フォーマット・為替レート変換など、通貨の表示と処理を正しく実装する方法を解説します。
Use Cases
Use Cases
Claude CodeでDiscord Botを開発する実践ガイド
Claude Codeを活用したDiscord Botの開発方法を解説。discord.js、スラッシュコマンド、ボタン操作、Embed、音声チャンネル連携まで実践的に紹介します。