auth-team.ts40 lines · main
| 1 | import type { MiddlewareHandler } from 'hono'; |
| 2 | import { ForbiddenError } from '@briven/shared'; |
| 3 | import { hasRoleAtLeast } from '../services/access.js'; |
| 4 | import { getAuthTeamRole } from '../services/auth-team-seats.js'; |
| 5 | import type { MemberRole } from '../db/schema.js'; |
| 6 | import type { Session, User } from './session.js'; |
| 7 | |
| 8 | /** |
| 9 | * Auth-team access gate for `/v1/projects/:id/auth/*` routes. |
| 10 | * |
| 11 | * Must run AFTER `requireProjectAuth()` (which populates `projectRole`). |
| 12 | * If the user already has project-level admin/owner access, pass through. |
| 13 | * Otherwise, check the `project_auth_team_members` table — an auth team |
| 14 | * admin is treated as project admin for the remainder of the request. |
| 15 | * |
| 16 | * Viewer-tier auth team members are NOT elevated by this middleware; |
| 17 | * routes that want to allow viewers should skip this gate. |
| 18 | */ |
| 19 | export const requireAuthTeamAdmin = (): MiddlewareHandler => async (c, next) => { |
| 20 | const role = c.get('projectRole') as MemberRole | null | undefined; |
| 21 | if (role && hasRoleAtLeast(role, 'admin')) { |
| 22 | return next(); |
| 23 | } |
| 24 | |
| 25 | const user = c.get('user') as User | null; |
| 26 | const projectId = c.req.param('id'); |
| 27 | if (user && projectId) { |
| 28 | const teamRole = await getAuthTeamRole(projectId, user.id); |
| 29 | if (teamRole === 'admin') { |
| 30 | // Elevate the request context so downstream `requireProjectRole('admin')` |
| 31 | // sees an effective admin role. |
| 32 | c.set('projectRole', 'admin'); |
| 33 | return next(); |
| 34 | } |
| 35 | } |
| 36 | |
| 37 | throw new ForbiddenError('requires auth team admin access'); |
| 38 | }; |
| 39 | |
| 40 | export type { Session, User }; |