Ember Simple Auth - How to Access the Current User in the Model Hook

Almost any serios web and mobile app has to deal with authentication at some point. Being a security matter this is not always easy to get done. Luckily, in Ember.js there is a great add-on for this. Ember Simple Auth is the standard solution for authorisation and authentication in Ember.

Apart from standard authorisation and authentication you can also extend this ESA's functionality. For example have a current user property that you can use throughout your app.

Let's assume that you need to access the current user in your route. You'll likely need this so that you can retrieve a record or query your backend for some data based on it.

Make the current user available in your application via a service and inject it in your route. Here's how you can do that, following the Ember Simple Auth documentation. There are 2 ways to request the current user:

Using a dedicated end point

// app/services/current-user.js

import Service from '@ember/service'
import { inject as service } from '@ember/service'
import { resolve } from 'rsvp'
import { tracked } from '@glimmer/tracking'

export default class CurrentUserService extends Service {
  @service store
  @service session

  @tracked user

  async fetchUser() {
    if (this.session.isAuthenticated) {
      this.user = await this.store.queryRecord('user', { me: true })
    } else {
      return resolve()
    }
  }
}
// app/adapters/user.js

import ApplicationAdapter from './application'

export default class UserAdapter extends ApplicationAdapter {
  urlForQueryRecord(query) {
    let baseUrl = this.buildURL();

    if (query.me) {
      delete query.me
      return `${baseUrl}/me`
    }

    return baseUrl
  },
}

Loading the user with its id

You can also add some validation logic into the setupController hook. After all the query params are present on the Controller. You don't have access to the params hash here but you know that the query params are properties on the Controller.

// app/services/current-user.js

import Service from '@ember/service'
import { inject as service } from '@ember/service'
import { resolve } from 'rsvp'

export default class CurrentUserService extends Service {
  @service store
  @service session

  @tracked user

  async fetchUser() {
    let userId = this.session.data.authenticated.user_id

    if (userId) {
      this.user = await this.store.findRecord('user', userId)
    } else {
      return resolve()
    }
  }
}

You now need to load the current user and populate the currentUser service user property. To do that you need to call the currentUser service load() method.

// app/routes/application.js

import Route from '@ember/routing/route'
import { inject as service } from '@ember/service'
import ApplicationRouteMixin from 'ember-simple-auth/mixins/application-route-mixin'

export default class ApplicationRoute extends Route.extend(ApplicationRouteMixin) {
  @service currentUser

  beforeModel() {
    return this._loadCurrentUser()
  }

  sessionAuthenticated() {
    super.sessionAuthenticated(...arguments)
    this._loadCurrentUser()
  }

  _loadCurrentUser() {
    return this.currentUser.load().catch(() => this.session.invalidate())
  }
}

The current user in action

And now you have access to the current user via the currentUser service you created. Here's an example of how you could use this. Given you have a list of bookings available at /bookings, you need a way to display all the bookings that belong to the current user at /bookings/me. Easy enough. Here's how you can do this:

// app/routes/bookings/me.js

import Route from '@ember/routing/route'
import { inject as service } from '@ember/service'

export default class MeRoute extends Route {
  @service currentUser

  model() {
    let userId = this.currentUser.user.id
    return this.store.query('booking', { user_id: userId })
  }
}

There are many use cases and needs for the current user and it's not always this simple to solve your problem. But at least this gives you a starting point if you need to get more creative.

Subscribe below to get future posts from Sabin