# 🔐 Sistema de Autenticação JWT

O PlanFi BFF implementa um sistema de autenticação JWT unificado que permite o uso do mesmo token tanto no BFF quanto na API Laravel.

## 🎯 Visão Geral

### Características Principais

- **Token Único**: Mesmo JWT funciona em BFF e Laravel
- **Múltiplos Tipos**: Suporte a `users` e `clients`
- **Guards Especializados**: Controle granular de acesso
- **Decorators Avançados**: Acesso fácil aos dados do token
- **Validação Robusta**: Claims obrigatórios e verificações de segurança

## ⚙️ Configuração

### Variáveis de Ambiente

```env
# JWT Configuration (deve ser o mesmo da API Laravel)
JWT_SECRET=sua-chave-secreta-jwt-do-laravel
JWT_ALGORITHM=HS256
JWT_TTL=3600
```

> ⚠️ **CRÍTICO**: O `JWT_SECRET` deve ser **exatamente o mesmo** usado na API Laravel.

### Estrutura do Token JWT

```typescript
interface JwtPayload {
  iss: string;    // Issuer - quem emitiu o token
  iat: number;    // Issued at - quando foi emitido
  exp: number;    // Expiration time - quando expira
  nbf: number;    // Not before - não usar antes de
  sub: string;    // Subject - ID do usuário
  jti: string;    // JWT ID - ID único do token
  prv?: string;   // Provider - tipo do usuário (users/clients)
}
```

## 🛡️ Guards Disponíveis

### 1. JwtAuthGuard - Qualquer Usuário

Permite acesso a qualquer usuário autenticado (user ou client):

```typescript
import { UseGuards } from '@nestjs/common';
import { JwtAuthGuard } from './auth/guards/jwt-auth.guard';
import { User } from './auth/decorators/user.decorator';

@Controller('api')
export class ExampleController {
  @UseGuards(JwtAuthGuard)
  @Get('protected')
  getProtected(@User() user: AuthenticatedUser) {
    return { 
      message: 'Acesso autorizado', 
      userId: user.id,
      userType: user.type 
    };
  }
}
```

### 2. UserJwtGuard - Apenas Usuários

Permite acesso apenas a usuários regulares (não clients):

```typescript
import { UserJwtGuard } from './auth/guards/user-jwt.guard';

@Controller('api')
export class ExampleController {
  @UseGuards(UserJwtGuard)
  @Get('user-only')
  getUserOnly(@User() user: AuthenticatedUser) {
    // user.type será sempre 'user'
    return { message: 'Apenas usuários podem acessar' };
  }
}
```

### 3. ClientJwtGuard - Apenas Clients

Permite acesso apenas a clients:

```typescript
import { ClientJwtGuard } from './auth/guards/client-jwt.guard';

@Controller('api')
export class ExampleController {
  @UseGuards(ClientJwtGuard)
  @Get('client-only')
  getClientOnly(@User() user: AuthenticatedUser) {
    // user.type será sempre 'client'
    return { message: 'Apenas clients podem acessar' };
  }
}
```

## 🎨 Decorators Disponíveis

### @User() - Dados Completos do Usuário

```typescript
// Usuário completo
@Get('profile')
getProfile(@User() user: AuthenticatedUser) {
  return {
    id: user.id,
    type: user.type,
    payload: user.payload,
    expiresAt: user.expiresAt,
    issuedAt: user.issuedAt
  };
}

// Campo específico do usuário
@Get('user-id')
getUserId(@User('id') userId: string) {
  return { userId };
}
```

### @Token() - Token JWT Raw

```typescript
@Get('debug')
getDebug(@Token() token: string) {
  return { 
    tokenLength: token.length,
    tokenParts: token.split('.').length 
  };
}
```

### @TokenData() - Dados Extraídos do Token

```typescript
// Todos os dados extraídos
@Get('token-data')
getTokenData(@TokenData() data: Record<string, any>) {
  return { extractedData: data };
}

// Campo específico
@Get('user-email')
getUserEmail(@TokenData('email') email: string) {
  return { email };
}
```

### Decorators de Conveniência

```typescript
@Get('user-info')
getUserInfo(
  @UserUuid() uuid: string,
  @UserEmail() email: string,
  @UserName() name: string,
) {
  return { uuid, email, name };
}
```

## 🔍 Validações Implementadas

### Validações Automáticas

- ✅ **Assinatura**: Verifica se o token foi assinado com a chave correta
- ✅ **Claims Obrigatórios**: `iss`, `iat`, `exp`, `nbf`, `sub`, `jti`
- ✅ **Expiração**: Verifica se o token não expirou (`exp`)
- ✅ **Not Before**: Verifica se o token não é usado antes do tempo (`nbf`)
- ✅ **Tipo de Usuário**: Determina se é `user` ou `client` baseado no `prv`

### Provider Hash Automático

O sistema calcula automaticamente os hashes de provider baseado nos modelos Laravel:

