3 Commits

Author SHA1 Message Date
311cadc1d1 Merge pull request 'feature/login-jwt' (#1) from feature/login-jwt into main
Reviewed-on: #1
2025-10-22 11:16:28 +00:00
Claudecio Martins
ec216528b5 feat(auth): implementar rotas e serviços de autenticação
- Adicionadas rotas de login, logout e seleção de contexto de trabalho em AuthRoutes.php.
- AuthService aprimorado com login detalhado, validação de token, listagem de contexto de trabalho e métodos de seleção.
- JWTService introduzido para lidar com a geração e validação de JWT.
- Migração de banco de dados atualizada para incluir dados de amostra da empresa.
- Arquivo de migração RBAC criado para futura implementação de controle de acesso baseado em função.
2025-10-22 13:15:43 +02:00
Claudecio Martins
75047e8b34 corrige caminho da pasta de logs no .gitignore 2025-10-21 16:55:03 +02:00
9 changed files with 1099 additions and 13 deletions

2
.gitignore vendored
View File

@@ -1,4 +1,4 @@
# Arquivo de variáveis de ambiente
.env
# Pasta de Logs
app/Storage/logs
storage/logs/

View File

@@ -1,12 +1,25 @@
<?php
namespace Workbloom\Component\Auth\v0\Controllers;
use Workbloom\Helpers\DataSanitizer;
use Workbloom\Services\JWTService;
use AxiumPHP\Helpers\RequestHelper;
use Workbloom\Helpers\DataSanitizer;
use Workbloom\Component\Auth\v0\Services\AuthService;
class AuthController {
public function login() {
/**
* Lida com a requisição de login de um usuário via POST.
*
* Este método atua como um **controlador** de API, processando as credenciais de login, realizando a validação de presença de campo e delegando a lógica complexa de autenticação, revogação/registro de token para a camada de serviço.
*
* #### Fluxo de Operação:
* 1. **Coleta e Validação de Presença:** Obtém os dados do formulário. Verifica se os campos **'login'** e **'password'** estão presentes. Se algum estiver ausente, envia um erro **400 Bad Request** e encerra a execução.
* 2. **Sanitização e Delegação:** Os dados são sanitizados e encapsulados em um array de credenciais. O método delega a lógica de autenticação (busca de usuário, `password_verify`, revogação de token antigo e registro de novo token) para o **`AuthService::login()`**.
* 3. **Envio da Resposta JSON:** O resultado retornado pelo serviço é formatado e enviado de volta ao cliente, comunicando o código HTTP, status e mensagem da operação.
*
* @return void Este método não retorna um valor, pois seu objetivo é enviar uma resposta JSON e encerrar a execução do script.
*/
public function login(): void {
// Recebe formulário de login
$form = RequestHelper::getFilteredInput(form_type: INPUT_POST);
@@ -40,12 +53,117 @@
// Resposta JSON
RequestHelper::sendJsonResponse(
response_code: 200,
status: 'success',
message: 'Login successful',
output: [
'data' => $form
]
response_code: $loginService['response_code'] ?? 500,
status: $loginService['status'] ?? 'error',
message: $loginService['message'] ?? 'Internal server error',
output: $loginService['output'] ?? []
);
}
/**
* Lida com a requisição para listar os contextos de trabalho (empresas) disponíveis para o usuário autenticado.
*
* Este método atua como um **controlador** de API, extraindo o UUID e o status de root do usuário a partir do token JWT e delegando a lógica de busca de contextos para o `AuthService`.
*
* #### Fluxo de Operação:
* 1. **Obtenção e Decodificação do Token:** O token Bearer é obtido e decodificado via `JWTService`. Os dados essenciais do usuário, como `uuid` e `is_root`, são extraídos do payload.
* 2. **Delegação ao Serviço:** O método `listWorkContexts` do **`AuthService`** é chamado, passando o UUID do usuário e seu status de root. O serviço é responsável por consultar os contextos de trabalho (todas as empresas para usuários root ou apenas as associadas para usuários normais).
* 3. **Envio da Resposta JSON:** O resultado retornado pelo serviço (`workContexts`) é formatado para uma resposta JSON padronizada usando **`RequestHelper::sendJsonResponse`**. O código HTTP, status, mensagem e os dados dos contextos são comunicados ao cliente.
*
* @return void Este método não retorna um valor, pois seu objetivo é enviar uma resposta JSON e encerrar a execução do script.
*/
public function listWorkContexts(): void {
// Receve o token Bearer da requisição
$bearerToken = JWTService::decode(token: JWTService::getBearerToken());
// Consulta os contextos de trabalho associados ao token
$workContexts = (new AuthService())->listWorkContexts(usuario_uuid: $bearerToken['user_data']['uuid'], is_root: $bearerToken['user_data']['is_root']);
// Resposta JSON
RequestHelper::sendJsonResponse(
response_code: $workContexts['response_code'] ?? 500,
status: $workContexts['status'] ?? 'error',
message: $workContexts['message'] ?? 'Internal server error',
output: $workContexts['output'] ?? []
);
}
/**
* Lida com a requisição de seleção do contexto de trabalho (empresa) de um usuário.
*
* Este método atua como um **controlador** de API, responsável por:
* 1. **Extrair a Identidade:** Obtém e decodifica o token Bearer para identificar o usuário.
* 2. **Validação de Presença:** Verifica se o campo obrigatório `contexto_uuid` foi fornecido. Se estiver ausente, envia um erro **400 Bad Request** e encerra a execução.
* 3. **Delegação ao Serviço:** Delega a validação de acesso ao contexto, a revogação do token antigo e a geração do novo token atualizado (que inclui o contexto de trabalho) para o **`AuthService::selectWorkContext()`**.
* 4. **Envio da Resposta JSON:** O resultado retornado pelo serviço é formatado e enviado de volta ao cliente, comunicando o código HTTP, status, mensagem e o novo token JWT.
*
* @return void Este método não retorna um valor, pois seu objetivo é enviar uma resposta JSON e encerrar a execução do script.
*/
public function selectWorkContext(): void {
// Receve o token Bearer da requisição
$bearerToken = JWTService::decode(token: JWTService::getBearerToken());
// Recebe formulário de seleção de contexto
$form = RequestHelper::getFilteredInput(form_type: INPUT_POST);
// Campos Obrigatórios
$required_fields = [
'contexto_uuid'
];
// Validação dos campos obrigatórios
foreach ($required_fields as $field) {
if (empty($form[$field])) {
// Resposta JSON de erro
RequestHelper::sendJsonResponse(
response_code: 400,
status: 'error',
message: "O campo '{$field}' é obrigatório",
output: []
);
return;
}
}
// Chama o serviço de seleção de contexto
$workContexts = (new AuthService())->selectWorkContext(
usuario_uuid: $bearerToken['user_data']['uuid'],
contexto_uuid: DataSanitizer::string(value: $form['contexto_uuid'] ?? '')
);
// Resposta JSON
RequestHelper::sendJsonResponse(
response_code: $workContexts['response_code'] ?? 500,
status: $workContexts['status'] ?? 'error',
message: $workContexts['message'] ?? 'Internal server error',
output: $workContexts['output'] ?? []
);
}
/**
* Lida com a requisição de logout, revogando o token de autenticação do usuário.
*
* Este método atua como um **controlador** de API, responsável por extrair o token JWT do cabeçalho da requisição, delegar a lógica de revogação para o serviço e formatar a resposta JSON final.
*
* #### Fluxo de Operação:
* 1. **Obtenção do Token:** O token Bearer é extraído do cabeçalho da requisição.
* 2. **Delegação ao Serviço:** O método `logout` do **`AuthService`** é chamado com o token. O serviço é responsável por revogar o token no banco de dados e gerenciar a transação.
* 3. **Envio da Resposta JSON:** O resultado retornado pelo serviço (`logoutService`) é formatado para uma resposta JSON padronizada usando **`RequestHelper::sendJsonResponse`**. O código HTTP, status, mensagem e quaisquer dados de saída são comunicados ao cliente.
*
* @return void Este método não retorna um valor, pois seu objetivo é enviar uma resposta JSON e encerrar a execução do script.
*/
public function logout(): void {
// Chama o serviço de logout
$logoutService = (new AuthService())->logout(
token: JWTService::getBearerToken()
);
// Resposta JSON
RequestHelper::sendJsonResponse(
response_code: $logoutService['response_code'] ?? 500,
status: $logoutService['status'] ?? 'error',
message: $logoutService['message'] ?? 'Internal server error',
output: $logoutService['output'] ?? []
);
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace Workbloom\Component\Auth\v0\Middlewares;
use Exception;
use Workbloom\Services\JWTService;
use Workbloom\Component\Auth\v0\Services\AuthService;
class AuthMiddleware {
/**
* Gerencia o processo de autenticação de uma requisição de API via token Bearer.
*
* Este método estático atua como um **middleware de autenticação**, verificando a presença e a validade de um token JWT em uma requisição HTTP, incluindo checagens de revogação e expiração no banco de dados.
*
* #### Fluxo de Operação:
* 1. **Obtenção e Decodificação do Token:** Tenta extrair o token Bearer do cabeçalho `Authorization` e decodificá-lo. Se o token estiver ausente ou a decodificação inicial falhar, retorna `401 Unauthorized`.
* 2. **Validação de Segurança e Status:** Chama o método `validateAuthToken` do **`AuthService`**. Este serviço realiza as verificações críticas no banco de dados (revogação, expiração por data) e no dispositivo (IP/User Agent).
* 3. **Gerenciamento da Resposta:**
* * Se a validação do serviço for **bem-sucedida** (`status === 'success'`), o método retorna `true`, permitindo que o processamento da rota continue.
* * Se a validação do serviço **falhar** (por token expirado, revogado ou dispositivo inválido), o método retorna o array de erro formatado pelo serviço, com o código HTTP e a mensagem correspondente.
*
* @return array|bool Retorna `true` se o token for válido e o acesso for autorizado; caso contrário, retorna um array de erro formatado contendo o código HTTP e a mensagem da falha.
*/
public static function handle(): array|bool {
// Recebe o token decodificado do cabeçalho Authorization
$bearerToken = JWTService::getBearerToken();
try {
// Verifica se o token está presente
if (!$bearerToken) {
throw new Exception(message: "Token not provided", code: 401);
}
$decodedToken = JWTService::decode(token: $bearerToken);
if(empty($decodedToken)) {
return [
'response_code' => 401,
'status' => 'error',
'message' => 'Unauthorized: Invalid or missing token.'
];
}
// Chama serviço de login e valida token e permissões
$authService = (new AuthService())->validateAuthToken(token: $bearerToken);
// Verifica se a validação foi bem-sucedida
if($authService['status'] !== 'success') {
return [
'response_code' => $authService['response_code'] ?? 500,
'status' => $authService['status'] ?? 'error',
'message' => $authService['message'] ?? 'Internal server error',
'output' => $authService['output'] ?? []
];
}
return true;
} catch(Exception $e) {
return [
'response_code' => $e->getCode(),
'status' => 'error',
'message' => $e->getMessage()
];
}
}
}

View File

@@ -8,8 +8,10 @@
use Workbloom\Component\Auth\v0\DTO\UsuarioTokenDTO;
class UsuarioModel {
protected string $empresaTable = 'empresa';
protected string $usuarioTable = 'usuario';
protected string $usuarioTokenTable = 'usuario_token';
protected string $usuarioEmpresaTable = 'usuario_empresa';
/**
* Armazena um novo usuário no banco de dados.
@@ -475,7 +477,7 @@
* @param string $usuario_uuid O UUID do usuário para o qual os tokens serão revogados.
* @return array Um array associativo contendo o status da operação ("success" ou "error"), um código de resposta HTTP e uma mensagem.
*/
public function revolkOldUsuarioTokens(string $usuario_uuid): array {
public function revokeOldUsuarioTokens(string $usuario_uuid): array {
try {
$sql =
"UPDATE {$this->usuarioTokenTable} AS ut SET
@@ -507,4 +509,290 @@
];
}
}
/**
* Busca um registro de token de usuário no banco de dados por um identificador específico.
*
* Este método recupera todos os detalhes de um token de autenticação (JWT) da tabela `{$this->usuarioTokenTable}` de forma flexível, permitindo a busca por qualquer coluna (`$identifier`) e o valor correspondente.
*
* #### Fluxo de Operação:
* 1. **Execução da Query:** Monta e executa uma consulta `SELECT` que usa o `$identifier` de forma dinâmica na cláusula `WHERE` e um parâmetro nomeado seguro (`:value`) para o valor. A consulta utiliza `DB::fetchOne`, indicando que apenas um registro é esperado.
* 2. **Verificação de Resultados:**
* - Se o token **não for encontrado** (`empty($result)`), retorna um array com **`status` 'error'** e código **404 Not Found**.
* - Se o token for encontrado, retorna um array com **`status` 'success'**, código **200 OK** e os dados do registro na chave `output`.
*
* #### Tratamento de Erros:
* - Qualquer `Exception` capturada durante o processo retorna um array com **`status` 'error'**, o código de erro da exceção (ou `500` como fallback) e uma mensagem detalhada de erro.
*
* @param string $identifier A coluna da tabela a ser usada como critério de busca (ex: 'token', 'usuario_id', 'ip_address').
* @param mixed $value O valor correspondente ao identificador que está sendo buscado.
* @return array Um array associativo contendo o status da operação, um código de resposta HTTP e os dados do token, se encontrado.
*/
public function findUsuarioTokenByIdentifier(string $identifier, mixed $value): array {
try {
$sql =
"SELECT
id,
usuario_id,
usuario_ip,
usuario_user_agent,
token,
is_revoked,
created_at,
expires_at,
revoked_at
FROM {$this->usuarioTokenTable}
WHERE {$identifier} = :value";
$params = [
":value" => $value,
];
$result = DB::fetchOne(sql: $sql, params: $params);
if(empty($result)) {
return [
'response_code' => 404,
'status' => 'error',
'message' => 'Token de usuário não encontrado',
];
}
return [
'response_code' => 200,
'status' => 'success',
'message' => 'Token de usuário encontrado com sucesso',
'output' => [
'data' => $result
]
];
} catch(Exception $e) {
return [
'response_code' => $e->getCode() ?? 500,
'status' => 'error',
'message' => $e->getMessage() ?? 'Internal Server Error'
];
}
}
/**
* Busca e retorna todos os contextos de trabalho (empresas) associados a um usuário, identificando o usuário pelo seu UUID.
*
* Este método consulta a tabela de associação usuário-empresa para listar todas as empresas ativas (`status_id = 1`) às quais um usuário específico tem acesso.
*
* #### Fluxo de Operação:
* 1. **Execução da Query:** Executa uma consulta `SELECT` que une as tabelas de usuário, empresa e a tabela de associação (`{$this->usuarioEmpresaTable}`) para filtrar pelo `usuario_uuid`. A busca retorna múltiplos registros (empresas).
* 2. **Verificação de Resultados:**
* - Se **nenhum contexto de trabalho for encontrado** (`empty($result)`), retorna um array com **`status` 'error'** e código **404 Not Found**.
* - Se contextos forem encontrados, retorna um array com **`status` 'success'**, código **200 OK** e os dados das empresas encontradas na chave `output`.
*
* #### Tratamento de Erros:
* - Qualquer `Exception` capturada durante o processo retorna um array com **`status` 'error'**, o código de erro da exceção (ou `500` como fallback) e uma mensagem detalhada de erro.
*
* @param string $usuario_uuid O UUID do usuário para o qual os contextos de trabalho serão buscados.
* @return array Um array associativo contendo o status da operação, o código de resposta HTTP e os dados dos contextos de trabalho, se encontrados.
*/
public function findWorkContextsByUsuarioUUID(string $usuario_uuid): array {
try {
$sql =
"SELECT
'Empresa' AS contexto_tipo,
e.uuid AS contexto_uuid,
e.razao_social,
e.nome_fantasia,
e.document_inscricao
FROM {$this->usuarioEmpresaTable} ue
LEFT JOIN {$this->usuarioTable} u ON ue.usuario_id = u.id
LEFT JOIN {$this->empresaTable} e ON ue.empresa_id = e.id
WHERE u.uuid = :usuario_uuid
AND e.status_id = :status_id";
$params = [
":usuario_uuid"=> $usuario_uuid,
":status_id" => 1
];
// Executa a query
$result = DB::fetchAll(sql: $sql, params: $params);
if(empty($result)) {
return [
'response_code' => 404,
'status' => 'error',
'message' => 'Contextos de trabalho não encontrados para o usuário',
];
}
return [
'response_code' => 200,
'status' => 'success',
'message' => 'Contextos de trabalho encontrados com sucesso',
'output' => [
'data' => ['contextos' => $result]
]
];
} catch(Exception $e) {
return [
'response_code' => $e->getCode() ?? 500,
'status' => 'error',
'message' => $e->getMessage() ?? 'Internal Server Error'
];
}
}
/**
* Busca e retorna todos os contextos de trabalho (empresas) ativos no sistema.
*
* Este método consulta a tabela `{$this->empresaTable}` para listar todas as empresas que estão marcadas como ativas (`status_id = 1`). É útil para a administração do sistema que precisa listar todas as entidades disponíveis.
*
* #### Fluxo de Operação:
* 1. **Execução da Query:** Executa uma consulta `SELECT` para buscar as informações essenciais de todas as empresas com `status_id = 1`.
* 2. **Verificação de Resultados:**
* * Se **nenhum contexto de trabalho for encontrado** (`empty($result)`), retorna um array com **`status` 'error'** e código **404 Not Found**, com uma mensagem indicando a ausência de contextos.
* * Se contextos forem encontrados, retorna um array com **`status` 'success'**, código **200 OK** e os dados das empresas na chave `output`.
*
* #### Tratamento de Erros:
* - Qualquer `Exception` capturada durante o processo retorna um array com **`status` 'error'**, o código de erro da exceção (ou `500` como fallback) e uma mensagem detalhada de erro.
*
* @return array Um array associativo contendo o status da operação, um código de resposta HTTP e os dados dos contextos de trabalho, se encontrados.
*/
public function findAllWorkContexts(int $is_root = 0): array {
try {
$sql =
"SELECT
'Empresa' AS contexto_tipo,
e.uuid AS contexto_uuid,
e.razao_social,
e.nome_fantasia,
e.document_inscricao
FROM {$this->empresaTable} e
WHERE e.status_id = :status_id";
$params = [
":status_id" => 1
];
// Executa a query
$result = DB::fetchAll(sql: $sql, params: $params);
if(empty($result)) {
return [
'response_code' => 404,
'status' => 'error',
'message' => 'Contextos de trabalho não encontrados para o usuário',
];
}
// Adiciona o contexto da plataforma se o usuário for Administrador
if($is_root === 1) {
$adminContext = [
'contexto_tipo' => 'Sistema',
'contexto_uuid' => '019a07ee-0c7c-7dd7-a447-802ede64dc98',
'nome' => 'Área do Administrador'
];
$result = array_merge([$adminContext], $result);
}
return [
'response_code' => 200,
'status' => 'success',
'message' => 'Contextos de trabalho encontrados com sucesso',
'output' => [
'data' => ['contextos' => $result]
]
];
} catch(Exception $e) {
return [
'response_code' => $e->getCode() ?? 500,
'status' => 'error',
'message' => $e->getMessage() ?? 'Internal Server Error'
];
}
}
/**
* Busca um contexto de trabalho (empresa) específico e verifica se o usuário tem permissão para acessá-lo.
*
* Este método é crucial para a validação do contexto de trabalho de um usuário (por exemplo, ao iniciar uma sessão em uma empresa). Ele usa uma lógica de consulta SQL para determinar se o `usuario_uuid` está associado à `contexto_uuid` fornecida ou se o usuário é um administrador de sistema (root).
*
* #### Fluxo de Permissão Lógica:
* A cláusula `WHERE` verifica DUAS condições de permissão:
* 1. **Associação Direta/Normal (EXISTS):** O usuário está diretamente associado à empresa (`ue.usuario_id = u.id AND ue.empresa_id = e.id`).
* 2. **Permissão de Root (OR EXISTS):** O usuário possui a flag `is_root = 1`.
*
* #### Fluxo de Operação:
* 1. **Execução da Query:** Executa uma consulta `SELECT` complexa que filtra as empresas por `contexto_uuid` e `status_id = 1`, além de verificar a permissão do usuário via `EXISTS`.
* 2. **Verificação de Resultados:**
* * Se o contexto for encontrado e o usuário tiver permissão, retorna um array com **`status` 'success'** e código **200 OK**, com os dados do contexto.
* * Se o contexto **não for encontrado** ou o usuário **não tiver permissão** (o que resulta em `empty($result)` devido ao `WHERE`), retorna um array com **`status` 'error'** e código **404 Not Found**.
*
* #### Tratamento de Erros:
* - Qualquer `Exception` capturada durante o processo retorna um array com **`status` 'error'**, o código de erro da exceção (ou `500` como fallback) e uma mensagem detalhada de erro.
*
* @param string $usuario_uuid O UUID do usuário cuja permissão de acesso está sendo verificada.
* @param string $contexto_uuid O UUID da empresa/contexto de trabalho que o usuário está tentando acessar.
* @return array Um array associativo contendo o status da operação, um código de resposta HTTP e os dados do contexto, se encontrado.
*/
public function findWorkContextByUsuarioAndContextoUUID(string $usuario_uuid, string $contexto_uuid): array {
try {
$sql =
"SELECT
'Empresa' AS contexto_tipo,
e.uuid AS contexto_uuid,
e.razao_social,
e.nome_fantasia,
e.document_inscricao
FROM {$this->empresaTable} e
WHERE
(
EXISTS (
SELECT 1
FROM {$this->usuarioEmpresaTable} ue
INNER JOIN {$this->usuarioTable} u
ON ue.usuario_id = u.id
WHERE
ue.empresa_id = e.id
AND u.uuid = :usuario_uuid
)
OR EXISTS (
SELECT 1
FROM {$this->usuarioTable} u2
WHERE
u2.uuid = :usuario_uuid
AND u2.is_root = 1
)
)
AND e.uuid = :contexto_uuid
AND e.status_id = :status_id";
$params = [
":usuario_uuid"=> $usuario_uuid,
":contexto_uuid"=> $contexto_uuid,
":status_id" => 1
];
// Executa a query
$result = DB::fetchOne(sql: $sql, params: $params);
if(empty($result)) {
return [
'response_code' => 404,
'status' => 'error',
'message' => 'Contexto de trabalho não encontrado para o usuário',
];
}
return [
'response_code' => 200,
'status' => 'success',
'message' => 'Contexto de trabalho encontrado com sucesso',
'output' => [
'data' => $result
]
];
} catch(Exception $e) {
return [
'response_code' => $e->getCode() ?? 500,
'status' => 'error',
'message' => $e->getMessage() ?? 'Internal Server Error'
];
}
}
}

View File

@@ -1,9 +1,37 @@
<?php
use AxiumPHP\Core\Router;
use Workbloom\Component\Auth\v0\Middlewares\AuthMiddleware;
use Workbloom\Component\Auth\v0\Controllers\AuthController;
// Rota de login
Router::POST(
uri: '/login',
handler: [AuthController::class, 'login']
);
);
// Rotas Logadas
Router::group(
prefix: '',
callback: function() {
// Lista Contextos de Trabalho
Router::GET(
uri: '/work-contexts',
handler: [AuthController::class, 'listWorkContexts']
);
// Seleção do contexto de trabalho
Router::POST(
uri: '/select-work-context',
handler: [AuthController::class, 'selectWorkContext']
);
// Logout
Router::POST(
uri: '/logout',
handler: [AuthController::class, 'logout']
);
},
middlewares: [
[AuthMiddleware::class, 'handle']
]
);

View File

@@ -1,8 +1,413 @@
<?php
namespace Workbloom\Component\Auth\v0\Services;
use DateTime;
use Exception;
use Ramsey\Uuid\Uuid;
use Workbloom\Services\DB;
use Workbloom\Services\JWTService;
use Workbloom\Component\Auth\v0\DTO\UsuarioTokenDTO;
use Workbloom\Component\Auth\v0\Models\UsuarioModel;
class AuthService {
public function login(array $credentials) {
return ['data' => $credentials];
/**
* Realiza a autenticação de um usuário, verificando as credenciais, revogando sessões antigas e gerando um novo token JWT.
*
* Este método gerencia todo o fluxo de login e a criação da sessão de autenticação. Ele garante a atomicidade das operações de banco de dados (`revogação` e `registro do token`) utilizando transações.
*
* #### Fluxo de Operação:
* 1. **Início da Transação:** Inicia uma transação no banco de dados (`DB::beginTransaction()`).
* 2. **Verificação de Credenciais:** Busca o usuário pelo login (e-mail ou nome de usuário) e verifica a senha fornecida contra o hash armazenado. Se falhar, lança uma `Exception` (código 401).
* 3. **Verificação de Status:** Confere se o usuário está ativo (`status_id = 1`) e não excluído. Se não, lança uma `Exception` (código 403).
* 4. **Geração do JWT:** Monta o payload do token com os dados essenciais do usuário e gera o novo token JWT.
* 5. **Revogação de Tokens Antigos:** Revoga todos os tokens ativos anteriores do usuário no banco de dados. Se a revogação falhar, a transação é revertida, e uma `Exception` é lançada.
* 6. **Registro do Novo Token:** Armazena o novo token JWT e metadados de sessão (IP, User Agent, expiração) no banco de dados. Se o registro falhar, a transação é revertida, e uma `Exception` é lançada.
* 7. **Confirmação e Retorno:** Se todas as etapas forem bem-sucedidas, a transação é confirmada (`DB::commit()`), e o token gerado é retornado com `status` 'success' e código `200 OK`.
*
* #### Tratamento de Erros:
* - Qualquer `Exception` capturada reverte a transação e retorna um array de erro formatado.
*
* @param array $credentials Um array associativo contendo as credenciais do usuário, com as chaves 'login' e 'password'.
* @return array Um array associativo contendo o status da operação, o código de resposta HTTP e o token JWT em caso de sucesso.
*/
public function login(array $credentials): array {
try {
// Consulta o usuario com base no login
$usuarioLogin = (new UsuarioModel())->findUsuarioByLogin(login: $credentials['login']);
if($usuarioLogin['status'] !== 'success') {
throw new Exception(message: 'Usuário não encontrado', code: 404);
}
$usuarioData = $usuarioLogin['output']['data'];
// Verifica a senha
if(!password_verify(password: $credentials['password'], hash: $usuarioData['senha_hash'])) {
throw new Exception(message: 'Senha inválida', code: 401);
}
// Verifica se o usuário está ativo
if(($usuarioData['status_id'] != 1) || ($usuarioData['deleted_at'] !== null)) {
throw new Exception(message: 'Usuário inativo ou excluído', code: 403);
}
// Monta o payload do token
$payload['user_data'] = [
'uuid' => $usuarioData['uuid'],
'nome_completo' => $usuarioData['nome_completo'],
'nome_usuario' => $usuarioData['nome_usuario'],
'is_root' => $usuarioData['is_root']
];
// Gera o token JWT
$generateJWT = JWTService::generateToken(payload: $payload);
// Inicia Transação no Banco de Dados
DB::beginTransaction();
// Revoga tokens de sessões anteriores
$revokeOldTokens = (new UsuarioModel())->revokeOldUsuarioTokens(usuario_uuid: $usuarioData['uuid']);
if($revokeOldTokens['status'] !== 'success') {
DB::rollBack();
throw new Exception(message: 'Erro ao revogar tokens antigos', code: 500);
}
// Registra o token da sessão
$registerToken = (new UsuarioModel())->storeUsuarioToken(
usuarioTokenDTO: new UsuarioTokenDTO(data: [
'usuario_id' => $usuarioData['id'],
'usuario_ip' => $_SERVER['REMOTE_ADDR'] ?? null,
'usuario_user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? null,
'token' => $generateJWT['token'],
'is_revoked' => 0,
'expires_at' => $generateJWT['expires_at']
])
);
if($registerToken['status'] !== 'success') {
DB::rollBack();
throw new Exception(message: 'Erro ao registrar token de sessão', code: 500);
}
DB::commit();
return [
'response_code' => 200,
'status' => 'success',
'message' => 'Login realizado com sucesso',
'output' => [
'data' => $generateJWT
]
];
} catch(Exception $e) {
return [
'response_code' => $e->getCode() ?? 500,
'status' => 'error',
'message' => $e->getMessage() ?? 'Internal server error'
];
}
}
/**
* Valida um token de autenticação JWT contra o registro do banco de dados e verifica a expiração/revogação.
*
* Este método realiza uma série de verificações de segurança para garantir que o token seja válido e não tenha sido comprometido ou expirado. Ele gerencia o processo de revogação do token no banco de dados se o token for encontrado, mas estiver expirado.
*
* #### Fluxo de Operação:
* 1. **Busca e Validação no DB:** Consulta o token no banco de dados. Se não for encontrado, lança `Exception` (código 404/401).
* 2. **Verificação de Expiração e Revogação:** Checa se o token está revogado (`is_revoked === 1`) ou se o prazo de validade (`expires_at`) foi ultrapassado.
* 3. **Revogação Automática (se Expirado):** Se o token estiver expirado, e ainda não estiver revogado, o método inicia uma transação (`DB::beginTransaction()`) para revogá-lo no DB. Em seguida, lança uma `Exception` com código `401 Unauthorized` (Token revogado).
* 4. **Verificação de Dispositivo:** Compara o IP (`usuario_ip`) e o User Agent (`usuario_user_agent`) armazenados no token com os da requisição atual. Se não houver correspondência, lança uma `Exception` (código 401), invalidando o token para o dispositivo.
* 5. **Retorno de Sucesso:** Se todas as verificações passarem, retorna `status` 'success' com o código `200 OK` e os dados do token.
*
* #### Tratamento de Erros:
* - Qualquer `Exception` lançada durante o processo é capturada, e o método retorna um array de erro formatado. O `DB::rollBack()` é tratado nos sub-métodos de revogação, mas o fluxo garante que o erro seja propagado.
*
* @param string $token O token JWT de autenticação a ser validado.
* @return array Um array associativo contendo o status da operação, um código de resposta HTTP e os dados do token, se válido.
*/
public function validateAuthToken(string $token) {
try {
// Consulta o token no banco de dados
$findToken = (new UsuarioModel())->findUsuarioTokenByIdentifier(identifier: 'token', value: $token);
if($findToken['status'] !== 'success') {
throw new Exception(message: 'Token não encontrado', code: 404);
}
$tokenData = $findToken['output']['data'];
// Verifica se o token está revogado ou expirado
if(($tokenData['is_revoked'] === 1) || (new DateTime() > new DateTime(datetime: $tokenData['expires_at']))) {
// Revoga o token se ainda não estiver revogado
if($tokenData['is_revoked'] === 0) {
// Incia Transação no Banco de Dados
DB::beginTransaction();
// Atualiza o token para revogado
$revokeToken = (new UsuarioModel())->updateUsuarioToken(usuarioTokenDTO: new UsuarioTokenDTO(data: [
'is_revoked' => 1,
'revoked_at' => (new DateTime())->format(format: 'Y-m-d H:i:s'),
'token' => $tokenData['token']
]));
// Verifica se a revogação foi bem-sucedida
if($revokeToken['status'] !== 'success') {
DB::rollBack();
throw new Exception(message: 'Erro ao revogar token', code: 500);
}
// Finaliza Transação no Banco de Dados
DB::commit();
}
// Lança exceção indicando que o token foi revogado
throw new Exception(message: 'Token revogado', code: 401);
}
// Verifica se o agente do usuário e o IP correspondem ao da requisição atual
if(isset($tokenData['usuario_ip']) && !in_array(needle: $tokenData['usuario_ip'], haystack: [$_SERVER['REMOTE_ADDR'], null], strict: true)) {
throw new Exception(message: "Token inválido para este dispositivo ou IP", code: 401);
}
if(isset($tokenData['usuario_user_agent']) && !in_array(needle: $tokenData['usuario_user_agent'], haystack: [$_SERVER['HTTP_USER_AGENT'], null], strict: true)) {
throw new Exception(message: "Token inválido para este dispositivo ou IP", code: 401);
}
return [
'response_code' => 200,
'status' => 'success',
'message' => 'Token válido',
'output' => [
'data' => $tokenData
]
];
} catch(Exception $e) {
return [
'response_code' => $e->getCode() ?? 500,
'status' => 'error',
'message' => $e->getMessage() ?? 'Internal server error'
];
}
}
/**
* Lista os contextos de trabalho (empresas) disponíveis para um usuário.
*
* Este método determina quais empresas o usuário autenticado pode acessar. Se o usuário for marcado como 'root' (`$is_root` = 1), ele lista todos os contextos ativos no sistema. Caso contrário, lista apenas os contextos associados diretamente ao seu UUID.
*
* #### Fluxo de Operação:
* 1. **Verificação de Permissão Root:**
* - Se `$is_root` for `1`, chama `UsuarioModel::findAllWorkContexts()` para listar todos os contextos ativos do sistema.
* - Se a consulta falhar, lança uma `Exception` (código 500).
* 2. **Busca por Usuário Específico:**
* - Se o usuário não for root, chama `UsuarioModel::findWorkContextsByUsuarioUUID()` para listar apenas os contextos diretamente associados ao `usuario_uuid`.
* - Se a consulta falhar, lança uma `Exception` (código 500).
* 3. **Retorno:** Retorna o array de resultados de contextos (sucesso ou falha, conforme retornado pelo modelo).
*
* #### Tratamento de Erros:
* - Qualquer `Exception` capturada durante o processo (incluindo falhas nos métodos do modelo) retorna um array de erro formatado com o código HTTP e a mensagem da exceção.
*
* @param string $usuario_uuid O UUID do usuário autenticado.
* @param int $is_root Indica se o usuário possui permissão de root (1) ou não (0).
* @return array Um array associativo contendo o status da operação, código HTTP e os dados dos contextos de trabalho (`output` com `data`), ou um erro.
*/
public function listWorkContexts(string $usuario_uuid, int $is_root = 0): array {
try {
// Se for root, lista todos os contextos de trabalho
if($is_root) {
// Consulta os contextos de trabalho do usuário
$contextos = (new UsuarioModel())->findAllWorkContexts(is_root: $is_root);
if($contextos['status'] !== 'success') {
throw new Exception(message: 'Erro ao recuperar contextos de trabalho', code: 500);
}
// Lógica para listar todos os contextos de trabalho
return $contextos;
}
// Consulta os contextos de trabalho do usuário
$contextos = (new UsuarioModel())->findWorkContextsByUsuarioUUID(usuario_uuid: $usuario_uuid);
if($contextos['status'] !== 'success') {
throw new Exception(message: 'Erro ao recuperar contextos de trabalho', code: 500);
}
// Lógica para listar os contextos de trabalho do usuário
return $contextos;
} catch(Exception $e) {
return [
'response_code' => $e->getCode() ?? 500,
'status' => 'error',
'message' => $e->getMessage() ?? 'Internal server error'
];
}
}
/**
* Seleciona e ativa um contexto de trabalho (empresa) para o usuário autenticado.
*
* Este método é chamado após o login para reemitir um token JWT que inclui as informações do contexto de trabalho selecionado. A operação revoga o token antigo da sessão atual e registra um novo, garantindo a atomicidade das operações de banco de dados.
*
* #### Fluxo de Operação:
* 1. **Verificação de Permissão:** Se o usuário não for 'root' (`$is_root === 0`), verifica se o usuário tem permissão para acessar o `contexto_uuid` específico. Se a verificação falhar, lança uma `Exception` (código 404).
* 2. **Busca de Dados:** Busca os dados completos do usuário e os dados do contexto (se não for root).
* 3. **Montagem do Payload:** Monta o payload do novo token JWT, incluindo os dados básicos do usuário e o novo `context_data` (UUID e informações da empresa, se aplicável).
* 4. **Gerenciamento Transacional:**
* - Inicia uma transação no banco de dados (`DB::beginTransaction()`).
* - **Revoga Token Antigo:** Atualiza o token anterior para revogado. Se falhar, reverte a transação e lança `Exception` (código 500).
* - **Gera e Registra Novo Token:** Gera um novo token JWT com o payload atualizado e o registra no banco de dados. Se falhar, reverte a transação e lança `Exception` (código 500).
* 5. **Confirmação e Retorno:** A transação é confirmada (`DB::commit()`), e o novo token é retornado com `status` 'success' e código `200 OK`.
*
* #### Tratamento de Erros:
* - Qualquer `Exception` lançada é capturada, e o método retorna um array de erro formatado com o código HTTP e a mensagem da exceção.
*
* @param string $usuario_uuid O UUID do usuário autenticado.
* @param string $contexto_uuid O UUID do contexto de trabalho (empresa) selecionado.
* @param int $is_root Flag que indica se o usuário é root (0 ou 1).
* @return array Um array associativo contendo o status da operação, o código de resposta HTTP e o novo token JWT.
*/
public function selectWorkContext(string $usuario_uuid, string $contexto_uuid, int $is_root = 0): array {
try {
// Se não for root, verifica se o contexto de trabalho pertence ao usuário
if($is_root === 0) {
// Consulta se o contexto de trabalho pertence ao usuário
$workContext = (new UsuarioModel())->findWorkContextByUsuarioAndContextoUUID(
usuario_uuid: $usuario_uuid,
contexto_uuid: $contexto_uuid
);
if($workContext['status'] !== 'success') {
throw new Exception(message: 'Contexto de trabalho não encontrado para o usuário', code: 404);
}
}
// Consulta os dados do usuário
$usuarioLogin = (new UsuarioModel())->findUsuarioByIdentifier(identifier: 'uuid', value: $usuario_uuid);
if($usuarioLogin['status'] !== 'success') {
throw new Exception(message: 'Usuário não encontrado', code: 404);
}
$usuarioData = $usuarioLogin['output']['data'];
// Monta novo payload do token com o contexto selecionado
$payload['user_data'] = [
'uuid' => $usuarioData['uuid'],
'nome_completo' => $usuarioData['nome_completo'],
'nome_usuario' => $usuarioData['nome_usuario'],
'is_root' => $usuarioData['is_root']
];
// Monta novo payload do token com o contexto selecionado
if(isset($workContext)) {
$payload['context_data'] = [
'uuid' => $workContext['output']['data']['contexto_uuid']
];
if($workContext['output']['data']['razao_social'] !== null) {
$payload['context_data']['razao_social'] = $workContext['output']['data']['razao_social'];
}
if($workContext['output']['data']['nome_fantasia'] !== null) {
$payload['context_data']['nome_fantasia'] = $workContext['output']['data']['nome_fantasia'];
}
} else {
$payload['context_data'] = [
'uuid' => $contexto_uuid
];
}
// Inicia Transação no Banco de Dados
DB::beginTransaction();
// Gera o token JWT
$generateJWT = JWTService::generateToken(payload: $payload);
// Revoga o token antigo
$revokeToken = (new UsuarioModel())->updateUsuarioToken(usuarioTokenDTO: new UsuarioTokenDTO(data: [
'is_revoked' => 1,
'revoked_at' => (new DateTime())->format(format: 'Y-m-d H:i:s'),
'token' => JWTService::getBearerToken()
]));
if($revokeToken['status'] !== 'success') {
DB::rollBack();
throw new Exception(message: 'Erro ao revogar token antigo', code: 500);
}
// Registra o novo token da sessão
$registerToken = (new UsuarioModel())->storeUsuarioToken(
usuarioTokenDTO: new UsuarioTokenDTO(data: [
'usuario_id' => $usuarioData['id'],
'usuario_ip' => $_SERVER['REMOTE_ADDR'] ?? null,
'usuario_user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? null,
'token' => $generateJWT['token'],
'is_revoked' => 0,
'expires_at' => $generateJWT['expires_at']
])
);
if($registerToken['status'] !== 'success') {
DB::rollBack();
throw new Exception(message: 'Erro ao registrar token de sessão', code: 500);
}
// Confirma a transação no banco de dados
DB::commit();
return [
'response_code' => 200,
'status' => 'success',
'message' => 'Contexto de trabalho selecionado com sucesso',
'output' => ['data' => $generateJWT]
];
} catch(Exception $e) {
return [
'response_code' => $e->getCode() ?? 500,
'status' => 'error',
'message' => $e->getMessage() ?? 'Internal server error'
];
}
}
/**
* Finaliza a sessão do usuário revogando um token de autenticação JWT específico.
*
* Este método atua como um **serviço**, orquestrando a revogação do token no banco de dados e garantindo a atomicidade da operação.
*
* #### Fluxo de Operação:
* 1. **Início da Transação:** Inicia uma transação no banco de dados (`DB::beginTransaction()`).
* 2. **Preparação do DTO:** Cria um `UsuarioTokenDTO` para a revogação, definindo o `token`, o status `is_revoked = 1` e o `revoked_at` como o timestamp atual.
* 3. **Revogação no Modelo:** Chama `UsuarioModel::updateUsuarioToken()` para invalidar o token no banco de dados.
* 4. **Gerenciamento de Transação:**
* * Se a revogação falhar (status do modelo for `!== 'success'`), a transação é **revertida** (`DB::rollBack()`), e uma `Exception` é lançada.
* * Se a revogação for bem-sucedida, a transação é **confirmada** (`DB::commit()`).
* 5. **Retorno de Sucesso:** Retorna um array com **`status` 'success'** e código **200 OK**.
*
* #### Tratamento de Erros:
* - Qualquer `Exception` lançada durante o processo é capturada, a transação é revertida, e o método retorna um array de erro formatado com o código HTTP e a mensagem da exceção.
*
* @param string $token O token JWT a ser revogado.
* @return array Um array associativo contendo o status da operação, o código de resposta HTTP e uma mensagem.
*/
public function logout(string $token): array {
try {
// Inicia Transação no Banco de Dados
DB::beginTransaction();
// Atualiza o token para revogado
$revokeToken = (new UsuarioModel())->updateUsuarioToken(usuarioTokenDTO: new UsuarioTokenDTO(data: [
'is_revoked' => 1,
'revoked_at' => (new DateTime())->format(format: 'Y-m-d H:i:s'),
'token' => $token
]));
if($revokeToken['status'] !== 'success') {
DB::rollBack();
throw new Exception(message: 'Erro ao revogar token', code: 500);
}
// Confirma a transação no banco de dados
DB::commit();
return [
'response_code' => 200,
'status' => 'success',
'message' => 'Logout realizado com sucesso'
];
} catch(Exception $e) {
return [
'response_code' => $e->getCode() ?? 500,
'status' => 'error',
'message' => $e->getMessage() ?? 'Internal server error'
];
}
}
}

View File

@@ -0,0 +1,179 @@
<?php
namespace Workbloom\Services;
use DateTime;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Workbloom\Config\Config;
class JWTService {
private static string $issuer;
private static int $expiration;
private static string $algorithm;
private static string $secretKey;
/**
* Inicializa as configurações para a geração de tokens JWT.
*
* Este método estático carrega as informações necessárias para a criação e validação de tokens
* JWT (JSON Web Tokens) a partir de uma classe de configuração (`Config`). Ele busca a chave secreta,
* o algoritmo de criptografia, o emissor e o tempo de expiração definidos para a aplicação
* e os armazena como propriedades estáticas da classe.
*
* Este método deve ser chamado antes de qualquer operação que envolva a geração ou
* verificação de tokens, garantindo que as configurações estejam prontas para uso.
*
* @return void
*/
public static function init(): void {
$Config = Config::getInstance();
self::$issuer = $Config->get(key: 'JWT_ISSUER');
self::$algorithm = $Config->get(key: 'JWT_ALGO');
self::$secretKey = $Config->get(key: 'JWT_SECRET');
self::$expiration = $Config->get(key: 'JWT_EXPIRATION');
}
/**
* Gera um token JWT com base em um payload e nas configurações da aplicação.
*
* Este método cria um token JWT válido a partir de um array de dados fornecido (`$payload`),
* adicionando automaticamente metadados essenciais como a data de emissão, a data de expiração e o emissor.
*
* O processo detalhado inclui:
* 1. **Inicialização:** Chama o método `init()` para garantir que todas as configurações do JWT
* (chave secreta, algoritmo, etc.) estejam carregadas.
* 2. **Definição dos Timestamps:** Calcula o timestamp de emissão (`iat`) e o de expiração (`exp`)
* com base no tempo de expiração definido nas configurações.
* 3. **Montagem do Payload:** Adiciona as chaves `iat`, `exp` e `iss` (emissor) ao array `$payload`.
* 4. **Codificação do Token:** Utiliza a biblioteca `JWT::encode()` para codificar o payload
* com a chave secreta e o algoritmo definidos nas configurações.
*
* @param array $payload Um array associativo contendo os dados personalizados que serão incluídos no token.
* @return array O token JWT gerado como um array.
*/
public static function generateToken(array $payload): array {
// Carrega as informações do arquivo de configuração
self::init();
// Define data de emissão e calcula a data de expiração do token
$expiration = self::$expiration;
$createdAt = (new DateTime())->getTimestamp();
$expireAt = (new DateTime())->modify(modifier: "+{$expiration} seconds")->getTimestamp();
// Adiciona data de criação ao payload
$payload['iat'] = $createdAt;
// Adiciona a expiração ao payload
$payload['exp'] = $expireAt;
// Adiciona o emissor ao payload
$payload['iss'] = self::$issuer;
return [
'token' => JWT::encode(payload: $payload, key: self::$secretKey, alg: self::$algorithm),
'expires_at' => (new DateTime())->setTimestamp(timestamp: $expireAt)->format(format: 'Y-m-d H:i:s')
];
}
/**
* Valida um token JWT e retorna o objeto de payload decodificado.
*
* Este método estático decodifica e valida um token JWT fornecido, utilizando
* a chave secreta e o algoritmo de criptografia configurados na classe.
*
* #### Como funciona:
* 1. Recebe o token (`$token`) que precisa ser validado.
* 2. Usa a biblioteca `JWT::decode()` para decodificar o token.
* 3. Para a decodificação, ele cria uma nova instância de `Key`, passando
* a chave secreta (`self::$secretKey`) e o algoritmo (`self::$algorithm`).
* Isso garante que o token só será decodificado se tiver sido assinado com a chave e algoritmo corretos.
* 4. Se o token for válido e a assinatura corresponder, a função retorna o payload do token
* como um objeto.
* 5. Se o token for inválido (por exemplo, assinatura incorreta, expirado ou formato incorreto),
* a biblioteca JWT lançará uma exceção, que a função que chama este método deve capturar.
*
* @param string $token O token JWT a ser validado e decodificado.
* @return object O objeto de payload decodificado do token.
*/
public static function validateToken(string $token): object {
// Inicializa as configurações se ainda não estiverem definidas
if (!isset(self::$secretKey)) {
self::init();
}
return JWT::decode(jwt: $token, keyOrKeyArray: new Key(keyMaterial: self::$secretKey, algorithm: self::$algorithm));
}
/**
* Decodifica um token JWT e retorna seu payload como um array associativo.
*
* Este método estático é responsável por decodificar e validar um token JWT. Antes de decodificar, ele garante que as configurações de chave secreta e algoritmo (`self::$secretKey` e `self::$algorithm`) já foram inicializadas chamando o método `init()` se necessário.
*
* #### Como Funciona:
* 1. **Inicialização:** Primeiro, verifica se a chave secreta (`self::$secretKey`) já foi carregada. Se não, ele chama `self::init()` para carregar as configurações de um arquivo externo.
* 2. **Decodificação:** Usa a biblioteca `Firebase\JWT\JWT::decode()` para decodificar o token fornecido. O método passa a chave secreta e o algoritmo para verificar a assinatura do token.
* 3. **Conversão:** O payload decodificado, que por padrão é um objeto, é convertido para uma string JSON e depois para um array associativo, o que facilita o acesso aos dados.
* 4. **Retorno:** Retorna o payload do token como um array associativo. Se o token for inválido (assinatura incorreta, expirado, etc.), a biblioteca JWT lançará uma exceção, que a função que chama este método deve capturar.
*
* @param string $token O token JWT a ser decodificado.
* @return array O payload do token decodificado como um array associativo.
*/
public static function decode(?string $token): array {
// Inicializa as configurações se ainda não estiverem definidas
if (!isset(self::$secretKey)) {
self::init();
}
if($token === null) {
return [];
}
$decoded = JWT::decode(jwt: $token, keyOrKeyArray: new Key(keyMaterial: self::$secretKey, algorithm: self::$algorithm));
return json_decode(json: json_encode(value: $decoded), associative: true);
}
/**
* Extrai o token de autenticação do tipo 'Bearer' do cabeçalho da requisição HTTP.
*
* Este método estático é um utilitário projetado para buscar um token JWT (JSON Web Token)
* que é enviado no cabeçalho `Authorization`. Ele é robusto o suficiente para verificar
* o token em diferentes superglobais do PHP, dependendo da configuração do servidor web.
*
* #### Fluxo de Busca:
* 1. Primeiro, tenta obter o cabeçalho `Authorization` diretamente da superglobal `$_SERVER`.
* 2. Em seguida, tenta a chave `HTTP_AUTHORIZATION`, que é um nome de variável comum em alguns ambientes.
* 3. Por último, usa a função `apache_request_headers()` para tentar obter o cabeçalho, caso as
* superglobais não estejam preenchidas. Isso garante compatibilidade com servidores Apache onde o cabeçalho pode não ser exposto.
* 4. Se o cabeçalho for encontrado, o valor é sanitizado (removendo espaços em branco) e a busca continua.
* 5. Se nenhum cabeçalho de autenticação for encontrado, o método retorna `null`.
*
* #### Extração do Token:
* - Depois de encontrar o cabeçalho, o método usa uma expressão regular (`/Bearer\s(\S+)/`)
* para verificar se o valor do cabeçalho está no formato `Bearer <token>`.
* - Se o padrão corresponder, ele extrai e retorna a string do token.
* - Se o padrão não corresponder, o método retorna `null`.
*
* @return string|null O token de autenticação do tipo 'Bearer' como uma string, ou `null`
* se o cabeçalho não for encontrado ou não estiver no formato esperado.
*/
public static function getBearerToken(): ?string {
if (!isset(self::$secretKey)) {
self::init();
}
// Tenta pegar o header de todas as fontes possíveis
$headers = $_SERVER['Authorization'] ?? $_SERVER['HTTP_AUTHORIZATION'] ?? $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ?? (function_exists(function: 'apache_request_headers') ? apache_request_headers() : []);
// Se veio do apache_request_headers, normaliza chaves
if (is_array(value: $headers)) {
$headers = array_change_key_case(array: $headers, case: CASE_LOWER);
$headers = $headers['authorization'] ?? '';
}
$headers = trim(string: $headers);
if ($headers && preg_match(pattern: '/Bearer\s(\S+)/', subject: $headers, matches: $matches)) {
return $matches[1];
}
return null;
}
}

View File

@@ -70,6 +70,10 @@ CREATE TABLE IF NOT EXISTS public.empresa (
deleted_at TIMESTAMP DEFAULT NULL
);
INSERT INTO public.empresa (uuid, razao_social, nome_fantasia, inscricao_tipo_id, document_inscricao, inscricao_raiz, endereco_codigo_ibge, endereco_cep, endereco_logradouro, endereco_numero, endereco_complemento, endereco_bairro, endereco_cidade, endereco_estado) VALUES
('019a07d3-9174-76b6-a9f0-9e0e189fdba8', 'Ativa Servicos em Refrigeracao e Servicos em Eletrica LTDA', 'Ativa Refrigeração e Elétrica', 1, '61365393000121', '61365393', '2304400', '60115191', 'Rua Monesenhor Bruno', '1153', 'Sala 1423', 'Aldeota', 'Fortaleza', 'CE'),
('019a07d3-9174-7879-93ae-4de7da3b5a93', 'A C C Andrade LTDA', 'A.c. Car', 1, '21899523000191', '21899523', '2307601', '62930000', 'Rua Leila Kristinna Lopes Maia', '58', NULL, 'Limoeirinho', 'Limoeiro do Norte', 'CE');
-- ===============================================================================================================
-- Empresa Certificado Digital
-- ===============================================================================================================

View File