セッションガード

セッションガード

セッションガードは、HTTPリクエスト中にユーザーのログインと認証を行うために@adonisjs/sessionパッケージを使用します。

セッションとクッキーは長い間インターネット上で使用されており、ほとんどのアプリケーションで非常に優れた機能を提供しています。したがって、サーバーレンダリングされるアプリケーションや同じトップレベルドメインのSPAウェブクライアントでは、セッションガードの使用を推奨します。

ガードの設定

認証ガードはconfig/auth.tsファイル内で定義されます。このファイル内のguardsオブジェクトの下に複数のガードを設定できます。

config/auth.ts
import { defineConfig } from '@adonisjs/auth'
import { sessionGuard, sessionUserProvider } from '@adonisjs/auth/session'
const authConfig = defineConfig({
default: 'web',
guards: {
web: sessionGuard({
useRememberMeTokens: false,
provider: sessionUserProvider({
model: () => import('#models/user'),
}),
})
},
})
export default authConfig

sessionGuardメソッドはSessionGuardクラスのインスタンスを作成します。これは、認証中にユーザーを検索するために使用できるユーザープロバイダと、リメンバートークンの動作を設定するためのオプションの設定オブジェクトを受け入れます。

sessionUserProviderメソッドはSessionLucidUserProviderクラスのインスタンスを作成します。これは、認証に使用するモデルへの参照を受け入れます。

ログインの実行

guard.loginメソッドを使用してユーザーをログインできます。このメソッドはUserモデルのインスタンスを受け入れ、ユーザーのログインセッションを作成します。

次の例:

  • AuthFinder mixinからverifyCredentialsメソッドを使用して、メールアドレスとパスワードでユーザーを検索します。

  • auth.use('web')は、config/auth.tsファイルで設定されたSessionGuardのインスタンスを返します。

  • 次に、guard.login(user)メソッドを呼び出して、ユーザーのログインセッションを作成します。

  • 最後に、ユーザーを/dashboardエンドポイントにリダイレクトします。リダイレクトエンドポイントをカスタマイズしてください。

import User from '#models/user'
import { HttpContext } from '@adonisjs/core/http'
export default class SessionController {
async store({ request, auth, response }: HttpContext) {
/**
* ステップ1:リクエストボディから資格情報を取得します。
*/
const { email, password } = request.only(['email', 'password'])
/**
* ステップ2:資格情報を検証します。
*/
const user = await User.verifyCredentials(email, password)
/**
* ステップ3:ユーザーをログインさせます。
*/
await auth.use('web').login(user)
/**
* ステップ4:保護されたルートにリダイレクトします。
*/
response.redirect('/dashboard')
}
}

ルートの保護

authミドルウェアを使用して、未認証のユーザーからルートを保護できます。このミドルウェアは、名前付きミドルウェアコレクションのstart/kernel.tsファイル内で登録されます。

import router from '@adonisjs/core/services/router'
export const middleware = router.named({
auth: () => import('#middleware/auth_middleware')
})

未認証のユーザーから保護したいルートにauthミドルウェアを適用します。

import { middleware } from '#start/kernel'
import router from '@adonisjs/core/services/router'
router
.get('dashboard', () => {})
.use(middleware.auth())

デフォルトでは、authミドルウェアはdefaultガード(設定ファイルで定義されている)を使用してユーザーを認証します。ただし、authミドルウェアを割り当てる際にガードの配列を指定することもできます。

次の例では、authミドルウェアはwebガードとapiガードを使用してリクエストを認証しようとします。

import { middleware } from '#start/kernel'
import router from '@adonisjs/core/services/router'
router
.get('dashboard', () => {})
.use(
middleware.auth({
guards: ['web', 'api']
})
)

認証例外の処理

認証ミドルウェアは、ユーザーが認証されていない場合にE_UNAUTHORIZED_ACCESSをスローします。この例外は、次のコンテンツネゴシエーションルールを使用して自動的に処理されます。

  • Accept=application/jsonヘッダーを持つリクエストは、messageプロパティを持つエラーの配列を受け取ります。

  • Accept=application/vnd.api+jsonヘッダーを持つリクエストは、JSON API仕様に従ったエラーの配列を受け取ります。

  • サーバーレンダリングされるアプリケーションの場合、ユーザーは/loginページにリダイレクトされます。リダイレクトエンドポイントは、authミドルウェアクラス内で設定できます。

