Files
course-craft-service/apps/api/src/auth/guards/jwt-auth.guard.ts
2026-02-06 02:17:59 +03:00

84 lines
2.5 KiB
TypeScript

import {
Injectable,
CanActivate,
ExecutionContext,
UnauthorizedException,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { JwtService } from '@nestjs/jwt';
import { AuthService } from '../auth.service';
import { SupabaseService } from '../supabase.service';
import { UsersService } from '../../users/users.service';
import { IS_PUBLIC_KEY } from '../decorators/public.decorator';
import { JwtPayload } from '../auth.service';
@Injectable()
export class JwtAuthGuard implements CanActivate {
constructor(
private reflector: Reflector,
private jwtService: JwtService,
private authService: AuthService,
private supabaseService: SupabaseService,
private usersService: UsersService
) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
context.getHandler(),
context.getClass(),
]);
if (isPublic) {
return true;
}
const request = context.switchToHttp().getRequest();
const authHeader = request.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
throw new UnauthorizedException('Missing or invalid authorization header');
}
const token = authHeader.replace('Bearer ', '');
try {
// 1) Try our own JWT (from POST /auth/exchange)
try {
const payload = this.jwtService.verify<JwtPayload>(token);
const user = await this.authService.validateJwtPayload(payload);
if (user) {
request.user = user;
return true;
}
} catch {
// Not our JWT or expired — try Supabase below
}
// 2) Fallback: Supabase access_token (for backward compatibility)
const supabaseUser = await this.supabaseService.verifyToken(token);
if (!supabaseUser) {
throw new UnauthorizedException('Invalid token');
}
let user = await this.usersService.findBySupabaseId(supabaseUser.id);
if (!user) {
user = await this.usersService.create({
supabaseId: supabaseUser.id,
email: supabaseUser.email!,
name:
supabaseUser.user_metadata?.full_name ||
supabaseUser.user_metadata?.name ||
null,
avatarUrl: supabaseUser.user_metadata?.avatar_url || null,
});
}
request.user = user;
return true;
} catch (error) {
if (error instanceof UnauthorizedException) throw error;
throw new UnauthorizedException('Token validation failed');
}
}
}