API認証方式の概要
APIの認証方式には目的と用途に応じていくつかの選択肢があります。適切な認証方式を選択することはAPIのセキュリティと使いやすさの両立に不可欠です。
APIキー認証の実装
// APIキーのハッシュ化保存
import { randomBytes, createHash } from 'crypto';
const generateApiKey = () => {
const rawKey = randomBytes(32).toString('hex');
const keyHash = createHash('sha256').update(rawKey).digest('hex');
return { rawKey, keyHash }; // rawKeyのみユーザーに1回表示
};
// APIキーの検証ミドルウェア
const apiKeyAuth = async (req, res, next) => {
const apiKey = req.headers['x-api-key'];
if (!apiKey) return res.status(401).json({ error: 'APIキーが必要です' });
const keyHash = createHash('sha256').update(apiKey).digest('hex');
const keyRecord = await db.apiKeys.findUnique({ where: { keyHash } });
if (!keyRecord || keyRecord.revokedAt) {
return res.status(401).json({ error: '無効なAPIキー' });
}
// レート制限チェック(Redisを使用)
const count = await redis.incr(`ratelimit:${keyRecord.id}:${getMinute()}`);
await redis.expire(`ratelimit:${keyRecord.id}:${getMinute()}`, 60);
if (count > keyRecord.rateLimit) {
return res.status(429).json({ error: 'レート制限超過' });
}
req.apiKey = keyRecord;
next();
};
JWT認証の実装
import jwt from 'jsonwebtoken';
// JWTの生成(ログイン時)
const generateTokens = (userId, email) => {
const accessToken = jwt.sign(
{ sub: userId, email, type: 'access' },
process.env.JWT_SECRET,
{ expiresIn: '15m' }
);
const refreshToken = jwt.sign(
{ sub: userId, type: 'refresh' },
process.env.JWT_REFRESH_SECRET,
{ expiresIn: '30d' }
);
return { accessToken, refreshToken };
};
// JWT検証ミドルウェア
const jwtAuth = (req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Authorization headerが必要です' });
}
const token = authHeader.slice(7);
try {
const payload = jwt.verify(token, process.env.JWT_SECRET);
req.user = payload;
next();
} catch (err) {
if (err.name === 'TokenExpiredError') {
return res.status(401).json({ error: 'トークンが期限切れです', code: 'TOKEN_EXPIRED' });
}
return res.status(401).json({ error: '無効なトークン' });
}
};
OAuth 2.0 Client Credentials(サーバー間認証)
// Client Credentialsフロー(サーバー間API認証)
const getServiceToken = async () => {
const response = await fetch('https://auth.example.com/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'client_credentials',
client_id: process.env.CLIENT_ID,
client_secret: process.env.CLIENT_SECRET,
scope: 'api:read api:write'
})
});
const { access_token, expires_in } = await response.json();
// トークンをキャッシュしてexpires_in秒間再利用
await redis.setEx('service_token', expires_in - 60, access_token);
return access_token;
};
認証方式の選択ガイド
- APIキー:シンプルなサーバー間・バックエンドAPIアクセス
- JWT:ユーザー認証・SPA・モバイルアプリ向けAPI
- OAuth 2.0 Authorization Code:サードパーティのユーザーリソースへのアクセス
- OAuth 2.0 Client Credentials:マイクロサービス間・バッチ処理の認証
- mTLS:金融・ペイメント系の高セキュリティなサーバー間通信
まとめ
API認証はセキュリティの基盤です。APIキーはシンプルで使いやすく、JWTはステートレスでスケーラブルです。OAuth 2.0はサードパーティ連携に必須です。用途に応じて適切な認証方式を選び、APIキーはDBにハッシュ化して保存・JWTは短い有効期限設定・リフレッシュトークンでUX向上というベストプラクティスを実践してください。