ログイン済みのユーザーへのアクセス

auth.userプロパティを使用して、ログイン済みのユーザーインスタンスにアクセスできます。この値は、authまたはsilent_authミドルウェアを使用するか、auth.authenticateまたはauth.checkメソッドを手動で呼び出した場合にのみ利用できます。

authミドルウェアを使用する
import { middleware } from '#start/kernel'
import router from '@adonisjs/core/services/router'
router
.get('dashboard', async ({ auth }) => {
await auth.user!.getAllMetrics()
})
.use(middleware.auth())
手動でauthenticateメソッドを呼び出す
import { middleware } from '#start/kernel'
import router from '@adonisjs/core/services/router'
router
.get('dashboard', async ({ auth }) => {
/**
* まず、ユーザーを認証します。
*/
await auth.authenticate()
/**
* 次に、ユーザーオブジェクトにアクセスします。
*/
await auth.user!.getAllMetrics()
})

サイレント認証ミドルウェア

silent_authミドルウェアはauthミドルウェアと似ていますが、ユーザーが認証されていない場合に例外を投げません。代わりに、リクエストは通常通り続行されます。

このミドルウェアは、ユーザーを常に認証して何らかのアクションを実行したいが、ユーザーが認証されていない場合にリクエストをブロックしたくない場合に便利です。

このミドルウェアを使用する予定がある場合は、ルーターミドルウェアのリストに登録する必要があります。

start/kernel.ts
import router from '@adonisjs/core/services/router'
router.use([
// ...
() => import('app/middleware/silent_auth')
])

リクエストが認証されているかどうかを確認する

auth.isAuthenticatedフラグを使用して、リクエストが認証されているかどうかを確認できます。認証されたリクエストでは、auth.userの値は常に定義されています。

import { middleware } from '#start/kernel'
import router from '@adonisjs/core/services/router'
router
.get('dashboard', async ({ auth }) => {
if (auth.isAuthenticated) {
await auth.user!.getAllMetrics()
}
})
.use(middleware.auth())

認証されたユーザーを取得するか失敗する

auth.userプロパティに対してnon-null assertion operatorを使用することが好きではない場合は、auth.getUserOrFailメソッドを使用できます。このメソッドは、ユーザーオブジェクトを返すか、E_UNAUTHORIZED_ACCESS例外をスローします。

import { middleware } from '#start/kernel'
import router from '@adonisjs/core/services/router'
router
.get('dashboard', async ({ auth }) => {
const user = auth.getUserOrFail()
await user.getAllMetrics()
})
.use(middleware.auth())

Edgeテンプレート内でユーザーにアクセスする

InitializeAuthMiddlewareは、Edgeテンプレートとctx.authプロパティを共有します。したがって、現在ログインしているユーザーにはauth.userプロパティを使用してアクセスできます。

@if(auth.isAuthenticated)
<p>こんにちは{{ auth.user.email }}さん</p>
@end

保護されていないルートでログイン済みのユーザー情報を取得したい場合は、auth.checkメソッドを使用してユーザーがログインしているかどうかを確認し、auth.userプロパティにアクセスできます。これは、公開ページのウェブサイトヘッダーにログイン済みのユーザー情報を表示する場合に非常に便利です。

{{--
これは公開ページです。したがって、authミドルウェアによって保護されていません。
ただし、ウェブサイトのヘッダーにログイン済みのユーザー情報を表示したい場合があります。
そのために、`auth.check`メソッドを使用してユーザーがログインしているかどうかを
静かにチェックし、ヘッダーにメールアドレスを表示します。
アイデアがわかりますね!
--}}
@eval(await auth.check())
<header>
@if(auth.isAuthenticated)
<p>こんにちは{{ auth.user.email }}さん</p>
@end
</header>

ログアウトの実行

