Session guard

This guide covers session-based authentication in AdonisJS. You will learn:

  • How to configure the session guard
  • How to log users in and out
  • How to protect routes from unauthenticated access
  • How to access the authenticated user
  • How to implement "Remember Me" functionality
  • How to prevent logged-in users from accessing guest-only pages

Overview

The session guard uses the @adonisjs/session package to track logged-in users. When a user logs in, their identifier is stored in the session, and a cookie is sent to the browser. On subsequent requests, the session middleware reads this cookie and restores the authenticated state.

Sessions and cookies have been the standard for web authentication for decades. Use the session guard when building server-rendered applications or SPAs hosted on the same top-level domain as your API (for example, your app at example.com with an API at api.example.com).

Configuring the guard

Authentication guards are defined in config/auth.ts. The following example shows a session guard configuration:

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

The sessionGuard method creates an instance of the SessionGuard class. It accepts a user provider and an optional configuration object for remember me tokens.

The sessionUserProvider method creates an instance of SessionLucidUserProvider, which uses a Lucid model to find users during authentication.

Logging in

Use the auth.use('web').login() method to create a session for a user. The method accepts a User model instance and stores their identifier in the session.

app/controllers/session_controller.ts
import User from '#models/user'
import type { HttpContext } from '@adonisjs/core/http'

export default class SessionController {
  async store({ request, auth, response }: HttpContext) {
    /**
     * Get credentials from request body
     */
    const { email, password } = request.only(['email', 'password'])

    /**
     * Verify credentials using the AuthFinder mixin
     */
    const user = await User.verifyCredentials(email, password)

    /**
     * Create session for the user
     */
    await auth.use('web').login(user)

    /**
     * Redirect to a protected page
     */
    return response.redirect('/dashboard')
  }
}

The auth.use('web') method returns the guard instance configured under the name web in your config/auth.ts file.

Logging out

Use the auth.use('web').logout() method to destroy the user's session. If the user has an active remember me token, it will also be deleted.

app/controllers/session_controller.ts
import type { HttpContext } from '@adonisjs/core/http'

export default class SessionController {
  async destroy({ auth, response }: HttpContext) {
    await auth.use('web').logout()
    return response.redirect('/login')
  }
}

Protecting routes

Use the auth middleware to protect routes from unauthenticated users. The middleware is registered in start/kernel.ts under the named middleware collection:

start/kernel.ts
import router from '@adonisjs/core/services/router'

export const middleware = router.named({
  auth: () => import('#middleware/auth_middleware'),
})

Apply the middleware to routes that require authentication:

start/routes.ts
import { middleware } from '#start/kernel'
import router from '@adonisjs/core/services/router'

router
  .get('dashboard', ({ auth }) => {
    return `Welcome ${auth.user!.fullName}`
  })
  .use(middleware.auth())

By default, the auth middleware authenticates using the default guard from your config. To specify guards explicitly, pass them as an option:

start/routes.ts
import { middleware } from '#start/kernel'
import router from '@adonisjs/core/services/router'

router
  .get('dashboard', ({ auth }) => {
    return `Welcome ${auth.user!.fullName}`
  })
  .use(
    middleware.auth({
      guards: ['web', 'api'],
    })
  )

When multiple guards are specified, authentication succeeds if any of them authenticates the request.

Handling authentication errors

When the auth middleware cannot authenticate a request, it throws the E_UNAUTHORIZED_ACCESS exception. The exception is converted to an HTTP response using content negotiation:

  • Requests with Accept: application/json receive an array of error objects.
  • Requests with Accept: application/vnd.api+json receive errors formatted per the JSON API specification.
  • Server-rendered applications redirect to /login. You can customize this path in app/middleware/auth_middleware.ts.

Accessing the authenticated user

After authentication, the user instance is available via auth.user. This property is populated when using the auth middleware, the silent_auth middleware, or when manually calling auth.authenticate() or auth.check().

