PAS7 Studio

Auth middleware у Bun.js: JWT, sessions, API keys і multi-tenant context без хаосу

Практичний deep dive по auth middleware у Bun.js: як будувати JWT, session і API-key перевірки, де тримати tenant context, як розділяти 401/403, що кешувати, як уникати витоку токенів і які best practices брати з OWASP, Hono та Elysia.

14 трав. 2026 р.· 13 хв читання· Технології
Кому підійдеBackend інженериFull-stack розробникиTech leadsКоманди, що будують SaaS або internal API на Bun
Технічна ілюстрація auth middleware pipeline у Bun.js з токеном, сесією, API key і tenant context
Гайд / СеріяСтаття серії

Bun.js Middleware Production Guide 2026

Серія про production middleware у Bun.js: огляд, безпека, performance, observability, rate limiting, body parsing, WebSocket/SSE і тестування request pipeline.

Найнебезпечніші auth баги рідко виглядають як “користувач без токена зайшов у систему”. Частіше токен валідний, сесія існує, API key справжній, але middleware прив'язав request не до того tenant, не перевірив scope, переплутав 401 і 403, або залогував секрет.

Bun дає швидкий HTTP layer, але auth - це не runtime trick. Це contract: які credentials приймаються, де вони зберігаються, як валідовуються, хто створює user context, де починається authorization і як усе це тестується.

У цій статті ми не будемо робити “ще один login tutorial”. Ми розберемо production-патерни auth middleware для Bun API.

JWT, sessions і API keys мають різні failure modes.
Authentication і authorization не варто змішувати в один глобальний middleware.
Tenant context має бути явно виведений з credential і policy, а не з довільного header.
Токени й cookies мають жити у logs так, ніби їх там немає.

Це другий chapter серії про Bun middleware. Перший дав загальну модель pipeline, цей фокусується на auth шарі: де він має бути тонким, де йому потрібен storage, а де він уже не middleware.

Ментальна модель auth pipeline: extract credential, verify, resolve subject, resolve tenant, attach context, continue або reject.
JWT middleware: коли він підходить, які claims перевіряти, чому decode без verify не є auth. [4]
Session middleware: cookies, HTTPS, secure attributes, session store і logout semantics. [1][5]
API keys: hashed storage, scopes, rotation, owner metadata і machine-to-machine доступ.
Hono і Elysia: коли брати built-in middleware/plugins, а коли писати власний thin layer. [2][3][6][7]
Погані практики: токени в localStorage/logs, глобальний DB lookup, tenant з header без перевірки, long-lived JWT без revocation story.

Authentication відповідає на питання: “хто або що робить request?” Authorization відповідає: “чи має цей subject право зробити саме цю дію над саме цим ресурсом?” Middleware часто має робити перше і підготувати context для другого.

Добрий auth middleware створює auth object: subjectId, subjectType, tenantId, scopes, sessionId, authMethod, requestId. Але він не повинен знати всі бізнес-правила продукту. Наприклад, canUpdateInvoice(invoiceId) краще перевіряти у route/use case, бо там є resource context.

Якщо зробити глобальний middleware “перевіряє усе”, ви отримаєте policy engine без схем, тестів і visibility. Якщо зробити middleware надто тонким, кожен route почне сам читати headers і cookies. Баланс - тонкий auth context плюс explicit authorization біля бізнес-дії.

Auth middleware краще мислити як state machine: credential витягується, перевіряється, перетворюється на subject і tenant context, після чого request пропускається або відхиляється.

Скріншот секції auth-vs-authorization

Практичне правило

Middleware має сказати, хто прийшов і з яким базовим контекстом. Чи може він виконати конкретну дію, має вирішувати route, guard або use case.

Немає одного “правильного” auth механізму. Є різні trade-offs: stateless перевірка, контрольований logout, machine-to-machine доступ, rotation, revocation і UX.

МеханізмКоли підходитьЩо перевірятиГоловний ризик
JWT access tokenMobile/API clients, service-to-service, distributed verificationexp, iss, aud, signature, algorithm, subject, scopesВажка revocation модель і спокуса робити довгоживучі tokens
Server-side sessionBrowser apps, dashboards, SaaS, де потрібен контрольований logoutSession id у secure cookie, session store, expiry, device/risk metadataSession fixation, insecure cookies, weak store cleanup
API keyIntegrations, webhooks, internal automations, machine clientsHashed key lookup, scope, owner, environment, last used, rotationPlaintext key storage і ключі без scopes або expiry policy
HybridBFF, enterprise SaaS, admin panels plus public APISession для browser, JWT/API keys для servicesРізні auth моделі без єдиного audit і error contract