guard.logoutメソッドを使用してユーザーをログアウトできます。ログアウト時には、ユーザーの状態がセッションストアから削除されます。現在アクティブなリメンバートークンも削除されます(リメンバートークンを使用している場合)。

import { middleware } from '#start/kernel'
import router from '@adonisjs/core/services/router'
router
.post('logout', async ({ auth, response }) => {
await auth.use('web').logout()
return response.redirect('/login')
})
.use(middleware.auth())

リメンバーミー機能の使用

リメンバーミー機能は、セッションの有効期限が切れた後に自動的にユーザーをログインさせる機能です。これは、暗号的に安全なトークンを生成し、ユーザーのブラウザにクッキーとして保存することで実現されます。

ユーザーのセッションが期限切れになった後、AdonisJSはリメンバーミークッキーを使用してトークンの有効性を検証し、自動的にユーザーのログインセッションを再作成します。

リメンバーミートークンテーブルの作成

リメンバーミートークンはデータベースに保存されるため、remember_me_tokensテーブルを作成するために新しいマイグレーションを作成する必要があります。

node ace make:migration remember_me_tokens
import { BaseSchema } from '@adonisjs/lucid/schema'
export default class extends BaseSchema {
protected tableName = 'remember_me_tokens'
async up() {
this.schema.createTable(this.tableName, (table) => {
table.increments()
table
.integer('tokenable_id')
.notNullable()
.unsigned()
.references('id')
.inTable('users')
.onDelete('CASCADE')
table.string('hash').notNullable().unique()
table.timestamp('created_at').notNullable()
table.timestamp('updated_at').notNullable()
table.timestamp('expires_at').notNullable()
})
}
async down() {
this.schema.dropTable(this.tableName)
}
}

トークンプロバイダの設定

トークンの読み書きには、DbRememberMeTokensProviderをUserモデルに割り当てる必要があります。

import { BaseModel } from '@adonisjs/lucid/orm'
import { DbRememberMeTokensProvider } from '@adonisjs/auth/session'
export default class User extends BaseModel {
// ...モデルの残りのプロパティ
static rememberMeTokens = DbRememberMeTokensProvider.forModel(User)
}

設定ファイルでリメンバーミートークンを有効にする

最後に、config/auth.tsファイル内のセッションガード設定でuseRememberTokensフラグを有効にします。

import { defineConfig } from '@adonisjs/auth'
import { sessionGuard, sessionUserProvider } from '@adonisjs/auth/session'
const authConfig = defineConfig({
default: 'web',
guards: {
web: sessionGuard({
useRememberMeTokens: true,
rememberMeTokensAge: '2 years',
provider: sessionUserProvider({
model: () => import('#models/user'),
}),
})
},
})
export default authConfig

ログイン時にユーザーを記憶する

セットアップが完了したら、次のようにguard.loginメソッドを使用して、リメンバーミートークンとクッキーを生成できます。

import User from '#models/user'
import { HttpContext } from '@adonisjs/core/http'
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,
/**
* "remember_me"の入力が存在する場合にトークンを生成します
*/
!!request.input('remember_me')
)
response.redirect('/dashboard')
}
}

guestミドルウェアの使用

authパッケージには、ログインしているユーザーが/loginページにアクセスできないようにリダイレクトするために使用できるguestミドルウェアが付属しています。これは、1つのデバイス上の1人のユーザーに対して複数のセッションを作成するのを避けるために行う必要があります。

import router from '@adonisjs/core/services/router'
import { middleware } from '#start/kernel'
router
.get('/login', () => {})
.use(middleware.guest())

デフォルトでは、guestミドルウェアはdefaultガード(設定ファイルで定義されている)を使用してユーザーのログイン状態をチェックします。ただし、guestミドルウェアを割り当てる際にガードの配列を指定することもできます。

router
.get('/login', () => {})
.use(middleware.guest({
guards: ['web', 'admin_web']
}))

最後に、ログインしているユーザーのリダイレクトルートを./app/middleware/guest_middleware.tsファイル内で設定できます。

イベント

利用可能なイベントのリストを表示するには、イベントリファレンスガイドを参照してください。