- Removed unused permission and app retrieval logic from the login process. - Introduced a new endpoint for reissuing access tokens to handle role changes and account switches. - Updated JWT payload handling to improve clarity and maintainability. - Enhanced the readJwtPayload function for better decoding of JWT claims.
126 lines
2.8 KiB
TypeScript
126 lines
2.8 KiB
TypeScript
import type { FastifyReply, FastifyRequest } from 'fastify'
|
|
import {
|
|
getUserApps,
|
|
getUserById,
|
|
getUserPermissions,
|
|
type UserRow,
|
|
} from '@authportal/db'
|
|
import type { AppId, MeResponse } from '@authportal/shared'
|
|
import { APP_IDS, allPermissionKeys, normalizePermissionKeys } from '@authportal/shared'
|
|
|
|
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 loadAuthUser(
|
|
request: FastifyRequest,
|
|
user: UserRow,
|
|
): AuthUser {
|
|
const apps = getUserApps(request.server.db, user.id)
|
|
let permissions = normalizePermissionKeys(
|
|
getUserPermissions(request.server.db, user.id),
|
|
)
|
|
if (user.isAdmin) {
|
|
permissions = allPermissionKeys()
|
|
}
|
|
return {
|
|
id: user.id,
|
|
email: user.email,
|
|
name: user.name,
|
|
isAdmin: user.isAdmin,
|
|
apps: asAppIds(apps),
|
|
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 row = getUserById(request.server.db, sub)
|
|
if (!row || row.disabled) {
|
|
return reply.status(401).send({
|
|
error: { code: 'UNAUTHORIZED', message: 'Пользователь недоступен' },
|
|
})
|
|
}
|
|
|
|
request.authUser = loadAuthUser(request, row)
|
|
}
|
|
|
|
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: 'Только для администраторов' },
|
|
})
|
|
}
|
|
}
|