83 lines
2.3 KiB
TypeScript
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;
|
|
}
|
|
}
|