Autenticação
O provedor de autenticação AdonisJs é um sistema completo para autenticar solicitações HTTP usando vários autenticadores.
Usando autenticadores, você pode construir sistemas de login tradicionais baseados em sessão e proteger suas APIs.
NOTA
Para gerenciamento de perfil de usuário opinativo, confira o pacote oficial Adonis Persona – um serviço simples e funcional para permitir que você crie, verifique e atualize perfis de usuário no AdonisJs.
Autenticadores
Cada autenticador é uma combinação de esquema de autenticação e serializador.
Esquemas
- Sessão (
session
) - Autenticação básica (
basic
) - JWT (
jwt
) - Tokens de API pessoais (
api
)
Serializadores
- Lucid (
lucid
) - Banco de dados (
database
)
Categorias de autenticação
A autenticação é dividida em duas categorias amplas: Stateful e Stateless.
A autenticação baseada em sessão é considerada Stateful, pois usuários logados podem navegar para diferentes áreas do aplicativo sem reenviar suas credenciais.
A autenticação Stateless exige que o usuário reenvie suas credenciais em cada solicitação HTTP, o que é muito comum com APIs.
O AdonisJs fornece as ferramentas e ajudantes necessários para gerenciar ambos os tipos de autenticação com facilidade.
Hash de senha
O provedor de autenticação AdonisJs usa o módulo Hash para verificar senhas.
Sempre faça hash de suas senhas antes de salvá-las no banco de dados.
Configuração
O provedor de autenticação AdonisJs vem pré-instalado com boilerplates fullstack
e api
.
Se o provedor de autenticação ainda não estiver configurado, siga as instruções abaixo.
Primeiro, execute o comando adonis
para baixar o provedor de autenticação:
adonis install @adonisjs/auth
Em seguida, registre o provedor de autenticação dentro do arquivo start/app.js
:
// .start/app.js
const providers = [
'@adonisjs/auth/providers/AuthProvider'
]
Finalmente, registre o middleware de autenticação dentro do arquivo start/kernel.js
:
// .start/kernel.js
const globalMiddleware = [
'Adonis/Middleware/AuthInit'
]
const namedMiddleware = {
auth: 'Adonis/Middleware/Auth',
guest: 'Adonis/Middleware/AllowGuestOnly'
}
Config
Sua configuração de autenticação é salva dentro do arquivo config/auth.js
.
Por padrão, o autenticador session
é usado para autenticar solicitações de aplicativo.
Exemplo básico
Vamos começar com o exemplo de login de um usuário, mostrando apenas o perfil dele se ele estiver logado.
Primeiro, adicione as seguintes rotas ao arquivo start/routes.js
:
// .start/routes.js
Route
.post('login', 'UserController.login')
.middleware('guest')
Route
.get('users/:id', 'UserController.show')
.middleware('auth')
Em seguida, crie o UserController
por meio do comando adonis
:
adonis make:controller User
Adicione o método login
ao UserController
:
// .app/Controllers/Http/UserController.js
class UserController {
async login ({ auth, request }) {
const { email, password } = request.all()
await auth.attempt(email, password)
return 'Logged in successfully'
}
}
O método login
acima extrai o email
e a password
do usuário da solicitação e os registra se suas credenciais forem válidas.
Por fim, adicione o método show
ao UserController
:
// .app/Controllers/Http/UserController.js
class UserController {
async login () {
...
}
show ({ auth, params }) {
if (auth.user.id !== Number(params.id)) {
return "You cannot see someone else's profile"
}
return auth.user
}
}
O método show
acima verifica se o parâmetro de rota id
é igual ao id
do usuário logado no momento. Se sim, o modelo de usuário autenticado é retornado (que o AdonisJS converte para JSON na resposta final).
Sessão
Configuração de Sessão
// .config/auth.js
module.exports = {
authenticator: 'session',
session: {
serializer: 'Lucid',
scheme: 'session',
model: 'App/Models/User',
uid: 'email',
password: 'password'
}
}
Proteção CSRF
Cross-Site Request Forgery (CSRF) permite que um invasor execute ações em nome de outro usuário sem seu conhecimento ou permissão.
O AdonisJs protege seu aplicativo de ataques CSRF negando solicitações não identificadas. As solicitações HTTP com métodos POST, PUT e DELETE são verificadas para garantir que as pessoas certas do lugar certo invoquem essas solicitações.
Configuração
Instale o provedor shield
via npm executando o seguinte comando:
await auth.attempt(uid, password)
Em seguida, registre o provedor dentro do arquivo start/app.js
:
const user = await User.find(1)
await auth.login(user)
Finalmente, registre o middleware global dentro do arquivo start/kernel.js
:
await auth.loginViaId(1)
NOTA
O middleware Shield depende de sessões, então certifique-se de que elas estejam configuradas corretamente.
Configuração
A configuração para CSRF é salva dentro do arquivo config/shield.js
:
await auth
.remember(true)
.attempt(email, password)
Chave | Valor | Descrição |
---|---|---|
serializer | lucid , database | Serializador usado para buscar o usuário do banco de dados. |
scheme | session , basic , jwt , api | Esquema usado para buscar e autenticar credenciais do usuário. |
uid | Nome do campo do banco de dados | Campo do banco de dados usado como identificador exclusivo para um determinado usuário. |
password | Nome do campo do banco de dados | Campo usado para verificar a senha do usuário. |
model | Namespace do modelo (lucid apenas) | Modelo usado para consultar o banco de dados, aplicável somente ao usar o serializador lucid . |
table | Nome da tabela do banco de dados (database apenas) | Aplicável somente ao usar o serializador database . |
Métodos de Sessão
O autenticador session expõe os seguintes métodos para efetuar login e autenticar usuários.
attempt(uid, password)
Login via uid
e password
, lançando uma exceção se nenhum usuário for encontrado ou a senha for inválida:
try {
await auth.check()
} catch (error) {
response.send('You are not logged in')
}
login(user)
Login via instância do modelo user
, não verifica nada, mas simplesmente marca o usuário como logado:
try {
return await auth.getUser()
} catch (error) {
response.send('You are not logged in')
}
loginViaId(id)
Login via ID do usuário, consultando o banco de dados para garantir que o usuário exista:
await auth.logout()
remember
Ao chamar métodos como attempt
, login
ou loginViaId
, encadeie o método remember
para garantir que os usuários permaneçam logados após fechar o navegador:
try {
await auth.check()
} catch (error) {
response.send(error.message)
}
NOTA
O método remember
cria um token para o usuário dentro da tabela tokens
. Se você quiser revogar a sessão de longa duração de um usuário específico, basta definir is_revoked
como true.
check
Verifique se um usuário já está logado lendo sua sessão:
try {
return await auth.getUser()
} catch (error) {
response.send('Credentials missing')
}
getUser
Retorna a instância do usuário logado (por meio do método check
):
// .config/auth.js
module.exports = {
authenticator: 'jwt',
jwt: {
serializer: 'Lucid',
model: 'App/Models/User',
scheme: 'jwt',
uid: 'email',
password: 'password',
options: {
secret: Config.get('app.appKey'),
// For additional options, see the table below...
}
}
}
logout
Desconecte o usuário logado no momento:
To secure JWT using `RS256` asymmetric algorithm, you need to explictly provide `algorithm` option and set it to `RS256`.
You must provide your `private key` via `secret` option which will be used by the framework to sign your JWT.
And you must provide your `public key` via `public` option, which will be used by the framework
to verify the JWT.
const fs = use("fs");
module.exports = {
authenticator: 'jwt',
jwt: {
//...
options: {
algorithm: 'RS256',
secret: fs.readFileSync('absolute-path-to-your-private-key-file'),
public: fs.readFileSync('absolute-path-to-your-public-key-file'),
// For additional options, see the table above...
}
}
}
Autenticação básica
Como a autenticação básica é sem estado, com os usuários passando credenciais por solicitação, não há conceito de login
e logout
.
NOTA
O cabeçalho Authorization = Basic <credentials>
deve ser definido para autenticar solicitações de autenticação básicas, onde <credentials>
é uma string codificada em base64
de uid:password
, onde uid
é o campo de banco de dados uid
definido no arquivo config/auth.js
.
Métodos de autenticação básicos
O autenticador básico expõe os seguintes métodos para autenticar usuários.
check
Verifique as credenciais básicas de autenticação do usuário no cabeçalho da solicitação, verificando a existência do usuário e validando sua senha:
await auth.attempt(uid, password)
getUser
Retorna a instância do usuário conectado (por meio do método check
):
// Saída
{
type: 'type',
token: '.....',
refreshToken: '....'
}
JWT
Autenticação JWT é um padrão da indústria para implementar autenticação sem estado por meio de tokens de string.
O AdonisJs oferece suporte a tokens JWT prontos para uso por meio de seu autenticador jwt.
OBSERVAÇÃO
O cabeçalho Authorization = Bearer <token>
deve ser definido para autenticar solicitações de autenticação jwt, onde <token>
é um token JWT válido.
Configuração JWT
const user = await User.find(1)
await auth.generate(user)
Chave | Valores | Valor padrão | Descrição |
---|---|---|---|
algorithm | HS256 , HS384 , RS256 | HS256 | Algoritmo usado para gerar tokens. |
expiresIn | Tempo válido em segundos ou string ms | null | Quando expirar os tokens. |
notBefore | Tempo válido em segundos ou string ms | null | Tempo mínimo para manter os tokens válidos. |
audience | String | null | aud alegar. |
issuer | Array or String | null | iss alegar. |
subject | String | null | sub alegar. |
public | String | null | Chave public para verificar o token criptografado com o algoritmo RSA. |
await auth
.withRefreshToken()
.attempt(uid, password)
Métodos JWT
O autenticador jwt expõe os seguintes métodos para gerar tokens JWT e autenticar usuários.
attempt(uid, password, [jwtPayload], [jwtOptions])
Valida as credenciais do usuário e gera um token JWT em troca:
const refreshToken = request.input('refresh_token')
await auth.generateForRefreshToken(refreshToken, true)
await auth
.newRefreshToken()
.generateForRefreshToken(refreshToken)
generate(user, [jwtPayload], [jwtOptions])
Gera o token JWT para um determinado usuário:
try {
await auth.check()
} catch (error) {
response.send('Missing or invalid jwt token')
}
Opcionalmente, você pode passar um objeto personalizado para ser codificado dentro do token. Passar jwtPayload=true
codifica o objeto do usuário dentro do token.
withRefreshToken
Instrua o autenticador JWT a gerar um token de atualização também:
try {
return await auth.getUser()
} catch (error) {
response.send('Missing or invalid jwt token')
}
O token de atualização é gerado para que os clientes possam atualizar o token jwt
real sem solicitar credenciais do usuário novamente.
generateForRefreshToken(refresh_token, [jwtPayload])
Gere um novo token JWT usando o token de atualização. Passar jwtPayload=true codifica o objeto do usuário dentro do token.
await auth.listTokens()
newRefreshToken
Ao gerar um novo token jwt
, o provedor de autenticação não emite novamente um novo token de atualização e, em vez disso, usa o antigo. Se desejar, você também pode regenerar um novo token de atualização:
const token = await auth.attempt(uid, password)
check
Verifica se um token JWT válido foi enviado por meio do cabeçalho Authorization
:
// Saída:
{
type: 'bearer',
token: '...'
}
getUser
Retorna a instância do usuário conectado (por meio do método check
):
const user = await User.find(1)
const token = await auth.generate(user)
listTokens
Lista todos os tokens de atualização JWT para o usuário:
try {
await auth.check()
} catch (error) {
response.send('Missing or invalid api token')
}
Tokens de API pessoais
Os tokens de API pessoais foram popularizados pelo Github para uso em scripts como um substituto revogável para a autenticação tradicional de email e senha.
O AdonisJs permite que você crie aplicativos onde seus usuários podem criar tokens de API pessoais e usá-los para autenticação.
NOTA
O cabeçalho Authorization = Bearer <token>
deve ser definido para autenticar solicitações de autenticação api, onde <token>
é um token de API válido.
Métodos de API
O autenticador api expõe os seguintes métodos para gerar tokens de API e autenticar usuários.
attempt(uid, password)
Valida as credenciais do usuário e então gera um novo token para elas:
try {
await auth.getUser()
} catch (error) {
response.send('Missing or invalid api token')
}
await auth.listTokens()
generate(user)
Gera token para um determinado usuário:
// loggedin user via sessions
const user = auth.user
auth
.authenticator('jwt')
.generate(user)
check
Verifica se um token de API válido foi passado pelo cabeçalho Authorization
:
// .start/kernel.js
const namedMiddleware = {
auth: 'Adonis/Middleware/Auth'
}
getUser
Retorna a instância do usuário logado (pelo método check
):
// .start/routes.js
Route
.get('users/profile', 'UserController.profile')
.middleware(['auth'])
listTokens
Lista todos os tokens de API para o usuário:
// .start/kernel.js
const namedMiddleware = {
guest: 'Adonis/Middleware/AllowGuestOnly'
}
Alternando autenticadores
O auth provider simplifica a troca entre vários autenticadores em tempo de execução chamando o método authenticator
.
Assumindo que o usuário esteja logado usando o autenticador session
, podemos gerar um token JWT para ele da seguinte forma:
// .start/routes.js
// Não queremos que nosso usuário logado acesse esta visualização
Route
.get('login', 'AuthController.login')
.middleware(['guest'])
Middleware Auth
O middleware auth
automatiza a autenticação para quaisquer rotas aplicadas.
Ele é registrado como um middleware nomeado dentro do arquivo start/kernel.js
:
Hello {{ auth.user.username }}!
Uso:
@loggedIn
<h2> Hello {{ auth.user.username }} </h2>
@else
<p> Please login </p>
@endloggedIn
Middleware Guest
O middleware guest
verifica se o usuário não está autenticado.
Ele é registrado como um middleware nomeado dentro do arquivo start/kernel.js
:
const refreshToken = '' // get it from user
await auth
.authenticator('jwt')
.revokeTokens([refreshToken])
Uso:
const refreshToken = '' // get it from user
await auth
.authenticator('jwt')
.revokeTokens([refreshToken], true)
Helpers
O provedor de autenticação adiciona alguns helpers à instância de visualização para que você possa escrever HTML em torno do estado de um usuário conectado.
auth
Referência ao objeto auth
:
await auth
.authenticator('jwt')
.revokeTokens()
loggedIn
A tag loggedIn
pode ser usada para escrever if/else
em torno do usuário conectado:
// for currently loggedin user
const apiToken = auth.getAuthHeader()
await auth
.authenticator('api')
.revokeTokens([apiToken])
Revogando tokens
Os esquemas jwt
e api
expõem métodos para revogar tokens usando a interface auth
.
NOTA
Para jwt
, os tokens de atualização são apenas revogados, já que os tokens reais nunca são salvos no banco de dados.
revokeTokens(tokens, delete = false)
O método a seguir revogará tokens definindo um sinalizador na tabela tokens
:
const user = await User.find(1)
await auth
.authenticator('jwt')
.revokeTokensForUser(user)
Se true
for passado como o 2º argumento, em vez de definir o sinalizador de banco de dados is_revoked
, a linha relevante será excluída do banco de dados:
const refreshToken = '' // get it from user
await auth
.authenticator('jwt')
.revokeTokens([refreshToken], true)
Para revogar todos os tokens, chame revokeTokens
sem nenhum argumento:
await auth
.authenticator('jwt')
.revokeTokens()
Ao revogar o token api
para o usuário atualmente conectado, você pode acessar o valor do cabeçalho da solicitação:
// for currently loggedin user
const apiToken = auth.getAuthHeader()
await auth
.authenticator('api')
.revokeTokens([apiToken])
revokeTokensForUser(user, tokens, delete = false)
Este método funciona da mesma forma que o método revokeTokens
, mas em vez disso, você pode especificar o usuário você mesmo:
const user = await User.find(1)
await auth
.authenticator('jwt')
.revokeTokensForUser(user)
Criptografia de Token
Os tokens são salvos em formato simples dentro do banco de dados, mas são enviados em formato criptografado para o usuário final.
Isso é feito para garantir que, se alguém acessar seu banco de dados, não consiga usar seus tokens diretamente (eles teriam que descobrir como criptografá-los usando a chave secreta).