Files
auth-portal/apps/api/src/plugins/auth-guards.ts
T

113 lines
2.6 KiB
TypeScript

import type { FastifyReply, FastifyRequest } from 'fastify'
import type { UserRow } from '@authportal/db'
import type { AppId, MeResponse } from '@authportal/shared'
import { APP_IDS } from '@authportal/shared'
import { getUserProfile, type UserProfile } from '../lib/user-profile.js'
export type AuthUser = {
id: string
email: string
name: string
isAdmin: boolean
apps: AppId[]
permissions: string[]
}
declare module '@fastify/jwt' {
interface FastifyJWT {
payload: {
sub: string
email: string
name: string
apps: string[]
permissions: string[]
tenants?: Record<string, string>
bgp_tenant_id?: string
is_admin?: boolean
iss: string
}
user: {
sub: string
email: string
name: string
apps: string[]
permissions: string[]
tenants?: Record<string, string>
bgp_tenant_id?: string
is_admin?: boolean
iss: string
}
}
}
declare module 'fastify' {
interface FastifyRequest {
authUser?: AuthUser
}
}
function asAppIds(apps: string[]): AppId[] {
return apps.filter((a): a is AppId =>
(APP_IDS as readonly string[]).includes(a),
)
}
export function toMe(user: UserRow, apps: string[], permissions: string[]): MeResponse {
return {
id: user.id,
email: user.email,
name: user.name,
is_admin: user.isAdmin,
apps: asAppIds(apps),
permissions,
}
}
export function authUserFromProfile(profile: UserProfile): AuthUser {
const { row } = profile
return {
id: row.id,
email: row.email,
name: row.name,
isAdmin: row.isAdmin,
apps: asAppIds(profile.apps),
permissions: profile.permissions,
}
}
export async function requireAuth(
request: FastifyRequest,
reply: FastifyReply,
): Promise<void> {
try {
await request.jwtVerify()
} catch {
return reply.status(401).send({
error: { code: 'UNAUTHORIZED', message: 'Требуется авторизация' },
})
}
const sub = request.user.sub
const profile = getUserProfile(request.server.db, sub)
if (!profile || profile.row.disabled) {
return reply.status(401).send({
error: { code: 'UNAUTHORIZED', message: 'Пользователь недоступен' },
})
}
request.authUser = authUserFromProfile(profile)
}
export async function requireAdmin(
request: FastifyRequest,
reply: FastifyReply,
): Promise<void> {
await requireAuth(request, reply)
if (reply.sent) return
if (!request.authUser?.isAdmin) {
return reply.status(403).send({
error: { code: 'FORBIDDEN', message: 'Только для администраторов' },
})
}
}