Autenticação de usuário


1Introdução

Esta documentação explica passo a passo como configurar um sistema de autenticação sem senha em uma aplicação Temma.

O princípio é simples: o usuário digita seu endereço de email, recebe um link mágico por email, e ao clicar nele, é autenticado. O link só pode ser usado uma vez e expira após uma hora.

Esse sistema se baseia em dois componentes fornecidos pelo Temma:

  • O controlador/plugin \Temma\Controllers\Auth: gerencia o formulário de login, o envio de emails e a sessão do usuário.
  • O atributo \Temma\Attributes\Auth: restringe o acesso a controladores e ações com base na autenticação, nos papéis e nos serviços.

2Pré-requisitos

Para seguir este tutorial, você precisa de:

  • Um projeto Temma funcionando (veja a página de Instalação).
  • Um banco de dados MySQL ou MariaDB configurado em suas fontes de dados.
  • Um servidor capaz de enviar emails (para os links mágicos).

3Criação do banco de dados

O sistema de autenticação requer duas tabelas: User (usuários) e AuthToken (tokens de conexão enviados por email).

Execute as seguintes consultas SQL para criar essas tabelas:

CREATE TABLE User (
    id               INT UNSIGNED NOT NULL AUTO_INCREMENT,
    date_creation    DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    date_last_login  DATETIME,
    date_last_access DATETIME,
    email            TINYTEXT CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
    name             TINYTEXT,
    roles            SET('admin', 'writer', 'reviewer'), -- deve ser personalizado
    services         SET('articles', 'news', 'images'), -- deve ser personalizado
    PRIMARY KEY (id),
    UNIQUE INDEX email (email(255))
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

CREATE TABLE AuthToken (
    token         CHAR(64) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL,
    expiration    DATETIME NOT NULL,
    user_id       INT UNSIGNED NOT NULL,
    PRIMARY KEY (token),
    INDEX expiration (expiration),
    FOREIGN KEY (user_id) REFERENCES User (id) ON DELETE CASCADE
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

Os campos roles e services na tabela User são do tipo SET: você precisa personalizá-los de acordo com as necessidades da sua aplicação. Roles representam as funções do usuário (administrador, redator, etc.), e services representam os módulos aos quais eles têm acesso (artigos, imagens, etc.).


4Configuração

Você precisa configurar \Temma\Controllers\Auth tanto como um pré-plugin (para gerenciar a sessão do usuário a cada requisição) quanto como uma rota (para tornar o formulário de login acessível).

Adicione o seguinte ao seu arquivo etc/temma.php:

<?php

return [
    'application' => [
        'dataSources' => [
            'db' => 'mysql://user:password@localhost/myDatabase'
        ]
    ],
    'plugins' => [
        '_pre' => [
            '\Temma\Controllers\Auth'
        ]
    ],
    'routes' => [
        'auth' => '\Temma\Controllers\Auth'
    ]
];
  • Linhas 5 a 7: configuração da conexão com o banco de dados (adapte ao seu ambiente).
  • Linhas 10 a 12: declaração do pré-plugin. A cada requisição, ele verifica se o usuário está autenticado e disponibiliza suas informações.
  • Linhas 14 a 16: declaração da rota. O controlador ficará acessível pelas URLs /auth/login e /auth/logout.

5Personalizando o template de login

O controlador Auth usa um template Smarty localizado em templates/auth/login.tpl. A versão fornecida pelo Temma é mínima; recomendamos que você crie sua própria versão.

Aqui está um exemplo de template de formulário de login:

<html>
<head>
    <title>Login</title>
</head>
<body>
    <h1>Login</h1>
    {if $authError}
        <p style="color: red;">Endereço de email desconhecido.</p>
    {/if}
    {if $authSent}
        <p style="color: green;">
            Um link de login foi enviado para o seu endereço de email.
            Verifique sua caixa de entrada.
        </p>
    {else}
        <form method="post" action="/auth/login">
            <label for="email">Endereço de email:</label>
            <input type="email" id="email" name="email" required />
            <button type="submit">Entrar</button>
        </form>
    {/if}
</body>
</html>
  • Linha 7: se o endereço de email não for encontrado no banco de dados, uma mensagem de erro é exibida.
  • Linha 10: se o email for enviado com sucesso, uma mensagem de confirmação é exibida no lugar do formulário.
  • Linhas 16 a 19: o formulário envia o endereço de email via POST para /auth/login.

6Testando o login

Antes de testar, certifique-se de ter pelo menos um usuário no banco de dados. Você pode inserir um manualmente:

INSERT INTO User (email, name) VALUES ('john@example.com', 'John');

O processo de login funciona da seguinte forma:

  1. Acesse /auth/login no seu navegador.
  2. Digite o endereço de email do usuário e envie o formulário.
  3. Um email contendo um link de login é enviado para esse endereço.
  4. Ao clicar no link, o usuário é autenticado e redirecionado para a página inicial.

O link de login é de uso único e expira após uma hora.


7Protegendo páginas com o atributo Auth

O atributo Auth restringe o acesso a controladores ou ações com base na autenticação. Ele depende da variável de template $currentUser definida pelo pré-plugin.

Para proteger um controlador inteiro (todas as suas ações exigem autenticação):

use \Temma\Attributes\Auth as TµAuth;

#[TµAuth]
class Account extends \Temma\Web\Controller {
    public function profile() {
        // acessível apenas a usuários autenticados
    }
    public function settings() {
        // idem
    }
}

Para proteger apenas ações específicas:

use \Temma\Attributes\Auth as TµAuth;

class Blog extends \Temma\Web\Controller {
    // acessível a todos
    public function list() { }

    // restrito a usuários com o papel "writer"
    #[TµAuth('writer')]
    public function create() { }

    // restrito a usuários com acesso ao serviço "images"
    #[TµAuth(service: 'images')]
    public function uploadImage() { }

    // restrito a usuários com o papel "admin" ou "writer"
    #[TµAuth(['admin', 'writer'])]
    public function edit() { }
}

Se um usuário não autorizado tentar acessar uma página protegida, ele recebe um erro HTTP 401 por padrão. Para redirecioná-lo ao formulário de login, adicione esta configuração ao etc/temma.php:

<?php

return [
    'x-security' => [
        'authRedirect' => '/auth/login'
    ]
];

Para armazenar a URL solicitada de modo que o usuário seja redirecionado a ela após o login, use o parâmetro storeUrl:

use \Temma\Attributes\Auth as TµAuth;

#[TµAuth(redirect: '/auth/login', storeUrl: true)]
class Account extends \Temma\Web\Controller {
    // ...
}

Dessa forma, após efetuar o login, o usuário será redirecionado para a página que estava tentando acessar.


8Usando informações do usuário

O pré-plugin Auth fornece duas variáveis de template, acessíveis em templates, controladores e plugins:

  • currentUserId: identificador do usuário autenticado (ou null se não autenticado).
  • currentUser: dados do usuário (array associativo contendo as chaves id, email, name, roles, services, etc.).

Aqui está um exemplo de uso em um template Smarty:

{if $currentUserId}
    <p>Olá, {$currentUser.name}!</p>
    {if $currentUser.roles.admin}
        <a href="/admin">Administração</a>
    {/if}
    <a href="/auth/logout">Logout</a>
{else}
    <a href="/auth/login">Login</a>
{/if}

Em um controlador, essas variáveis são acessíveis via $this['currentUserId'] e $this['currentUser']:

class Account extends \Temma\Web\Controller {
    public function profile() {
        $userId = $this['currentUserId'];
        $user = $this['currentUser'];
        $this['userName'] = $user['name'];
        $this['isAdmin'] = $user['roles']['admin'] ?? false;
    }
}

9Logout

Para desconectar um usuário, redirecione-o para a URL /auth/logout. Ele será automaticamente redirecionado para a página inicial após o logout.

Exemplo de link de logout em um template:

<a href="/auth/logout">Logout</a>

Se você quiser redirecionar o usuário para uma URL específica após o login (e, portanto, também após o logout e um novo login), configure o parâmetro redirection (veja a seção Configuração avançada).


10Configuração avançada

Todas as opções de configuração ficam sob a chave x-security > auth no arquivo etc/temma.php.

Personalização do email

Você pode personalizar o remetente, o assunto e o conteúdo do email de login:

<?php

return [
    'x-security' => [
        'auth' => [
            'emailSender'  => 'no-reply@mysite.com',
            'emailSubject' => 'Your login link',
            'emailText'    => "Hi,\n\nHere is your login link:\n%s\n\nIt is valid for 1 hour.\n\nBest regards"
        ]
    ]
];

O marcador %s no texto é substituído pela URL de login.

Registro automático

Por padrão, apenas os usuários já presentes no banco de dados podem fazer login. Para registrar automaticamente novos usuários:

<?php

return [
    'x-security' => [
        'auth' => [
            'registration' => true
        ]
    ]
];

Redirecionamento após o login

Por padrão, o usuário é redirecionado para a página inicial após o login. Você pode alterar essa URL:

<?php

return [
    'x-security' => [
        'auth' => [
            'redirection' => '/my-account'
        ]
    ]
];

Observe que, se o atributo Auth tiver armazenado a URL solicitada (parâmetro storeUrl), ela será usada em seu lugar.

Personalização de nomes de tabelas e campos

Se suas tabelas ou campos tiverem nomes diferentes dos padrões, você pode redefini-los:

<?php

return [
    'x-security' => [
        'auth' => [
            'userData' => [
                'base'     => 'auth_db',
                'table'    => 'tUser',
                'id'       => 'user_id',
                'email'    => 'user_mail',
                'roles'    => 'user_roles',
                'services' => 'user_services',
            ],
            'tokenData' => [
                'base'       => 'auth_db',
                'table'      => 'tToken',
                'token'      => 'token_hash',
                'expiration' => 'expiration_date',
                'user_id'    => 'fk_user_id',
            ]
        ]
    ]
];

Para mais detalhes sobre todas as opções de configuração, consulte a documentação completa do controlador/plugin Auth e do atributo Auth.