JWT, sessions і API keys відповідають на різні задачі: stateless access, browser logout control і machine-to-machine доступ.

Скріншот секції credential-map

Висновок

Для browser SaaS зазвичай починайте із sessions. Для external API - API keys або короткоживучі JWT. Для distributed systems - JWT плюс revocation або introspection там, де ризик високий.

У native Bun.serve ви можете зробити auth layer без framework. Важливо не мутувати глобальний state і не ховати context у module-level змінних. Передавайте context явно: або через wrapper, або через власний AppRequestContext.

Один з простих патернів:

TS
type AuthContext = {
  subjectId: string;
  tenantId: string;
  scopes: string[];
  method: "jwt" | "session" | "api-key";
};

type AppContext = { auth: AuthContext | null; requestId: string };
type Handler = (req: Request, ctx: AppContext) => Promise<Response> | Response;

type Middleware = (next: Handler) => Handler;

const unauthorized = () =>
  Response.json({ error: "unauthorized" }, { status: 401 });

const withAuth: Middleware = (next) => async (req, ctx) => {
  const header = req.headers.get("authorization");

  if (!header?.startsWith("Bearer ")) return unauthorized();

  const token = header.slice("Bearer ".length);
  const auth = await verifyAccessToken(token);

  if (!auth) return unauthorized();

  return next(req, { ...ctx, auth });
};

Bun.serve({
  fetch: withAuth(async (_req, ctx) => {
    return Response.json({ subjectId: ctx.auth?.subjectId });
  }),
});

Цей приклад навмисно не показує реалізацію verifyAccessToken, бо вона залежить від вашого JWT/session/API-key рішення. Важливий shape: middleware не читає business resource, не робить permissions на invoice/project/order і не створює side effects. Він створює auth context або повертає 401.

Що тут важливо

Контекст має бути request-scoped і явний. Якщо auth state живе у глобальній змінній, singleton або mutable module object, ви створюєте ризик leakage між запитами.

Bun має вбудований Cookie API для HTTP server routes: BunRequest містить cookies, а modified cookies автоматично застосовуються до response у routes. Це зручно для session cookies, logout і refresh flows. [1]

Але cookie API не вирішує security policy. OWASP session guidance наголошує на HTTPS для всієї session, Secure cookie attribute, обмеженій persistence, контролі session lifecycle і reauthentication після ризикових подій. [5]

Для browser auth у Bun варто проектувати sessions як server-side state: cookie містить непрозорий session id, а permissions, tenant і risk metadata живуть у session store. Це дає контрольований logout, rotation, device tracking і можливість відкликати session без очікування expiry JWT.

Session baseline

Cookie має бути httpOnly, secure, з коректним sameSite, вузьким path, зрозумілим TTL і server-side session store. Не кладіть sensitive user data прямо в cookie.

Якщо ви не хочете писати весь auth layer руками, Hono і Elysia мають готові primitives. Але готовий middleware не скасовує ваші правила issuer, audience, scopes, tenant і error contract.

Hono jwt() перевіряє token і кладе payload у context через c.get('jwtPayload'); middleware шукає Authorization header, якщо не налаштовано cookie option. [2]
Hono JWT Auth Middleware
Hono bearerAuth() підходить для API tokens і дає verifyToken, custom errors, realm, prefix і headerName. [3]
Hono Bearer Auth Middleware
Elysia має JWT plugin для signing/verification у handlers і Bearer plugin для витягування bearer token. Це добре лягає на Elysia lifecycle. [6][7]
Elysia JWT і Bearer plugins

Висновок

Built-in middleware варто брати для parsing і базової verification. Але tenant resolution, scopes, audit і business authorization залишаються вашим application design.

У SaaS найчастіша auth-помилка виглядає не як “немає токена”, а як “valid user зміг працювати в чужому tenant”. Причина проста: middleware взяв x-tenant-id з request і не перевірив, що subject справді має доступ до цього tenant.

Tenant context має виводитись із credential або перевірятися проти server-side membership. Якщо користувач може перемикати tenant, header або path param може бути input, але не authority.

Правильний flow: verify credential, отримати subject, отримати allowed tenants/scopes, зіставити requested tenant із allowed list, прикріпити resolved tenant у context. Якщо tenant не дозволений, це вже 403, а не 401.