```typescript
// Hashes calculados automaticamente
const USER_HASH = sha1('App\\Models\\User');           // users
const CLIENT_HASH = sha1('App\\Models\\User\\Client'); // clients
```

## 🚨 Tratamento de Erros

### Códigos de Erro HTTP

| Código | Descrição | Cenário |
|--------|-----------|---------|
| **401** | Unauthorized | Token inválido, expirado ou não fornecido |
| **403** | Forbidden | Token válido mas sem permissão (ex: client em rota de user) |

### Exemplos de Erros

```typescript
// Token não fornecido
throw new UnauthorizedException('Token não fornecido ou inválido');

// Token expirado
throw new UnauthorizedException('Token expirado');

// Tipo de usuário incorreto
throw new ForbiddenException('Acesso negado para este tipo de usuário');
```

## 🧪 Testando a Autenticação

### 1. Obter Token da API Laravel

```bash
curl -X POST http://localhost:8000/api/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "password": "password"
  }'
```

### 2. Usar Token no BFF

```bash
# Health check (sem auth)
curl http://localhost:3333/auth/health

# Perfil (com auth)
curl -H "Authorization: Bearer SEU_TOKEN_AQUI" \
  http://localhost:3333/auth/profile

# Debug do token
curl -H "Authorization: Bearer SEU_TOKEN_AQUI" \
  http://localhost:3333/auth/token/debug
```

### 3. Testar Diferentes Guards

```bash
# Dashboard de usuário (apenas users)
curl -H "Authorization: Bearer TOKEN_DE_USER" \
  http://localhost:3333/auth/user/dashboard

# Dashboard de client (apenas clients)
curl -H "Authorization: Bearer TOKEN_DE_CLIENT" \
  http://localhost:3333/auth/client/dashboard
```

## 🔄 Integração com Laravel

### Compatibilidade Total

O sistema é 100% compatível com Laravel JWT:

- ✅ Mesmo `JWT_SECRET`
- ✅ Mesmo algoritmo (`HS256`)
- ✅ Mesma estrutura de payload
- ✅ Mesmos provider hashes
- ✅ Mesma lógica de validação

### Exemplo de Uso Conjunto

```typescript
@Get('laravel-data')
@UseGuards(UserJwtGuard)
async getLaravelData(
  @Token() token: string,
  @User() user: AuthenticatedUser
) {
  // 1. Dados do BFF (extraídos do token)
  const bffData = {
    userId: user.id,
    userType: user.type
  };

  // 2. Requisição para Laravel com o mesmo token
  const laravelResponse = await this.httpService.get(
    'http://localhost:8000/api/user/profile',
    {
      headers: { Authorization: `Bearer ${token}` }
    }
  );

  // 3. Combinar dados de ambas as fontes
  return {
    bff: bffData,
    laravel: laravelResponse.data
  };
}
```

## 🔧 Configuração Avançada

### Customizar Validação

```typescript
// jwt.strategy.ts
async validate(payload: JwtPayload): Promise<AuthenticatedUser> {
  // Validações customizadas
  if (!payload.sub) {
    throw new UnauthorizedException('Subject não encontrado no token');
  }

  // Lógica customizada de determinação de tipo
  const userType = this.determineUserType(payload.prv);
  
  return {
    id: payload.sub,
    type: userType,
    payload,
    expiresAt: new Date(payload.exp * 1000),
    issuedAt: new Date(payload.iat * 1000),
    tokenData: this.extractTokenData(payload),
    rawToken: null // Será preenchido pelo guard
  };
}
```

### Adicionar Novos Decorators

```typescript
// custom.decorator.ts
export const UserRole = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    const user = request.user as AuthenticatedUser;
    return user.tokenData?.role;
  },
);

// Uso
@Get('admin-only')
checkRole(@UserRole() role: string) {
  if (role !== 'admin') {
    throw new ForbiddenException('Apenas admins');
  }
  return { message: 'Área restrita' };
}
```

## 🔐 Melhores Práticas

### Segurança

1. **Mantenha o JWT_SECRET seguro** e sincronizado entre sistemas
2. **Use HTTPS em produção** para proteger tokens em trânsito
3. **Configure TTL adequado** para balancear segurança e UX
4. **Monitore tokens inválidos** para detectar ataques
5. **Implemente blacklist** se necessário (tokens invalidados)

### Performance

1. **Cache dados do usuário** quando possível
2. **Evite validações desnecessárias** em cada requisição
3. **Use guards específicos** (UserJwtGuard vs JwtAuthGuard)
4. **Extraia apenas dados necessários** do token

### Desenvolvimento

1. **Use endpoints de debug** para troubleshooting
2. **Mantenha logs detalhados** para auditoria
3. **Teste com diferentes tipos de token** (user/client)
4. **Documente novos decorators** e guards customizados

---

## 📚 Referências

- [NestJS Authentication](https://docs.nestjs.com/security/authentication)
- [Passport JWT Strategy](http://www.passportjs.org/packages/passport-jwt/)
- [Laravel JWT](https://jwt-auth.readthedocs.io/)
- [JWT.io Debugger](https://jwt.io/)
