@iquadras/shared-guards

v0.0.2

Published

Guards compartilhados para autenticação e autorização JWT no ecossistema iQuadras (NestJS, React)

Readme

@iquadras/shared-guards

Guards compartilhados para autenticação e autorização JWT no ecossistema iQuadras.

Geral

Hierarquia de permissões

  1. superAdmin — Acesso total ao sistema (admin iQuadras)
  2. isAdmin na organização — Acesso total naquela organização (dono do centro)
  3. Módulos — Acesso apenas aos módulos em permissions[].modules

Módulos disponíveis

| Módulo | Descrição | |--------|-----------| | MODULES.BOOKING | Reserva de quadras | | MODULES.CLASSES | Aulas | | MODULES.REPLAY | Replay |

Funções de permissão (core)

  • isSuperAdmin(user) — É super admin?
  • isOrgAdmin(user, organizationId) — É admin da org ou super admin?
  • hasModuleAccess(user, organizationId, module) — Tem acesso ao módulo na org?

Header do JWT

O token deve ser enviado em x-auth-access-token.


NestJS

Instalação

npm install @iquadras/shared-guards @nestjs/jwt

Configuração

1. Defina JWT_SECRET no .env

2. Importe o módulo no AppModule:

import { SharedGuardsModule } from '@iquadras/shared-guards';

@Module({
  imports: [SharedGuardsModule.forRoot()],
})
export class AppModule {}

3. Remova o JwtModule de outros módulos — o SharedGuardsModule já registra globalmente.

4. (Opcional) Logs em main.ts:

const app = await NestFactory.create(AppModule, {
  logger: ['error', 'warn', 'log', 'debug'],
});

Uso

Rotas são públicas por padrão. Use decorators para proteger.

| Decorator | Quem tem acesso | |-----------|-----------------| | @RequireAnyAuth() | Qualquer usuário autenticado | | @RequireSuperAdminAuth() | Apenas super admins | | @RequireOrganizationAdminAuth({ organizationIdSource }) | Super admins ou admin da organização | | @RequireModuleAuth(module, { organizationIdSource }) | Super admin, admin da org ou usuário com o módulo |

OrganizationId: @RequireOrganizationAdminAuth e @RequireModuleAuth exigem organizationIdSource — informe sempre de onde virá o ID (params, query ou body):

import { RequireAnyAuth, GetUser, RequireModuleAuth, RequireOrganizationAdminAuth, RequireSuperAdminAuth, MODULES } from '@iquadras/shared-guards';

@Controller('organizations/:organizationId/reservas')
export class ReservasController {
  @Get(':id') findOne(@Param('id') id: string) { /* público */ }

  // organizationId no path (params)
  @Post() @RequireModuleAuth(MODULES.BOOKING, { organizationIdSource: 'params.organizationId' }) create(@Body() dto: CreateReservaDto) { }

  // organizationId no body
  @Post('outra-rota') @RequireModuleAuth(MODULES.BOOKING, { organizationIdSource: 'body.organizationId' }) createFromBody(@Body() dto: Dto) { }

  // organizationId na query
  @Get() @RequireOrganizationAdminAuth({ organizationIdSource: 'query.orgId' }) list(@Query() q: { orgId: string }) { }

  @Patch(':id/estornar') @RequireOrganizationAdminAuth({ organizationIdSource: 'params.organizationId' }) estornar(@Param('id') id: string) { }

  @Delete(':id') @RequireSuperAdminAuth() remove(@Param('id') id: string) { }
}

Paths comuns: params.organizationId, params.orgId, query.organizationId, query.orgId, body.organizationId, body.orgId. Pode passar array para tentar na ordem: { organizationIdSource: ['body.organizationId', 'query.orgId'] }.

Quando o organizationId é descoberto no processo (ex: buscando um recurso pelo id e obtendo a org dele), valide manualmente no controller com as funções do core:

import { isOrgAdmin, hasModuleAccess, GetUser } from '@iquadras/shared-guards';
import type { JwtPayload } from '@iquadras/shared-guards';

// No controller, após buscar o recurso:
const reserva = await this.reservasService.findOne(id);
const user = request.user as JwtPayload;
if (!isOrgAdmin(user, reserva.organizationId)) {
  throw new ForbiddenException('Sem permissão');
}

React

Instalação

npm install @iquadras/shared-guards

Uso

As funções recebem o token e fazem o decode internamente (sem validar — no client não temos o secret):

import {
  isSuperAdmin,
  isOrgAdmin,
  hasModuleAccess,
  MODULES,
} from '@iquadras/shared-guards/react';

// No componente — token do localStorage, contexto de auth, etc.
const token = getTokenFromStorage(); // sua função

const canCreate = hasModuleAccess(token, orgId, MODULES.BOOKING);
const isAdmin = isOrgAdmin(token, orgId);
const isSuper = isSuperAdmin(token);

Atenção: O decode no React não valida a assinatura. Use apenas para UI (exibir/esconder elementos). A validação real ocorre na API.


Core

Para usar apenas a lógica (sem framework) — ex: Adonis, Express, etc.:

import {
  isSuperAdmin,
  isOrgAdmin,
  hasModuleAccess,
  decodeToken,
  MODULES,
} from '@iquadras/shared-guards/core';