@nanokajs/auth 1.6.0 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,12 @@ All notable changes to `@nanokajs/auth` will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.6.1] — 2026-05-23
|
|
9
|
+
|
|
10
|
+
### Security
|
|
11
|
+
|
|
12
|
+
- `refreshHandler` で `verify()` に渡す前の refresh token 文字列長に 4096 文字の上限を導入。cookie / body 双方の経路に適用。閾値超過時は 401 を返す。巨大入力で base64 デコードに CPU を消費させる攻撃面を defense-in-depth として遮断(Issue #107)。
|
|
13
|
+
|
|
8
14
|
## [1.6.0] — 2026-05-17
|
|
9
15
|
|
|
10
16
|
### Added
|
package/dist/index.js
CHANGED
|
@@ -208,6 +208,7 @@ async function verify(token, secret) {
|
|
|
208
208
|
}
|
|
209
209
|
|
|
210
210
|
// src/create-auth.ts
|
|
211
|
+
var MAX_REFRESH_TOKEN_LENGTH = 4096;
|
|
211
212
|
function createAuth(opts) {
|
|
212
213
|
if (opts.secret.length < 32) {
|
|
213
214
|
throw new Error("createAuth: secret must be at least 32 characters");
|
|
@@ -302,6 +303,9 @@ function createAuth(opts) {
|
|
|
302
303
|
}
|
|
303
304
|
refreshToken = bodyRefreshToken;
|
|
304
305
|
}
|
|
306
|
+
if (refreshToken.length > MAX_REFRESH_TOKEN_LENGTH) {
|
|
307
|
+
throw new HTTPException(401, { message: "Invalid credentials" });
|
|
308
|
+
}
|
|
305
309
|
let payload;
|
|
306
310
|
try {
|
|
307
311
|
payload = await verify(refreshToken, opts.secret);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/blacklist-stores/kv.ts","../src/hashers/pbkdf2.ts","../src/jwt.ts","../src/create-auth.ts","../src/middleware.ts"],"names":["utf8","toBase64url","fromBase64url","accessToken","HTTPException"],"mappings":"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","file":"index.js","sourcesContent":["import type { KVNamespace } from '@cloudflare/workers-types'\nimport type { BlacklistStore } from '../blacklist-store.js'\n\nasync function hashKey(jti: string): Promise<string> {\n const encoder = new TextEncoder()\n const buf = await crypto.subtle.digest('SHA-256', encoder.encode(jti))\n const bytes = new Uint8Array(buf)\n let hex = ''\n for (let i = 0; i < bytes.length; i++) {\n hex += (bytes[i] as number).toString(16).padStart(2, '0')\n }\n return hex\n}\n\nexport function kvBlacklistStore(kv: KVNamespace, opts?: { prefix?: string }): BlacklistStore {\n const prefix = opts?.prefix ?? 'nanoka-auth:bl:'\n\n return {\n async add(jti: string, expiresAt: number): Promise<void> {\n const ttl = Math.min(2592000, Math.max(60, expiresAt - Math.floor(Date.now() / 1000)))\n await kv.put(`${prefix}${await hashKey(jti)}`, '1', { expirationTtl: ttl })\n },\n\n async has(jti: string): Promise<boolean> {\n return (await kv.get(`${prefix}${await hashKey(jti)}`)) !== null\n },\n\n async addWithSubject(jti: string, sub: string, expiresAt: number): Promise<void> {\n const ttl = Math.min(2592000, Math.max(60, expiresAt - Math.floor(Date.now() / 1000)))\n await kv.put(`${prefix}${await hashKey(jti)}`, JSON.stringify({ sub }), {\n expirationTtl: ttl,\n })\n },\n\n async hasForSubject(jti: string, sub: string): Promise<boolean> {\n const value = await kv.get(`${prefix}${await hashKey(jti)}`)\n if (value === null) return false\n if (value === '1') return true\n try {\n const parsed = JSON.parse(value) as unknown\n if (typeof parsed === 'object' && parsed !== null && 'sub' in parsed) {\n return (parsed as { sub: unknown }).sub === sub\n }\n } catch {\n // malformed value: treat as blacklisted (conservative)\n }\n return true\n },\n }\n}\n","import type { Hasher } from '../hasher.js'\n\nconst ITERATIONS = 310_000\nconst SALT_BYTES = 16\nconst HASH_BYTES = 32\nconst HASH_NAME = 'SHA-256'\n\nfunction utf8(str: string): Uint8Array<ArrayBuffer> {\n return new TextEncoder().encode(str) as unknown as Uint8Array<ArrayBuffer>\n}\n\nfunction toBase64url(buf: ArrayBuffer): string {\n const bytes = new Uint8Array(buf)\n let binary = ''\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i] as number)\n }\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\nfunction fromBase64url(str: string): Uint8Array<ArrayBuffer> | null {\n try {\n const padded = str\n .replace(/-/g, '+')\n .replace(/_/g, '/')\n .padEnd(str.length + ((4 - (str.length % 4)) % 4), '=')\n const binary = atob(padded)\n const buf = new ArrayBuffer(binary.length)\n const bytes = new Uint8Array(buf)\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i)\n }\n return bytes\n } catch {\n return null\n }\n}\n\nasync function derive(\n plain: string,\n salt: Uint8Array<ArrayBuffer>,\n iterations: number,\n): Promise<ArrayBuffer> {\n const key = await crypto.subtle.importKey('raw', utf8(plain), 'PBKDF2', false, ['deriveBits'])\n return crypto.subtle.deriveBits(\n { name: 'PBKDF2', salt, iterations, hash: HASH_NAME },\n key,\n HASH_BYTES * 8,\n )\n}\n\nfunction timingSafeEqual(a: Uint8Array<ArrayBuffer>, b: Uint8Array<ArrayBuffer>): boolean {\n if (a.length !== b.length) return false\n let diff = 0\n for (let i = 0; i < a.length; i++) {\n diff |= (a[i] as number) ^ (b[i] as number)\n }\n return diff === 0\n}\n\nexport const pbkdf2Hasher: Hasher = {\n async hash(plain: string): Promise<string> {\n const saltBuf = new ArrayBuffer(SALT_BYTES)\n const salt = new Uint8Array(saltBuf) as Uint8Array<ArrayBuffer>\n crypto.getRandomValues(salt)\n const derived = await derive(plain, salt, ITERATIONS)\n return `$pbkdf2$${ITERATIONS}$${toBase64url(saltBuf)}$${toBase64url(derived)}`\n },\n\n async verify(plain: string, stored: string): Promise<boolean> {\n const parts = stored.split('$')\n if (parts.length !== 5 || parts[0] !== '') return false\n const algoId = parts[1]\n const iterStr = parts[2]\n const saltB64 = parts[3]\n const hashB64 = parts[4]\n if (algoId !== 'pbkdf2') return false\n if (iterStr === undefined || saltB64 === undefined || hashB64 === undefined) return false\n\n const iterations = Number.parseInt(iterStr, 10)\n if (!Number.isFinite(iterations) || iterations <= 0 || iterations > 1_000_000) return false\n\n const salt = fromBase64url(saltB64)\n const storedHash = fromBase64url(hashB64)\n\n if (salt === null || storedHash === null) return false\n if (salt.length === 0 || storedHash.length !== HASH_BYTES) return false\n\n const derived = await derive(plain, salt, iterations)\n const derivedBytes = new Uint8Array(derived) as Uint8Array<ArrayBuffer>\n\n return timingSafeEqual(derivedBytes, storedHash)\n },\n}\n","const ALGORITHM = 'HS256'\n\nexport interface SignOptions {\n expiresIn?: number\n}\n\nfunction utf8(str: string): Uint8Array<ArrayBuffer> {\n return new TextEncoder().encode(str) as unknown as Uint8Array<ArrayBuffer>\n}\n\nfunction toBase64url(buf: ArrayBuffer): string {\n const bytes = new Uint8Array(buf)\n let binary = ''\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i] as number)\n }\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\nfunction fromBase64url(str: string): ArrayBuffer {\n const padded = str\n .replace(/-/g, '+')\n .replace(/_/g, '/')\n .padEnd(str.length + ((4 - (str.length % 4)) % 4), '=')\n const binary = atob(padded)\n const buf = new ArrayBuffer(binary.length)\n const bytes = new Uint8Array(buf)\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i)\n }\n return buf\n}\n\nasync function importKey(secret: string): Promise<CryptoKey> {\n return crypto.subtle.importKey('raw', utf8(secret), { name: 'HMAC', hash: 'SHA-256' }, false, [\n 'sign',\n 'verify',\n ])\n}\n\n/**\n * HS256 で JWT を署名する。\n * @remarks アルゴリズムは HS256 固定。RS256 / ES256 等の非対称アルゴリズムを追加する場合は\n * 鍵オブジェクト型を分離し、verify に期待する alg を引数で渡す API に変更すること(alg confusion 攻撃防止)。\n */\nexport async function sign(\n payload: Record<string, unknown>,\n secret: string,\n options?: SignOptions,\n): Promise<string> {\n if (!secret) throw new Error('secret must not be empty')\n const header = { alg: ALGORITHM, typ: 'JWT' }\n const headerB64 = toBase64url(utf8(JSON.stringify(header)).buffer as ArrayBuffer)\n\n if (options?.expiresIn !== undefined && !Number.isFinite(options.expiresIn)) {\n throw new Error('expiresIn must be a finite number')\n }\n const finalPayload =\n options?.expiresIn !== undefined\n ? { ...payload, exp: Math.floor(Date.now() / 1000) + options.expiresIn }\n : payload\n const payloadB64 = toBase64url(utf8(JSON.stringify(finalPayload)).buffer as ArrayBuffer)\n\n const signingInput = `${headerB64}.${payloadB64}`\n const key = await importKey(secret)\n const signatureBuf = await crypto.subtle.sign('HMAC', key, utf8(signingInput))\n const signatureB64 = toBase64url(signatureBuf)\n\n return `${signingInput}.${signatureB64}`\n}\n\n/**\n * HS256 JWT を検証してペイロードを返す。\n * @remarks 戻り値の型パラメータ T は実行時検証なし。受け取った payload は Zod 等で検証すること。\n * `nbf` / `iat` / `aud` / `iss` クレームは検証しない。必要な場合はアプリ側で payload を検証すること。\n */\nexport async function verify<T = Record<string, unknown>>(\n token: string,\n secret: string,\n): Promise<T> {\n if (!secret) throw new Error('secret must not be empty')\n const segments = token.split('.')\n if (segments.length !== 3) {\n throw new Error('Invalid token format')\n }\n\n const [headerB64, payloadB64, signatureB64] = segments as [string, string, string]\n\n const header = JSON.parse(new TextDecoder().decode(fromBase64url(headerB64))) as unknown\n if (typeof header !== 'object' || header === null || Array.isArray(header)) {\n throw new Error('Invalid token header')\n }\n if ((header as Record<string, unknown>).typ !== 'JWT') {\n throw new Error('Invalid token header')\n }\n const alg = (header as Record<string, unknown>).alg\n if (typeof alg !== 'string' || alg.toLowerCase() === 'none') {\n throw new Error('Invalid token header')\n }\n if (alg !== ALGORITHM) {\n throw new Error('Invalid token header')\n }\n\n const signingInput = `${headerB64}.${payloadB64}`\n const key = await importKey(secret)\n const signatureBuf = fromBase64url(signatureB64)\n const valid = await crypto.subtle.verify('HMAC', key, signatureBuf, utf8(signingInput))\n if (!valid) {\n throw new Error('Invalid signature')\n }\n\n const payload = JSON.parse(new TextDecoder().decode(fromBase64url(payloadB64))) as unknown\n if (typeof payload !== 'object' || payload === null || Array.isArray(payload)) {\n throw new Error('Invalid payload')\n }\n const payloadObj = payload as Record<string, unknown>\n\n if (payloadObj.exp !== undefined) {\n if (typeof payloadObj.exp !== 'number' || !Number.isFinite(payloadObj.exp)) {\n throw new Error('Invalid exp claim')\n }\n if (Math.floor(Date.now() / 1000) >= payloadObj.exp) {\n throw new Error('Token expired')\n }\n }\n\n return payloadObj as T\n}\n","import type { NanokaModel } from '@nanokajs/core'\nimport type { Handler, MiddlewareHandler } from 'hono'\nimport { getCookie, setCookie } from 'hono/cookie'\nimport { HTTPException } from 'hono/http-exception'\nimport type { BlacklistStore } from './blacklist-store.js'\nimport type { Hasher } from './hasher.js'\nimport { pbkdf2Hasher } from './hashers/pbkdf2.js'\nimport { sign, verify } from './jwt.js'\n\nexport interface CookieOptions {\n httpOnly?: boolean\n sameSite?: 'Strict' | 'Lax' | 'None'\n secure?: boolean\n path?: string\n accessTokenName?: string\n refreshTokenName?: string\n}\n\nexport interface CreateAuthOptions {\n // biome-ignore lint/suspicious/noExplicitAny: any is necessary for NanokaModel generic constraint\n model: NanokaModel<any>\n secret: string\n fields: { identifier: string; password: string }\n hasher?: Hasher\n jwt?: { expiresIn?: number; refreshExpiresIn?: number; rotation?: boolean }\n cookie?: CookieOptions\n blacklist?: BlacklistStore\n}\n\nexport interface AuthInstance {\n loginHandler(): Handler\n refreshHandler(): Handler\n middleware(): MiddlewareHandler\n}\n\nexport function createAuth(opts: CreateAuthOptions): AuthInstance {\n if (opts.secret.length < 32) {\n throw new Error('createAuth: secret must be at least 32 characters')\n }\n\n const rotation = opts.jwt?.rotation ?? false\n if (rotation && opts.blacklist === undefined) {\n throw new Error('createAuth: blacklist is required when jwt.rotation is true')\n }\n\n const hasher = opts.hasher ?? pbkdf2Hasher\n const expiresIn = opts.jwt?.expiresIn ?? 900\n const refreshExpiresIn = opts.jwt?.refreshExpiresIn ?? 604_800\n const identifierField = opts.fields.identifier\n const passwordField = opts.fields.password\n const dummyHashPromise = hasher.hash('__dummy__')\n\n const cookieOpts = opts.cookie\n const accessTokenName = cookieOpts?.accessTokenName ?? 'access_token'\n const refreshTokenName = cookieOpts?.refreshTokenName ?? 'refresh_token'\n\n return {\n loginHandler(): Handler {\n return async (c) => {\n let body: Record<string, unknown>\n try {\n body = await c.req.json<Record<string, unknown>>()\n } catch {\n throw new HTTPException(401, { message: 'Invalid credentials' })\n }\n const identifierValue = body[identifierField]\n const passwordValue = body[passwordField]\n\n if (typeof identifierValue !== 'string' || typeof passwordValue !== 'string') {\n throw new HTTPException(401, { message: 'Invalid credentials' })\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: model is typed as NanokaModel<any> per CreateAuthOptions\n const user = await opts.model.findOne({ [identifierField]: identifierValue } as any)\n\n if (user === null) {\n const dummyHash = await dummyHashPromise\n await hasher.verify('__dummy__', dummyHash)\n throw new HTTPException(401, { message: 'Invalid credentials' })\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: user shape is unknown at compile time\n const storedHash = (user as any)[passwordField] as string\n const valid = await hasher.verify(passwordValue, storedHash)\n\n if (!valid) {\n throw new HTTPException(401, { message: 'Invalid credentials' })\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: user shape is unknown at compile time\n const sub: string = String((user as any).id ?? (user as any)[identifierField])\n\n const refreshPayload: Record<string, unknown> = { sub, type: 'refresh' }\n if (rotation) {\n refreshPayload.jti = crypto.randomUUID()\n }\n\n const [accessToken, refreshToken] = await Promise.all([\n sign({ sub, type: 'access' }, opts.secret, { expiresIn }),\n sign(refreshPayload, opts.secret, { expiresIn: refreshExpiresIn }),\n ])\n\n if (cookieOpts !== undefined) {\n const sameSite = cookieOpts.sameSite ?? 'Lax'\n const baseCookieOptions = {\n httpOnly: cookieOpts.httpOnly ?? true,\n sameSite,\n secure: sameSite === 'None' ? true : (cookieOpts.secure ?? true),\n path: cookieOpts.path ?? '/',\n } as const\n setCookie(c, accessTokenName, accessToken, {\n ...baseCookieOptions,\n maxAge: expiresIn,\n })\n setCookie(c, refreshTokenName, refreshToken, {\n ...baseCookieOptions,\n maxAge: refreshExpiresIn,\n })\n return c.json({ ok: true }, 200)\n }\n\n return c.json({ accessToken, refreshToken }, 200)\n }\n },\n\n refreshHandler(): Handler {\n return async (c) => {\n let refreshToken: string | undefined\n\n if (cookieOpts !== undefined) {\n refreshToken = getCookie(c, refreshTokenName)\n if ((refreshToken === undefined || refreshToken === '') && rotation) {\n throw new HTTPException(401, { message: 'Invalid credentials' })\n }\n }\n\n if (refreshToken === undefined || refreshToken === '') {\n let body: Record<string, unknown>\n try {\n body = await c.req.json<Record<string, unknown>>()\n } catch {\n throw new HTTPException(401, { message: 'Invalid credentials' })\n }\n const bodyRefreshToken = body.refreshToken\n if (typeof bodyRefreshToken !== 'string') {\n throw new HTTPException(401, { message: 'Invalid credentials' })\n }\n refreshToken = bodyRefreshToken\n }\n\n let payload: Record<string, unknown>\n try {\n payload = await verify<Record<string, unknown>>(refreshToken, opts.secret)\n } catch (err) {\n console.warn('refresh token verify failed', err)\n throw new HTTPException(401, { message: 'Invalid credentials' })\n }\n\n if (payload.type !== 'refresh') {\n throw new HTTPException(401, { message: 'Invalid credentials' })\n }\n\n if (typeof payload.sub !== 'string' || payload.sub.length === 0) {\n throw new HTTPException(401, { message: 'Invalid credentials' })\n }\n\n if (rotation) {\n const jti = payload.jti\n if (typeof jti !== 'string' || jti.length === 0) {\n throw new HTTPException(401, { message: 'Invalid credentials' })\n }\n if (jti.length > 256) {\n throw new HTTPException(401, { message: 'Invalid credentials' })\n }\n if (!/^[A-Za-z0-9_-]+$/.test(jti)) {\n throw new HTTPException(401, { message: 'Invalid credentials' })\n }\n // biome-ignore lint/style/noNonNullAssertion: blacklist is guaranteed non-null when rotation is true (validated at createAuth call)\n const blacklist = opts.blacklist!\n // Use subject-aware methods only when BOTH are present.\n // Using them independently (e.g. hasForSubject without addWithSubject) would\n // cause the read and write paths to use different storage keys, allowing\n // a revoked JTI to be accepted on the read path (replay attack).\n const useSubjectAware =\n blacklist.hasForSubject !== undefined && blacklist.addWithSubject !== undefined\n const blacklisted = useSubjectAware\n ? await blacklist.hasForSubject!(jti, payload.sub)\n : await blacklist.has(jti)\n if (blacklisted) {\n throw new HTTPException(401, { message: 'Invalid credentials' })\n }\n const exp =\n typeof payload.exp === 'number'\n ? payload.exp\n : Math.floor(Date.now() / 1000) + refreshExpiresIn\n try {\n if (useSubjectAware) {\n await blacklist.addWithSubject!(jti, payload.sub, exp)\n } else {\n await blacklist.add(jti, exp)\n }\n } catch (err) {\n console.error('blacklist.add failed', err)\n throw new HTTPException(401, { message: 'Invalid credentials' })\n }\n\n const newJti = crypto.randomUUID()\n const [accessToken, newRefreshToken] = await Promise.all([\n sign({ sub: payload.sub, type: 'access' }, opts.secret, { expiresIn }),\n sign({ sub: payload.sub, type: 'refresh', jti: newJti }, opts.secret, {\n expiresIn: refreshExpiresIn,\n }),\n ])\n\n if (cookieOpts !== undefined) {\n const sameSite = cookieOpts.sameSite ?? 'Lax'\n const baseCookieOptions = {\n httpOnly: cookieOpts.httpOnly ?? true,\n sameSite,\n secure: sameSite === 'None' ? true : (cookieOpts.secure ?? true),\n path: cookieOpts.path ?? '/',\n } as const\n setCookie(c, accessTokenName, accessToken, {\n ...baseCookieOptions,\n maxAge: expiresIn,\n })\n setCookie(c, refreshTokenName, newRefreshToken, {\n ...baseCookieOptions,\n maxAge: refreshExpiresIn,\n })\n return c.json({ ok: true }, 200)\n }\n\n return c.json({ accessToken, refreshToken: newRefreshToken }, 200)\n }\n\n const accessToken = await sign({ sub: payload.sub, type: 'access' }, opts.secret, {\n expiresIn,\n })\n\n if (cookieOpts !== undefined) {\n const sameSite = cookieOpts.sameSite ?? 'Lax'\n const baseCookieOptions = {\n httpOnly: cookieOpts.httpOnly ?? true,\n sameSite,\n secure: sameSite === 'None' ? true : (cookieOpts.secure ?? true),\n path: cookieOpts.path ?? '/',\n } as const\n setCookie(c, accessTokenName, accessToken, {\n ...baseCookieOptions,\n maxAge: expiresIn,\n })\n return c.json({ ok: true }, 200)\n }\n\n return c.json({ accessToken }, 200)\n }\n },\n\n middleware(): MiddlewareHandler {\n return async (c, next) => {\n const authHeader = c.req.header('Authorization')\n if (!authHeader || !/^Bearer\\s+/i.test(authHeader)) {\n throw new HTTPException(401, { message: 'Unauthorized' })\n }\n const token = authHeader.replace(/^Bearer\\s+/i, '')\n if (!token) {\n throw new HTTPException(401, { message: 'Unauthorized' })\n }\n let payload: Record<string, unknown>\n try {\n payload = await verify<Record<string, unknown>>(token, opts.secret)\n } catch (err) {\n console.warn('access token verify failed', err)\n throw new HTTPException(401, { message: 'Unauthorized' })\n }\n if (payload.type !== 'access') {\n throw new HTTPException(401, { message: 'Unauthorized' })\n }\n // biome-ignore lint/suspicious/noExplicitAny: Variables shape is unknown at this call site\n c.set('user' as any, payload)\n await next()\n }\n },\n }\n}\n","import type { MiddlewareHandler } from 'hono'\nimport { HTTPException } from 'hono/http-exception'\nimport { verify } from './jwt.js'\n\nexport function authMiddleware<\n TPayload extends Record<string, unknown> = Record<string, unknown>,\n>(opts: { secret: string }): MiddlewareHandler<{ Variables: { user: TPayload } }> {\n return async (c, next) => {\n const authHeader = c.req.header('Authorization')\n if (!authHeader || !/^Bearer\\s+/i.test(authHeader)) {\n throw new HTTPException(401, { message: 'Unauthorized' })\n }\n const token = authHeader.replace(/^Bearer\\s+/i, '')\n if (!token) {\n throw new HTTPException(401, { message: 'Unauthorized' })\n }\n let payload: TPayload\n try {\n payload = await verify<TPayload>(token, opts.secret)\n } catch (err) {\n throw new HTTPException(401, { message: 'Unauthorized', cause: err })\n }\n if ((payload as Record<string, unknown>).type !== 'access') {\n throw new HTTPException(401, { message: 'Unauthorized' })\n }\n c.set('user', payload)\n await next()\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/blacklist-stores/kv.ts","../src/hashers/pbkdf2.ts","../src/jwt.ts","../src/create-auth.ts","../src/middleware.ts"],"names":["utf8","toBase64url","fromBase64url","accessToken","HTTPException"],"mappings":"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","file":"index.js","sourcesContent":["import type { KVNamespace } from '@cloudflare/workers-types'\nimport type { BlacklistStore } from '../blacklist-store.js'\n\nasync function hashKey(jti: string): Promise<string> {\n const encoder = new TextEncoder()\n const buf = await crypto.subtle.digest('SHA-256', encoder.encode(jti))\n const bytes = new Uint8Array(buf)\n let hex = ''\n for (let i = 0; i < bytes.length; i++) {\n hex += (bytes[i] as number).toString(16).padStart(2, '0')\n }\n return hex\n}\n\nexport function kvBlacklistStore(kv: KVNamespace, opts?: { prefix?: string }): BlacklistStore {\n const prefix = opts?.prefix ?? 'nanoka-auth:bl:'\n\n return {\n async add(jti: string, expiresAt: number): Promise<void> {\n const ttl = Math.min(2592000, Math.max(60, expiresAt - Math.floor(Date.now() / 1000)))\n await kv.put(`${prefix}${await hashKey(jti)}`, '1', { expirationTtl: ttl })\n },\n\n async has(jti: string): Promise<boolean> {\n return (await kv.get(`${prefix}${await hashKey(jti)}`)) !== null\n },\n\n async addWithSubject(jti: string, sub: string, expiresAt: number): Promise<void> {\n const ttl = Math.min(2592000, Math.max(60, expiresAt - Math.floor(Date.now() / 1000)))\n await kv.put(`${prefix}${await hashKey(jti)}`, JSON.stringify({ sub }), {\n expirationTtl: ttl,\n })\n },\n\n async hasForSubject(jti: string, sub: string): Promise<boolean> {\n const value = await kv.get(`${prefix}${await hashKey(jti)}`)\n if (value === null) return false\n if (value === '1') return true\n try {\n const parsed = JSON.parse(value) as unknown\n if (typeof parsed === 'object' && parsed !== null && 'sub' in parsed) {\n return (parsed as { sub: unknown }).sub === sub\n }\n } catch {\n // malformed value: treat as blacklisted (conservative)\n }\n return true\n },\n }\n}\n","import type { Hasher } from '../hasher.js'\n\nconst ITERATIONS = 310_000\nconst SALT_BYTES = 16\nconst HASH_BYTES = 32\nconst HASH_NAME = 'SHA-256'\n\nfunction utf8(str: string): Uint8Array<ArrayBuffer> {\n return new TextEncoder().encode(str) as unknown as Uint8Array<ArrayBuffer>\n}\n\nfunction toBase64url(buf: ArrayBuffer): string {\n const bytes = new Uint8Array(buf)\n let binary = ''\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i] as number)\n }\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\nfunction fromBase64url(str: string): Uint8Array<ArrayBuffer> | null {\n try {\n const padded = str\n .replace(/-/g, '+')\n .replace(/_/g, '/')\n .padEnd(str.length + ((4 - (str.length % 4)) % 4), '=')\n const binary = atob(padded)\n const buf = new ArrayBuffer(binary.length)\n const bytes = new Uint8Array(buf)\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i)\n }\n return bytes\n } catch {\n return null\n }\n}\n\nasync function derive(\n plain: string,\n salt: Uint8Array<ArrayBuffer>,\n iterations: number,\n): Promise<ArrayBuffer> {\n const key = await crypto.subtle.importKey('raw', utf8(plain), 'PBKDF2', false, ['deriveBits'])\n return crypto.subtle.deriveBits(\n { name: 'PBKDF2', salt, iterations, hash: HASH_NAME },\n key,\n HASH_BYTES * 8,\n )\n}\n\nfunction timingSafeEqual(a: Uint8Array<ArrayBuffer>, b: Uint8Array<ArrayBuffer>): boolean {\n if (a.length !== b.length) return false\n let diff = 0\n for (let i = 0; i < a.length; i++) {\n diff |= (a[i] as number) ^ (b[i] as number)\n }\n return diff === 0\n}\n\nexport const pbkdf2Hasher: Hasher = {\n async hash(plain: string): Promise<string> {\n const saltBuf = new ArrayBuffer(SALT_BYTES)\n const salt = new Uint8Array(saltBuf) as Uint8Array<ArrayBuffer>\n crypto.getRandomValues(salt)\n const derived = await derive(plain, salt, ITERATIONS)\n return `$pbkdf2$${ITERATIONS}$${toBase64url(saltBuf)}$${toBase64url(derived)}`\n },\n\n async verify(plain: string, stored: string): Promise<boolean> {\n const parts = stored.split('$')\n if (parts.length !== 5 || parts[0] !== '') return false\n const algoId = parts[1]\n const iterStr = parts[2]\n const saltB64 = parts[3]\n const hashB64 = parts[4]\n if (algoId !== 'pbkdf2') return false\n if (iterStr === undefined || saltB64 === undefined || hashB64 === undefined) return false\n\n const iterations = Number.parseInt(iterStr, 10)\n if (!Number.isFinite(iterations) || iterations <= 0 || iterations > 1_000_000) return false\n\n const salt = fromBase64url(saltB64)\n const storedHash = fromBase64url(hashB64)\n\n if (salt === null || storedHash === null) return false\n if (salt.length === 0 || storedHash.length !== HASH_BYTES) return false\n\n const derived = await derive(plain, salt, iterations)\n const derivedBytes = new Uint8Array(derived) as Uint8Array<ArrayBuffer>\n\n return timingSafeEqual(derivedBytes, storedHash)\n },\n}\n","const ALGORITHM = 'HS256'\n\nexport interface SignOptions {\n expiresIn?: number\n}\n\nfunction utf8(str: string): Uint8Array<ArrayBuffer> {\n return new TextEncoder().encode(str) as unknown as Uint8Array<ArrayBuffer>\n}\n\nfunction toBase64url(buf: ArrayBuffer): string {\n const bytes = new Uint8Array(buf)\n let binary = ''\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i] as number)\n }\n return btoa(binary).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\nfunction fromBase64url(str: string): ArrayBuffer {\n const padded = str\n .replace(/-/g, '+')\n .replace(/_/g, '/')\n .padEnd(str.length + ((4 - (str.length % 4)) % 4), '=')\n const binary = atob(padded)\n const buf = new ArrayBuffer(binary.length)\n const bytes = new Uint8Array(buf)\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i)\n }\n return buf\n}\n\nasync function importKey(secret: string): Promise<CryptoKey> {\n return crypto.subtle.importKey('raw', utf8(secret), { name: 'HMAC', hash: 'SHA-256' }, false, [\n 'sign',\n 'verify',\n ])\n}\n\n/**\n * HS256 で JWT を署名する。\n * @remarks アルゴリズムは HS256 固定。RS256 / ES256 等の非対称アルゴリズムを追加する場合は\n * 鍵オブジェクト型を分離し、verify に期待する alg を引数で渡す API に変更すること(alg confusion 攻撃防止)。\n */\nexport async function sign(\n payload: Record<string, unknown>,\n secret: string,\n options?: SignOptions,\n): Promise<string> {\n if (!secret) throw new Error('secret must not be empty')\n const header = { alg: ALGORITHM, typ: 'JWT' }\n const headerB64 = toBase64url(utf8(JSON.stringify(header)).buffer as ArrayBuffer)\n\n if (options?.expiresIn !== undefined && !Number.isFinite(options.expiresIn)) {\n throw new Error('expiresIn must be a finite number')\n }\n const finalPayload =\n options?.expiresIn !== undefined\n ? { ...payload, exp: Math.floor(Date.now() / 1000) + options.expiresIn }\n : payload\n const payloadB64 = toBase64url(utf8(JSON.stringify(finalPayload)).buffer as ArrayBuffer)\n\n const signingInput = `${headerB64}.${payloadB64}`\n const key = await importKey(secret)\n const signatureBuf = await crypto.subtle.sign('HMAC', key, utf8(signingInput))\n const signatureB64 = toBase64url(signatureBuf)\n\n return `${signingInput}.${signatureB64}`\n}\n\n/**\n * HS256 JWT を検証してペイロードを返す。\n * @remarks 戻り値の型パラメータ T は実行時検証なし。受け取った payload は Zod 等で検証すること。\n * `nbf` / `iat` / `aud` / `iss` クレームは検証しない。必要な場合はアプリ側で payload を検証すること。\n */\nexport async function verify<T = Record<string, unknown>>(\n token: string,\n secret: string,\n): Promise<T> {\n if (!secret) throw new Error('secret must not be empty')\n const segments = token.split('.')\n if (segments.length !== 3) {\n throw new Error('Invalid token format')\n }\n\n const [headerB64, payloadB64, signatureB64] = segments as [string, string, string]\n\n const header = JSON.parse(new TextDecoder().decode(fromBase64url(headerB64))) as unknown\n if (typeof header !== 'object' || header === null || Array.isArray(header)) {\n throw new Error('Invalid token header')\n }\n if ((header as Record<string, unknown>).typ !== 'JWT') {\n throw new Error('Invalid token header')\n }\n const alg = (header as Record<string, unknown>).alg\n if (typeof alg !== 'string' || alg.toLowerCase() === 'none') {\n throw new Error('Invalid token header')\n }\n if (alg !== ALGORITHM) {\n throw new Error('Invalid token header')\n }\n\n const signingInput = `${headerB64}.${payloadB64}`\n const key = await importKey(secret)\n const signatureBuf = fromBase64url(signatureB64)\n const valid = await crypto.subtle.verify('HMAC', key, signatureBuf, utf8(signingInput))\n if (!valid) {\n throw new Error('Invalid signature')\n }\n\n const payload = JSON.parse(new TextDecoder().decode(fromBase64url(payloadB64))) as unknown\n if (typeof payload !== 'object' || payload === null || Array.isArray(payload)) {\n throw new Error('Invalid payload')\n }\n const payloadObj = payload as Record<string, unknown>\n\n if (payloadObj.exp !== undefined) {\n if (typeof payloadObj.exp !== 'number' || !Number.isFinite(payloadObj.exp)) {\n throw new Error('Invalid exp claim')\n }\n if (Math.floor(Date.now() / 1000) >= payloadObj.exp) {\n throw new Error('Token expired')\n }\n }\n\n return payloadObj as T\n}\n","import type { NanokaModel } from '@nanokajs/core'\nimport type { Handler, MiddlewareHandler } from 'hono'\nimport { getCookie, setCookie } from 'hono/cookie'\nimport { HTTPException } from 'hono/http-exception'\nimport type { BlacklistStore } from './blacklist-store.js'\nimport type { Hasher } from './hasher.js'\nimport { pbkdf2Hasher } from './hashers/pbkdf2.js'\nimport { sign, verify } from './jwt.js'\n\n// defense-in-depth: refresh token 文字列の長さ上限。verify() 前段で base64 デコードに\n// CPU を消費させる巨大入力攻撃を遮断する。HS256 JWT の現実的サイズを十分上回る閾値。\nconst MAX_REFRESH_TOKEN_LENGTH = 4096\n\nexport interface CookieOptions {\n httpOnly?: boolean\n sameSite?: 'Strict' | 'Lax' | 'None'\n secure?: boolean\n path?: string\n accessTokenName?: string\n refreshTokenName?: string\n}\n\nexport interface CreateAuthOptions {\n // biome-ignore lint/suspicious/noExplicitAny: any is necessary for NanokaModel generic constraint\n model: NanokaModel<any>\n secret: string\n fields: { identifier: string; password: string }\n hasher?: Hasher\n jwt?: { expiresIn?: number; refreshExpiresIn?: number; rotation?: boolean }\n cookie?: CookieOptions\n blacklist?: BlacklistStore\n}\n\nexport interface AuthInstance {\n loginHandler(): Handler\n refreshHandler(): Handler\n middleware(): MiddlewareHandler\n}\n\nexport function createAuth(opts: CreateAuthOptions): AuthInstance {\n if (opts.secret.length < 32) {\n throw new Error('createAuth: secret must be at least 32 characters')\n }\n\n const rotation = opts.jwt?.rotation ?? false\n if (rotation && opts.blacklist === undefined) {\n throw new Error('createAuth: blacklist is required when jwt.rotation is true')\n }\n\n const hasher = opts.hasher ?? pbkdf2Hasher\n const expiresIn = opts.jwt?.expiresIn ?? 900\n const refreshExpiresIn = opts.jwt?.refreshExpiresIn ?? 604_800\n const identifierField = opts.fields.identifier\n const passwordField = opts.fields.password\n const dummyHashPromise = hasher.hash('__dummy__')\n\n const cookieOpts = opts.cookie\n const accessTokenName = cookieOpts?.accessTokenName ?? 'access_token'\n const refreshTokenName = cookieOpts?.refreshTokenName ?? 'refresh_token'\n\n return {\n loginHandler(): Handler {\n return async (c) => {\n let body: Record<string, unknown>\n try {\n body = await c.req.json<Record<string, unknown>>()\n } catch {\n throw new HTTPException(401, { message: 'Invalid credentials' })\n }\n const identifierValue = body[identifierField]\n const passwordValue = body[passwordField]\n\n if (typeof identifierValue !== 'string' || typeof passwordValue !== 'string') {\n throw new HTTPException(401, { message: 'Invalid credentials' })\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: model is typed as NanokaModel<any> per CreateAuthOptions\n const user = await opts.model.findOne({ [identifierField]: identifierValue } as any)\n\n if (user === null) {\n const dummyHash = await dummyHashPromise\n await hasher.verify('__dummy__', dummyHash)\n throw new HTTPException(401, { message: 'Invalid credentials' })\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: user shape is unknown at compile time\n const storedHash = (user as any)[passwordField] as string\n const valid = await hasher.verify(passwordValue, storedHash)\n\n if (!valid) {\n throw new HTTPException(401, { message: 'Invalid credentials' })\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: user shape is unknown at compile time\n const sub: string = String((user as any).id ?? (user as any)[identifierField])\n\n const refreshPayload: Record<string, unknown> = { sub, type: 'refresh' }\n if (rotation) {\n refreshPayload.jti = crypto.randomUUID()\n }\n\n const [accessToken, refreshToken] = await Promise.all([\n sign({ sub, type: 'access' }, opts.secret, { expiresIn }),\n sign(refreshPayload, opts.secret, { expiresIn: refreshExpiresIn }),\n ])\n\n if (cookieOpts !== undefined) {\n const sameSite = cookieOpts.sameSite ?? 'Lax'\n const baseCookieOptions = {\n httpOnly: cookieOpts.httpOnly ?? true,\n sameSite,\n secure: sameSite === 'None' ? true : (cookieOpts.secure ?? true),\n path: cookieOpts.path ?? '/',\n } as const\n setCookie(c, accessTokenName, accessToken, {\n ...baseCookieOptions,\n maxAge: expiresIn,\n })\n setCookie(c, refreshTokenName, refreshToken, {\n ...baseCookieOptions,\n maxAge: refreshExpiresIn,\n })\n return c.json({ ok: true }, 200)\n }\n\n return c.json({ accessToken, refreshToken }, 200)\n }\n },\n\n refreshHandler(): Handler {\n return async (c) => {\n let refreshToken: string | undefined\n\n if (cookieOpts !== undefined) {\n refreshToken = getCookie(c, refreshTokenName)\n if ((refreshToken === undefined || refreshToken === '') && rotation) {\n throw new HTTPException(401, { message: 'Invalid credentials' })\n }\n }\n\n if (refreshToken === undefined || refreshToken === '') {\n let body: Record<string, unknown>\n try {\n body = await c.req.json<Record<string, unknown>>()\n } catch {\n throw new HTTPException(401, { message: 'Invalid credentials' })\n }\n const bodyRefreshToken = body.refreshToken\n if (typeof bodyRefreshToken !== 'string') {\n throw new HTTPException(401, { message: 'Invalid credentials' })\n }\n refreshToken = bodyRefreshToken\n }\n\n if (refreshToken.length > MAX_REFRESH_TOKEN_LENGTH) {\n throw new HTTPException(401, { message: 'Invalid credentials' })\n }\n\n let payload: Record<string, unknown>\n try {\n payload = await verify<Record<string, unknown>>(refreshToken, opts.secret)\n } catch (err) {\n console.warn('refresh token verify failed', err)\n throw new HTTPException(401, { message: 'Invalid credentials' })\n }\n\n if (payload.type !== 'refresh') {\n throw new HTTPException(401, { message: 'Invalid credentials' })\n }\n\n if (typeof payload.sub !== 'string' || payload.sub.length === 0) {\n throw new HTTPException(401, { message: 'Invalid credentials' })\n }\n\n if (rotation) {\n const jti = payload.jti\n if (typeof jti !== 'string' || jti.length === 0) {\n throw new HTTPException(401, { message: 'Invalid credentials' })\n }\n if (jti.length > 256) {\n throw new HTTPException(401, { message: 'Invalid credentials' })\n }\n if (!/^[A-Za-z0-9_-]+$/.test(jti)) {\n throw new HTTPException(401, { message: 'Invalid credentials' })\n }\n // biome-ignore lint/style/noNonNullAssertion: blacklist is guaranteed non-null when rotation is true (validated at createAuth call)\n const blacklist = opts.blacklist!\n // Use subject-aware methods only when BOTH are present.\n // Using them independently (e.g. hasForSubject without addWithSubject) would\n // cause the read and write paths to use different storage keys, allowing\n // a revoked JTI to be accepted on the read path (replay attack).\n const useSubjectAware =\n blacklist.hasForSubject !== undefined && blacklist.addWithSubject !== undefined\n const blacklisted = useSubjectAware\n ? await blacklist.hasForSubject!(jti, payload.sub)\n : await blacklist.has(jti)\n if (blacklisted) {\n throw new HTTPException(401, { message: 'Invalid credentials' })\n }\n const exp =\n typeof payload.exp === 'number'\n ? payload.exp\n : Math.floor(Date.now() / 1000) + refreshExpiresIn\n try {\n if (useSubjectAware) {\n await blacklist.addWithSubject!(jti, payload.sub, exp)\n } else {\n await blacklist.add(jti, exp)\n }\n } catch (err) {\n console.error('blacklist.add failed', err)\n throw new HTTPException(401, { message: 'Invalid credentials' })\n }\n\n const newJti = crypto.randomUUID()\n const [accessToken, newRefreshToken] = await Promise.all([\n sign({ sub: payload.sub, type: 'access' }, opts.secret, { expiresIn }),\n sign({ sub: payload.sub, type: 'refresh', jti: newJti }, opts.secret, {\n expiresIn: refreshExpiresIn,\n }),\n ])\n\n if (cookieOpts !== undefined) {\n const sameSite = cookieOpts.sameSite ?? 'Lax'\n const baseCookieOptions = {\n httpOnly: cookieOpts.httpOnly ?? true,\n sameSite,\n secure: sameSite === 'None' ? true : (cookieOpts.secure ?? true),\n path: cookieOpts.path ?? '/',\n } as const\n setCookie(c, accessTokenName, accessToken, {\n ...baseCookieOptions,\n maxAge: expiresIn,\n })\n setCookie(c, refreshTokenName, newRefreshToken, {\n ...baseCookieOptions,\n maxAge: refreshExpiresIn,\n })\n return c.json({ ok: true }, 200)\n }\n\n return c.json({ accessToken, refreshToken: newRefreshToken }, 200)\n }\n\n const accessToken = await sign({ sub: payload.sub, type: 'access' }, opts.secret, {\n expiresIn,\n })\n\n if (cookieOpts !== undefined) {\n const sameSite = cookieOpts.sameSite ?? 'Lax'\n const baseCookieOptions = {\n httpOnly: cookieOpts.httpOnly ?? true,\n sameSite,\n secure: sameSite === 'None' ? true : (cookieOpts.secure ?? true),\n path: cookieOpts.path ?? '/',\n } as const\n setCookie(c, accessTokenName, accessToken, {\n ...baseCookieOptions,\n maxAge: expiresIn,\n })\n return c.json({ ok: true }, 200)\n }\n\n return c.json({ accessToken }, 200)\n }\n },\n\n middleware(): MiddlewareHandler {\n return async (c, next) => {\n const authHeader = c.req.header('Authorization')\n if (!authHeader || !/^Bearer\\s+/i.test(authHeader)) {\n throw new HTTPException(401, { message: 'Unauthorized' })\n }\n const token = authHeader.replace(/^Bearer\\s+/i, '')\n if (!token) {\n throw new HTTPException(401, { message: 'Unauthorized' })\n }\n let payload: Record<string, unknown>\n try {\n payload = await verify<Record<string, unknown>>(token, opts.secret)\n } catch (err) {\n console.warn('access token verify failed', err)\n throw new HTTPException(401, { message: 'Unauthorized' })\n }\n if (payload.type !== 'access') {\n throw new HTTPException(401, { message: 'Unauthorized' })\n }\n // biome-ignore lint/suspicious/noExplicitAny: Variables shape is unknown at this call site\n c.set('user' as any, payload)\n await next()\n }\n },\n }\n}\n","import type { MiddlewareHandler } from 'hono'\nimport { HTTPException } from 'hono/http-exception'\nimport { verify } from './jwt.js'\n\nexport function authMiddleware<\n TPayload extends Record<string, unknown> = Record<string, unknown>,\n>(opts: { secret: string }): MiddlewareHandler<{ Variables: { user: TPayload } }> {\n return async (c, next) => {\n const authHeader = c.req.header('Authorization')\n if (!authHeader || !/^Bearer\\s+/i.test(authHeader)) {\n throw new HTTPException(401, { message: 'Unauthorized' })\n }\n const token = authHeader.replace(/^Bearer\\s+/i, '')\n if (!token) {\n throw new HTTPException(401, { message: 'Unauthorized' })\n }\n let payload: TPayload\n try {\n payload = await verify<TPayload>(token, opts.secret)\n } catch (err) {\n throw new HTTPException(401, { message: 'Unauthorized', cause: err })\n }\n if ((payload as Record<string, unknown>).type !== 'access') {\n throw new HTTPException(401, { message: 'Unauthorized' })\n }\n c.set('user', payload)\n await next()\n }\n}\n"]}
|