Аутентификация
Аутентификация — проверка, кто пришёл на сайт: пользователь вводит почту и пароль, а приложение запоминает его в сессии. В AdonisJS этим занимается пакет @adonisjs/auth. Если при создании проекта на наборе Web вы выбрали вход через session, всё уже настроено: модель User, миграция таблицы users и файл config/auth.ts с охранником (guard) web.
Модель пользователя
// app/models/user.ts
import { DateTime } from 'luxon'
import hash from '@adonisjs/core/services/hash'
import { compose } from '@adonisjs/core/helpers'
import { BaseModel, column } from '@adonisjs/lucid/orm'
import { withAuthFinder } from '@adonisjs/auth/mixins/lucid'
const AuthFinder = withAuthFinder(() => hash.use('scrypt'), {
uids: ['email'],
passwordColumnName: 'password',
})
export default class User extends compose(BaseModel, AuthFinder) {
@column({ isPrimary: true })
declare id: number
@column()
declare fullName: string | null
@column()
declare email: string
@column({ serializeAs: null })
declare password: string
@column.dateTime({ autoCreate: true })
declare createdAt: DateTime
}Примесь withAuthFinder добавляет модели две вещи: пароль хешируется сам перед сохранением, а метод verifyCredentials проверяет почту и пароль. serializeAs: null не даёт паролю попасть в JSON.
Регистрация
// app/controllers/register_controller.ts
import type { HttpContext } from '@adonisjs/core/http'
import User from '#models/user'
import { registerValidator } from '#validators/auth'
export default class RegisterController {
async store({ request, auth, response }: HttpContext) {
const payload = await request.validateUsing(registerValidator)
const user = await User.create(payload)
await auth.use('web').login(user)
return response.redirect('/posts')
}
}Валидатор лежит в app/validators/auth.ts:
// app/validators/auth.ts
import vine from '@vinejs/vine'
export const registerValidator = vine.compile(
vine.object({
fullName: vine.string().trim().minLength(2),
email: vine.string().email().unique({ table: 'users', column: 'email' }),
password: vine.string().minLength(8),
})
)Правило unique не даст зарегистрировать вторую учётную запись с той же почтой. После создания пользователь сразу входит на сайт.
Вход и выход
// app/controllers/session_controller.ts
import type { HttpContext } from '@adonisjs/core/http'
import User from '#models/user'
export default class SessionController {
async store({ request, auth, response }: HttpContext) {
const { email, password } = request.only(['email', 'password'])
const user = await User.verifyCredentials(email, password)
await auth.use('web').login(user)
return response.redirect('/posts')
}
async destroy({ auth, response }: HttpContext) {
await auth.use('web').logout()
return response.redirect('/login')
}
}Если пароль неверный, verifyCredentials выбросит ошибку, и форма вернётся с сообщением. Показать его можно тегом @error('E_INVALID_CREDENTIALS') с переменной $message внутри.
Маршруты
// start/routes.ts
import router from '@adonisjs/core/services/router'
import { middleware } from '#start/kernel'
const SessionController = () => import('#controllers/session_controller')
router.on('/login').render('pages/auth/login').use(middleware.guest())
router.post('/login', [SessionController, 'store']).use(middleware.guest())
router.post('/logout', [SessionController, 'destroy']).use(middleware.auth())middleware.guest() пускает только гостей, middleware.auth() — только вошедших.
Текущий пользователь
На маршрутах с middleware.auth() пользователь доступен как auth.user — в контроллере и в шаблоне:
{{-- resources/views/components/layout.edge --}}
@if(auth.user)
<span>{{ auth.user.fullName }}</span>
<form method="POST" action="/logout">
{{ csrfField() }}
<button type="submit">Выйти</button>
</form>
@else
<a href="/login">Войти</a>
@endНа публичных страницах без этого middleware auth.user пуст, пока не вызван await auth.check(). Как сделать это сразу для всех страниц, показано в уроке о middleware.
Частая ошибка
Пароль хешируют вручную перед User.create, а примесь withAuthFinder хеширует его ещё раз. В базу попадает хеш от хеша, и войти уже невозможно. С withAuthFinder передавайте в create пароль как есть.