start/routes.ts
import { middleware } from '#start/kernel'
import router from '@adonisjs/core/services/router'

router
  .get('dashboard', async ({ auth }) => {
    const user = auth.user!
    return await user.getAllMetrics()
  })
  .use(middleware.auth())

Avoiding non-null assertions

If you prefer not to use the non-null assertion operator (!), use the auth.getUserOrFail() method instead. It returns the user or throws E_UNAUTHORIZED_ACCESS:

start/routes.ts
import { middleware } from '#start/kernel'
import router from '@adonisjs/core/services/router'

router
  .get('dashboard', async ({ auth }) => {
    const user = auth.getUserOrFail()
    return await user.getAllMetrics()
  })
  .use(middleware.auth())

Checking authentication status

Use the auth.isAuthenticated property to check if the current request is authenticated:

start/routes.ts
import { middleware } from '#start/kernel'
import router from '@adonisjs/core/services/router'

router
  .get('dashboard', async ({ auth }) => {
    if (auth.isAuthenticated) {
      return await auth.user!.getAllMetrics()
    }
  })
  .use(middleware.auth())

Silent authentication

The silent_auth middleware works like the auth middleware but doesn't throw an exception when the user is unauthenticated. The request continues normally, allowing you to optionally use authentication data when available.

This is useful for pages that work for both guests and authenticated users, such as a homepage that shows personalized content for logged-in users.

Register the middleware in your router middleware stack:

start/kernel.ts
import router from '@adonisjs/core/services/router'

router.use([
  // ...other middleware
  () => import('#middleware/silent_auth_middleware'),
])

Accessing the user in Edge templates

The initialize auth middleware shares the ctx.auth object with Edge templates. Access the authenticated user via auth.user:

@if(auth.isAuthenticated)
  <p>Hello {{ auth.user.email }}</p>
@end

On public pages that aren't protected by the auth middleware, call auth.check() to attempt authentication before accessing auth.user:

{{-- Check if user is logged in without requiring authentication --}}
@eval(await auth.check())

<header>
  @if(auth.isAuthenticated)
    <p>Hello {{ auth.user.email }}</p>
  @else
    <a href="/login">Sign in</a>
  @end
</header>

Remember me

The "Remember Me" feature keeps users logged in after their session expires by storing a long-lived token in a cookie. When the session expires, AdonisJS uses this token to automatically recreate the session.

Creating the tokens table

Remember me tokens are stored in the database. Create a migration for the tokens table:

node ace make:migration remember_me_tokens
database/migrations/TIMESTAMP_create_remember_me_tokens_table.ts
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)
  }
}

Configuring the token provider

Assign the DbRememberMeTokensProvider to your User model:

app/models/user.ts
import { BaseModel } from '@adonisjs/lucid/orm'
import { DbRememberMeTokensProvider } from '@adonisjs/auth/session'

export default class User extends BaseModel {
  // ...other model properties

  static rememberMeTokens = DbRememberMeTokensProvider.forModel(User)
}

Enabling remember me tokens

Enable the feature in your guard configuration:

config/auth.ts
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

Generating tokens during login

Pass a boolean as the second argument to login() to generate a remember me token:

app/controllers/session_controller.ts
import User from '#models/user'
import type { 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,
      !!request.input('remember_me')
    )

    return response.redirect('/dashboard')
  }
}

Guest middleware

The guest middleware redirects authenticated users away from pages like /login or /register. This prevents users from creating multiple sessions on a single device.

start/routes.ts
import { middleware } from '#start/kernel'
import router from '@adonisjs/core/services/router'

router
  .get('/login', () => {
    // Show login form
  })
  .use(middleware.guest())

By default, the middleware checks authentication using the default guard. To specify guards explicitly:

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

Configure the redirect path for authenticated users in app/middleware/guest_middleware.ts.

Events

The session guard emits events during authentication. See the events reference guide for the complete list.