tyt-api-nest/src/auth/family-access/family-access.guard.ts

83 lines
2.3 KiB
TypeScript

import {
CanActivate,
ExecutionContext,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import jwt from 'jsonwebtoken';
import type { FamilyActorContext } from '../../common/family-actor-context.js';
import { MESSAGES } from '../../common/messages.js';
import { PrismaService } from '../../prisma.service.js';
/**
* C 端家属小程序登录守卫。
*/
@Injectable()
export class FamilyAccessTokenGuard implements CanActivate {
constructor(private readonly prisma: PrismaService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest<{
headers: Record<string, string | string[] | undefined>;
familyActor?: FamilyActorContext;
}>();
const authorization = request.headers.authorization;
const headerValue = Array.isArray(authorization)
? authorization[0]
: authorization;
if (!headerValue || !headerValue.startsWith('Bearer ')) {
throw new UnauthorizedException(MESSAGES.AUTH.MISSING_BEARER);
}
request.familyActor = await this.verifyAndExtractActor(
headerValue.slice('Bearer '.length).trim(),
);
return true;
}
/**
* 校验家属 token 并回库确认账号仍存在。
*/
private async verifyAndExtractActor(token: string): Promise<FamilyActorContext> {
const secret = process.env.AUTH_TOKEN_SECRET;
if (!secret) {
throw new UnauthorizedException(MESSAGES.AUTH.TOKEN_SECRET_MISSING);
}
let payload: string | jwt.JwtPayload;
try {
payload = jwt.verify(token, secret, {
algorithms: ['HS256'],
issuer: 'tyt-api-nest-family',
});
} catch {
throw new UnauthorizedException(MESSAGES.AUTH.TOKEN_INVALID);
}
if (
typeof payload !== 'object' ||
payload.type !== 'FAMILY_MINIAPP' ||
typeof payload.id !== 'number' ||
!Number.isInteger(payload.id)
) {
throw new UnauthorizedException(MESSAGES.AUTH.TOKEN_PAYLOAD_INVALID);
}
const account = await this.prisma.familyMiniAppAccount.findUnique({
where: { id: payload.id },
select: {
id: true,
phone: true,
openId: true,
serviceUid: true,
},
});
if (!account) {
throw new UnauthorizedException(MESSAGES.AUTH.FAMILY_ACCOUNT_NOT_FOUND);
}
return account;
}
}