Не використовуйте x-tenant-id як source of truth.
Не кешуйте tenant permissions довше, ніж дозволяє ваша модель revocation.
Логуйте tenant id і subject id, але не credential.
Пишіть тести на cross-tenant access, а не тільки на missing token.

Tenant має бути resolved і перевірений сервером. Header або path param може бути запитом користувача, але не доказом доступу.

Скріншот секції multi-tenant-context

Неправильні auth errors створюють проблеми для клієнтів, logs і security review. У Bun pipeline краще одразу зафіксувати contract.

01

Credential відсутній або невалідний

Немає Authorization, token malformed, signature invalid, session expired, API key не знайдено. Клієнт має розуміти, що треба пройти authentication.

02

Credential валідний, але дія заборонена

Subject authenticated, але не має tenant access, scope, role або permission для ресурсу.

03

Auth abuse або rate limit

Brute force, занадто багато invalid token attempts, API key abuse. Не змішуйте це з 401, бо клієнт і monitoring мають реагувати інакше.

04

Auth infrastructure failure

Session store, JWKS endpoint, Redis або DB недоступні. Для деяких routes краще fail closed, для public degraded flows може бути інша policy.

Висновок

Єдиний error format важливий так само, як і правильний status code. Клієнт, monitoring і security tooling мають бачити стабільний contract.

Auth часто стає найважчим middleware, бо тягне crypto, storage, tenant resolution і audit. Прискорення починається не з алгоритму, а з маршрутизації й caching policy.

Добре: public/system routes поза auth pipeline

/health, /ready, static assets і public docs не повинні робити token parsing або session lookup.

Обережно: DB lookup на кожен JWT

Якщо кожен JWT все одно іде в DB для перевірки, подумайте, чи JWT дає вам користь проти session id. Можливо, вам потрібна session/introspection модель.

Добре: короткий cache для stable auth metadata

Tenant membership, API key owner і JWKS keys можна кешувати з чітким TTL та revocation story.

Обережно: long-lived cache permissions

Permissions змінюються. Якщо кеш живе довше за security expectation, користувач зберігає доступ після revoke.

Висновок

Auth performance не має конфліктувати з revocation. Якщо кешуєте, одразу визначайте TTL, invalidation і high-risk fallback.

Ці помилки не специфічні тільки для Bun, але в Bun-проєктах вони часто з'являються під виглядом “ми просто швидко написали thin middleware”.

Використовувати jwt.decode() або читати payload без signature verification.

Не перевіряти exp, iss, aud і algorithm для JWT.

Зберігати access token у localStorage для browser app без чіткої XSS threat model.

Логувати Authorization, cookies, refresh token або full API key.

Зберігати API keys у plaintext замість hash + prefix lookup.

Давати API key без scopes, owner, environment і rotation policy.

Вважати x-tenant-id авторитетним джерелом tenant context.

Повертати 200 з { error: ... } для auth failures.

Змішувати authentication, tenant membership і resource authorization в один глобальний middleware.

Рев'ю правило

Якщо middleware не можна пояснити як маленький state machine з 401/403/next, він уже занадто складний або бере на себе не свою відповідальність.

Перед staging або security review пройдіться по цьому списку. Він спеціально сформульований як інженерний checklist, а не як загальні поради.

Credential extraction один раз

Один шар читає Authorization/cookies/API key і передає результат далі як typed context.

JWT verification повна

Signature, algorithm, expiry, issuer, audience, subject і clock skew policy перевірені.

Session cookies захищені

httpOnly, secure, sameSite, narrow path, TTL і HTTPS policy зафіксовані.

API keys не зберігаються відкрито

У базі тільки hash, key prefix, owner, scopes, last used, created/revoked metadata.

Tenant context перевіряється

Requested tenant звіряється з membership/scopes subject, а не приймається з header як факт.

401 і 403 розділені

Missing/invalid credential дає 401; valid credential без права на дію дає 403.

Secrets redacted

Logs, tracing, error reports і analytics не містять tokens, cookies, API keys або PII.

Revocation story існує

Для JWT, sessions і API keys є зрозуміла модель відкликання, rotation і forced logout.

У Bun легко написати швидкий auth wrapper. Складніше написати auth middleware, який не змішує authentication і authorization, не тече між tenants, не логить секрети, не створює зайвий DB lookup на кожен request і має нормальний error contract.

JWT, sessions і API keys не конкурують як “кращий” і “гірший” варіант. Вони відповідають на різні задачі. JWT зручний для stateless access, sessions - для browser UX і контрольованого logout, API keys - для integrations. У production часто потрібен hybrid, але з єдиним audit і policy шаром.

