mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 20:21:09 +02:00
feat(auth): server-side tier gating via requireTier middleware
The JWT already carried a `tier` claim but nothing on the server read it
— AuthGate enforcement was client-only, so a valid JWT could hit paid
LLM/research endpoints regardless of the user's access tier.
- shared-hono authMiddleware now extracts `tier` into `c.userTier`,
defaulting unknown/missing claims to `public` (never silently grants
higher access).
- New `requireTier(minTier)` middleware + `hasTier`/`getTierLevel`
helpers. Tier hierarchy (guest < public < beta < alpha < founder) is
mirrored locally to avoid pulling the Svelte-facing shared-branding
package into Bun services.
- Applied `requireTier('beta')` as defense-in-depth on resource-heavy
apps/api modules (chat, context, food, guides, news-research, picture,
plants, research, traces, who) and the MCP endpoint. Pure CRUD modules
stay auth-only — access there is gated by ownership, not tier.
- DEV_BYPASS_AUTH now injects `userTier` (defaults to founder, override
via DEV_USER_TIER).
- Authentication guideline documents the pattern + test suite covers
hierarchy, passes-at-minimum, and rejection paths.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4efdcfffdb
commit
76d11a84ee
10 changed files with 208 additions and 5 deletions
|
|
@ -13,6 +13,7 @@ import {
|
|||
errorHandler,
|
||||
notFoundHandler,
|
||||
rateLimitMiddleware,
|
||||
requireTier,
|
||||
type AuthVariables,
|
||||
} from '@mana/shared-hono';
|
||||
|
||||
|
|
@ -57,8 +58,35 @@ app.route('/api/v1/wetter', wetterRoutes);
|
|||
|
||||
app.use('/api/*', authMiddleware());
|
||||
|
||||
// ─── Tier Gating ────────────────────────────────────────────
|
||||
// Defense-in-depth on top of per-route credits validation.
|
||||
// Routes that call LLMs, image-gen, or external search APIs are gated
|
||||
// to `beta`+ so that unauthenticated guest fallbacks (tier='public'
|
||||
// from a missing claim) can't hit paid infrastructure.
|
||||
// Pure CRUD modules (calendar, contacts, music, storage, todo, news,
|
||||
// presi, moodlit) rely on authMiddleware alone — users access only
|
||||
// their own records.
|
||||
const RESOURCE_MODULES = [
|
||||
'chat',
|
||||
'context',
|
||||
'food',
|
||||
'guides',
|
||||
'news-research',
|
||||
'picture',
|
||||
'plants',
|
||||
'research',
|
||||
'traces',
|
||||
'who',
|
||||
] as const;
|
||||
for (const mod of RESOURCE_MODULES) {
|
||||
app.use(`/api/v1/${mod}/*`, requireTier('beta'));
|
||||
}
|
||||
|
||||
// ─── MCP Endpoint ──────────────────────────────────────────
|
||||
// Streamable HTTP transport: POST (messages), GET (SSE stream), DELETE (close)
|
||||
// MCP exposes the full tool catalog including LLM/research tools, so it
|
||||
// gets the same minimum tier.
|
||||
app.use('/api/v1/mcp', requireTier('beta'));
|
||||
app.all('/api/v1/mcp', (c) => handleMcpRequest(c.req.raw, c.get('userId')));
|
||||
|
||||
// ─── Module Routes ──────────────────────────────────────────
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue