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 { const isPublic = this.reflector.getAllAndOverride(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(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'); } } }