51 lines
1.8 KiB
TypeScript
51 lines
1.8 KiB
TypeScript
import {
|
|
CanActivate,
|
|
ExecutionContext,
|
|
Injectable,
|
|
} from '@nestjs/common';
|
|
import { JwtService } from '@nestjs/jwt';
|
|
import { CLIENT_APP_ACTOR_MAP, ClientApp } from '@dukang/shared-types';
|
|
import type { AuthUser } from './jwt-auth.guard';
|
|
|
|
/** C 端 H5 / 小程序同属 USER,目录白名单只认 actorId→手机号,允许端间 token 兼容 */
|
|
const USER_CLIENT_APPS = new Set<ClientApp>([ClientApp.USER_MINI, ClientApp.USER_H5]);
|
|
|
|
function clientAppCompatible(headerApp: ClientApp | undefined, payloadApp: unknown): boolean {
|
|
if (!headerApp || payloadApp == null) return false;
|
|
if (headerApp === payloadApp) return true;
|
|
return USER_CLIENT_APPS.has(headerApp) && USER_CLIENT_APPS.has(payloadApp as ClientApp);
|
|
}
|
|
|
|
@Injectable()
|
|
export class OptionalJwtAuthGuard implements CanActivate {
|
|
constructor(private readonly jwtService: JwtService) {}
|
|
|
|
canActivate(context: ExecutionContext): boolean {
|
|
const req = context.switchToHttp().getRequest();
|
|
const auth = req.headers.authorization as string | undefined;
|
|
if (!auth?.startsWith('Bearer ')) {
|
|
return true;
|
|
}
|
|
try {
|
|
const payload = this.jwtService.verify(auth.slice(7));
|
|
const clientApp = req.headers['x-client-app'] as ClientApp;
|
|
if (!clientAppCompatible(clientApp, payload.clientApp)) return true;
|
|
const expectedActor = CLIENT_APP_ACTOR_MAP[clientApp];
|
|
if (payload.actorType !== expectedActor) return true;
|
|
req.user = {
|
|
actorType: payload.actorType,
|
|
actorId: BigInt(payload.actorId),
|
|
clientApp,
|
|
sub: payload.sub,
|
|
phoneVerified: !!payload.phoneVerified,
|
|
...(payload.storeId != null && payload.storeId !== ''
|
|
? { storeId: BigInt(payload.storeId) }
|
|
: {}),
|
|
} satisfies AuthUser;
|
|
} catch {
|
|
/* ignore invalid token */
|
|
}
|
|
return true;
|
|
}
|
|
}
|