Ember Simple Auth - how to overwrite the default transition after authentication

Ember Simple Auth is a library for implementing authentication and authorization in Ember.js applications. You're using it in your Ember app and you're migrating away from mixins, but you're not sure how you can overwrite the default transitions. You want to get it right the first time, don't you?

Here's what we're going to cover today:

  1. How to protect your routes
  2. Getting rid of the Ember Simple Auth mixins
  3. How you can overwrite the default transitions defined in Ember Simple Auth

If you already use Ember Simple Auth in your app but are still using the mixins, this guide is for you. I will help you set it up in a way you can get rid of the mixins and resolve all the deprecation warnings that come with them.

With Ember Simple Auth, you can define where you want to transition to, after logging in. Let's look at how you can protect the routes that need authentication. We'll also explore how to redirect the user to different routes when they log in or out.

Use a protected route, that wraps all routes which need authentication

Here's what you need to do:

1. In the router.js file, wrap all your routes that need authentication within a protected route:

// app/router.js

this.route('login')
this.route('logout')

this.route('protected', { path: '/' }, function () {
  this.route('todos')
  this.route('projects')
})

Note: defining the { path: '/' } will ensure that the protected path does not appear in the URL.

2. Create a session service that extends the Ember Simple Auth session service:

// app/services/session.js

import { service } from '@ember/service'

import BaseSessionService from 'ember-simple-auth/services/session'

export default class SessionService extends BaseSessionService {
  prohibitAuthentication(route = 'protected') {
    return super.prohibitAuthentication(route)
  }

  handleAuthentication() {}
}

Let's break down what we did here:

First, we defined the prohibitAuthentication() method to pass our protected route. According to the documentation, here's what the prohibitAuthentication method does:

Checks whether the session is authenticated. If it is, it transitions to the specified route or invokes the specified callback.

It means that we can use the prohibitAuthentication() method on public routes. For instance, the login and logout routes.

// app/login/route.js

import Route from '@ember/routing/route'

import { service } from '@ember/service'

export default class LoginRoute extends Route {
  @service session

  beforeModel() {
    this.session.prohibitAuthentication()
  }
}

Second, we overwrote the handleAuthentication() method, to prevent any default transitions.

3. Create the protected route and protect your routes

// app/protected/route.js

import Route from '@ember/routing/route'

import { service } from '@ember/service'

export default class ProtectedRoute extends Route {
  @service session

  beforeModel(transition) {
    this.session.requireAuthentication(transition, 'login')
  }
}

According to the documentation, here's what the requireAuthentication method does:

Checks whether the session is authenticated. If it is not, it transitions to the specified route or invokes the specified callback.

It is the opposite of the prohibitAuthentication() method, the difference being the following:

  • prohibitAuthentication -> transitions if the session is authenticated

  • requireAuthentication -> transitions if the session is not authenticated

4. Add your redirection after authentication logic

We can now add the authentication logic, right where we authenticate the session. Here's what this can look like:

<!-- app/components/login.hbs -->

<form {{on 'submit' (perform this.authenticateTask)}} ...attributes>
  <input type='email' value={{this.email}} />
  <input type='password' value={{this.password}} />

  <button type='submit'>Login</button>
</form>
// app/components/login.js

import Component from '@glimmer/component'
import { tracked } from '@glimmer/tracking'
import { task } from 'ember-concurrency'
import { service } from '@ember/service'

export default class LoginComponent extends Component {
  @service session
  @service router

  @tracked email
  @tracked password

  authenticateTask = task(this, { drop: true }, async () => {
    let { email, password } = this;

    await this.session.authenticate('authenticator:custom', { email, password })

    // here is where we can define our custom redirection after authentication
    this.router.transitionTo('todos')
  }
}

Note: authenticator:custom means that you've already defined a custom authenticator under app/authenticators/custom.js. See Ember Simple Auth - BaseAuthenticator for more details on how you can create custom authenticators.

Next steps - migrate away from using Ember Simple Auth mixins

Using this approach you can get rid of the following mixins:

If you are still using the Ember Simple Auth mixins, stop right there. Follow the steps above to migrate away from them and get rid of all deprecation warnings.

Subscribe below to get future posts from Sabin