import * as jwt from "jsonwebtoken"; // XXX: decodeがうまく使えないことがあるので応急対応 バージョン9以降だとなる? import { decode as jwtDecode } from "jsonwebtoken"; export type VerifyError = { reason: "ExpiredError" | "InvalidToken" | "InvalidTimeStamp" | "Unknown"; message: string; }; export const isVerifyError = (arg: unknown): arg is VerifyError => { const value = arg as VerifyError; if (value.message === undefined) { return false; } if (value.reason === undefined) { return false; } switch (value.reason) { case "ExpiredError": case "InvalidTimeStamp": case "InvalidToken": case "Unknown": return true; default: return false; } }; /** * Payloadと秘密鍵を使用して署名されたJWTを生成します * @param {T} payload payloadの型 * @param {number} expirationSeconds トークンの有効期限(秒) * @param {string} privateKey 署名に使用する秘密鍵 * @return {string} 署名済みトークン * @throws {Error} 秘密鍵の形式が間違っている等の理由が格納されたErrorオブジェクト */ export const sign = ( payload: T, expirationSeconds: number, privateKey: string ): string => { try { const token = jwt.sign(payload, privateKey, { expiresIn: expirationSeconds, algorithm: "RS256", }); return token; } catch (e) { throw e; } }; /** * tokenと公開鍵を使用して検証済みJWTのpayloadを取得します * @param {string} token JWT * @param {string} publicKey 検証に使用する公開鍵 * @return {T | VerifyError} Payload または 検証エラーの内容を表すオブジェクト */ export const verify = ( token: string, publicKey: string ): T | VerifyError => { try { const payload = jwt.verify(token, publicKey, { algorithms: ["RS256"], }) as T; return payload; } catch (e) { if (e instanceof jwt.TokenExpiredError) { return { reason: "ExpiredError", message: e.message, }; } else if (e instanceof jwt.NotBeforeError) { return { reason: "InvalidTimeStamp", message: e.message, }; } else if (e instanceof jwt.JsonWebTokenError) { return { reason: "InvalidToken", message: e.message, }; } else { return { reason: "Unknown", message: e.message, }; } } }; /** * tokenから未検証のJWTのpayloadを取得します * @param {string} token JWT * @return {T | VerifyError} Payload または デコードエラーの内容を表すオブジェクト */ export const decode = (token: string): T | VerifyError => { try { const payload = jwtDecode(token, { json: true, }) as T; return payload; } catch (e) { if (e instanceof jwt.TokenExpiredError) { return { reason: "ExpiredError", message: e.message, }; } else if (e instanceof jwt.NotBeforeError) { return { reason: "InvalidTimeStamp", message: e.message, }; } else if (e instanceof jwt.JsonWebTokenError) { return { reason: "InvalidToken", message: e.message, }; } else { return { reason: "Unknown", message: e.message, }; } } }; export const getJwtKey = (key: string): string => key.replace(/\\n/g, "\n");