Правильний Bun auth pipeline виглядає просто: extract, verify, resolve subject, resolve tenant, attach context, continue або reject. Усе інше має бути явно спроєктовано, протестовано і виміряно.

Чи краще JWT за sessions у Bun.js?

Не завжди. JWT корисний для stateless access і service-to-service сценаріїв, але складніший для revocation. Sessions краще підходять для browser SaaS, де потрібен контрольований logout, secure cookies і session store. Вибір залежить від клієнта, threat model і revocation вимог. [4][5]

Чи можна робити auth middleware без Hono або Elysia?

Так. Native `Bun.serve` дозволяє написати auth як композицію функцій навколо `Request`/`Response`. Але ви самі відповідаєте за context, errors, order, tests і security policy.

Що має повертати middleware: 401 чи 403?

`401` для missing/invalid credential: немає token, session expired, invalid signature, API key не знайдено. `403` для валідного credential без permission, tenant access або required scope.

Де зберігати tenant context?

У request-scoped auth context після перевірки credential і membership. Не довіряйте `x-tenant-id` як source of truth. Header або path param може бути requested tenant, але middleware має перевірити доступ subject до нього.

Чи можна логувати токени для дебагу?

Ні. Authorization headers, cookies, refresh tokens і API keys мають бути redacted. Логуйте request id, subject id, tenant id, auth method і error class, але не credential.

Як зберігати API keys?

Показуйте повний API key тільки один раз при створенні, зберігайте hash, тримайте короткий prefix для lookup, додавайте owner, scopes, environment, created/last used/revoked metadata і rotation policy.

Джерела підтверджують поведінку Bun Cookie API, Hono/Elysia auth middleware/plugins і security baseline для JWT та session management.

Перевірено: 14 трав. 2026 р.Актуально для: Bun 1.3.xАктуально для: Bun.serve routesАктуально для: Hono 4.xАктуально для: Elysia 1.xАктуально для: TypeScript APIПеревірено з: Bun CookieMapПеревірено з: Hono JWT Auth MiddlewareПеревірено з: Hono Bearer Auth MiddlewareПеревірено з: Elysia JWT pluginПеревірено з: Elysia Bearer plugin

PAS7 Studio може допомогти спроєктувати auth middleware для Bun, Hono або Elysia: JWT/session/API-key модель, tenant context, scopes, secure cookies, rotation, observability і security review.

Це особливо корисно для SaaS, internal platforms, B2B API і міграцій з Express/Fastify, де auth behavior уже розповзся по middleware, guards і helpers.

Ви тут02/05

Auth middleware у Bun.js: JWT, sessions, API keys і multi-tenant контекст

Пов'язані статті

ai-assistants

Скільки коштує розробка AI асистента у 2026: RAG чатбот, база знань, CRM, Telegram та підтримка

Практичний гід для бізнесу: від чого залежить ціна розробки AI асистента у 2026 році, що входить у RAG чатбот, інтеграції з CRM, Telegram, guardrails, оцінювання, моніторинг і супровід.

blogs

AI для розробки лендінгів: де він реально прискорює запуск, а де псує конверсію

Дослідження про використання AI у розробці лендінгів: v0, Webflow AI, Builder.io, Framer-подібні AI builders, генерація UX, copy, SEO, персоналізація, A/B тести, ризики шаблонності, безпеки, доступності та технічного боргу.

growth

AI SEO / GEO у 2026: ваші наступні клієнти — не люди, а агенти

Пошук зміщується від кліків до відповідей. Боти та AI-агенти сканують, цитують, рекомендують і дедалі частіше купують. Дізнайтесь, що таке AI SEO / GEO, чому класичного SEO вже недостатньо, і як PAS7 Studio допомагає брендам перемагати у «агентному» вебі.

blogs

Найпотужніший чіп від Apple? M5 Pro і M5 Max б'ють рекорди

Аналітичний розбір Apple M5 Pro і M5 Max станом на березень 2026 року. Пояснюємо, чому ці чіпи можна вважати найпотужнішими професійними ноутбучними SoC від Apple, як вони виглядають на тлі M4 Pro, M4 Max, M1 Pro, M1 Max і що показують у порівнянні з актуальними Intel та AMD.

Професійна розробка для вашого бізнесу

Створюємо сучасні веб-рішення та боти для бізнесу. Дізнайтеся, як ми можемо допомогти вам досягти цілей.