• Stars
    star
    1,926
  • Rank 24,063 (Top 0.5 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created about 11 years ago
  • Updated 3 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A library for implementing authentication/authorization in Ember.js applications.

Ember Simple Auth API docs

CI

Discord

  • Ember Simple Auth supports all Ember.js versions starting with 3.28.
  • Doesn't support IE11
  • Node >=16 is required
  • Supports Embroider see our ember-try scenario and test app for guidance.

Ember Simple Auth

Logo

Ember Simple Auth is a lightweight library for implementing authentication/ authorization with Ember.js applications. It has minimal requirements with respect to application structure, routes etc. With its pluggable strategies it can support all kinds of authentication and authorization mechanisms.

Table of Contents

Basic Information

Usage

Core Feature Guides

Other Guides

Other Resources

What does it do?

  • it maintains a client side session and synchronizes its state across multiple tabs/windows of the application
  • it authenticates the session against the application's own server, external providers like Facebook etc.
  • it is easily customizable and extensible

How does it work?

Ember Simple Auth consists of 3 main building blocks - the session, a session store and authenticators.

The session service is the main interface to the library. It provides methods for authenticating and invalidating the session as well as for setting and reading session data.

The session store persists the session state so that it survives a page reload. It also synchronizes the session state across multiple tabs or windows of the application so that e.g. a logout in one tab or window also results in a logout in all other tabs or windows of the application.

Authenticators authenticate the session. An application can leverage multiple authenticators to support multiple ways of authentication such as sending credentials to the application's own backend server, Facebook, github etc.

Example App

Ember Simple Auth comes with a test app that implements a complete auth solution including authentication against the application's own server as well as Facebook, authorization of Ember Data requests and error handling. Check out that test app for reference. To start it, run

git clone https://github.com/mainmatter/ember-simple-auth.git
cd ember-simple-auth/packages/test-app
pnpm install && ember serve

and go to http://localhost:4200.

Installation

Installing the library is as easy as:

ember install ember-simple-auth

Upgrading from a pre-3.0 release?

The 3.0 release of ember-simple-auth removes previously deprecated code, introducing some breaking changes, but thankfully there is an v3 upgrade guide.

Upgrading to 4.0 release?

The 4.1 release introduced a session#setup that fixes build issues for typescript and embroider users, due to ESA using initializers. Consult with the guide in order to fix them as well as prepare yourself for v5 release which will make it required. v4 upgrade guide.

Walkthrough

Once the library is installed, the session service can be injected wherever needed in the application. In order to display login/logout buttons depending on the current session state, inject the service into the respective controller or component and query its isAuthenticated property in the template:

// app/controllers/application.js
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';

export default class ApplicationController extends Controller {
  @service session;

  …
}
{{!-- app/templates/application.hbs --}}
<div class="menu">
  …
  {{#if this.session.isAuthenticated}}
    <a {{on "click" this.invalidateSession}}>Logout</a>
  {{else}}
    {{#link-to 'login'}}Login{{/link-to}}
  {{/if}}
</div>
<div class="main">
  {{outlet}}
</div>

In the invalidateSession action call the session service's invalidate method to invalidate the session and log the user out:

// app/controllers/application.js
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import { action } from "@ember/object";

export default class ApplicationController extends Controller {
  @service session;

  …

  @action
  invalidateSession() {
    this.session.invalidate();
  }
}

For authenticating the session, the session service provides the authenticate method that takes the name of the authenticator to use as well as other arguments depending on specific authenticator used. To define an authenticator, add a new file in app/authenticators and extend one of the authenticators the library comes with, e.g.:

// app/authenticators/oauth2.js
import OAuth2PasswordGrant from 'ember-simple-auth/authenticators/oauth2-password-grant';

export default class OAuth2Authenticator extends OAuth2PasswordGrant {}

With that authenticator and a login form like

{{!-- app/templates/login.hbs --}}
<form {{on "submit" this.authenticate}}>
  <label for="identification">Login</label>
  <input id='identification' placeholder="Enter Login" value={{this.identification}} {{on "change" this.updateIdentification}}>
  <label for="password">Password</label>
  <input id='password' placeholder="Enter Password" value={{this.password}} {{on "change" this.updatePassword}}>
  <button type="submit">Login</button>
  {{#if this.errorMessage}}
    <p>{{this.errorMessage}}</p>
  {{/if}}
</form>

the session can be authenticated with the session service's authenticate method:

// app/controllers/login.js
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import { action } from "@ember/object";
import { tracked } from "@glimmer/tracking";

export default class LoginController extends Controller {
  @tracked errorMessage;
  @service session;

  @action
  async authenticate(e) {
    e.preventDefault();
    let { identification, password } = this;
    try {
      await this.session.authenticate('authenticator:oauth2', identification, password);
    } catch(error) {
      this.errorMessage = error.error || error;
    }

    if (this.session.isAuthenticated) {
      // What to do with all this success?
    }
  }

  @action
  updateIdentification(e) {
    this.identification = e.target.value;
  }

  @action
  updatePassword(e) {
    this.password = e.target.value;
  }
}

To make a route in the application accessible only when the session is authenticated, call the session service's requireAuthentication method in the respective route's beforeModel method:

// app/routes/authenticated.js
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';

export default class AuthenticatedRoute extends Route {
  @service session;

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

This will make the route (and all of its subroutes) transition to the login route if the session is not authenticated. Add the login route in the router like this:

// app/router.js
Router.map(function() {
  this.route('login');
});

It is recommended to nest all of an application's routes that require the session to be authenticated under a common parent route:

// app/router.js
Router.map(function() {
  this.route('login');
  this.route('authenticated', { path: '' }, function() {
    // all routes that require the session to be authenticated
  });
});

To prevent a route from being accessed when the session is authenticated (which makes sense for login and registration routes for example), call the session service's prohibitAuthentication method in the respective route's beforeModel method:

// app/routes/login.js
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';

export default class LoginRoute extends Route {
  @service session;

  beforeModel(transition) {
    this.get('session').prohibitAuthentication('index');
  }
}

The session service also provides the handleAuthentication and handleInvalidation methods for handling authentication and invalidation of the session (which not only happens when the user submits the login form or clicks the logout button but also when the session is authenticated or invalidated in another tab or window of the application). The handleAuthentication method will transition to a configurable route while the handleInvalidation method will reload the page to clear all potentially sensitive data from memory. In order to customize those behaviours, these methods can be overridden when the application defines its own session service that extends the one provided by Ember Simple Auth.

To add authorization information to requests, you can use the session service to check if the session is authenticated and access authentication/authorization data, e.g. a token:

// app/adapters/application.js
import JSONAPIAdapter from '@ember-data/adapter/json-api';
import { computed } from '@ember/object';
import { inject as service } from '@ember/service';

export default class ApplicationAdapter extends JSONAPIAdapter {
  @service session;

  @computed('session.{data.authenticated.access_token,isAuthenticated}')
  get headers() {
    let headers = {};
    if (this.session.isAuthenticated) {
      // OAuth 2
      headers['Authorization'] = `Bearer ${this.session.data.authenticated.access_token}`;
    }

    return headers;
  }
}

The Session Service

The session service is the main interface to the library. It defines the authenticate, invalidate and authorize methods as well as the session events as shown above.

It also provides the isAuthenticated as well as the data properties. The latter can be used to get and set the session data. While the special authenticated section in the session data contains the data that was acquired by the authenticator when it authenticated the session and is read-only, all other session data can be written and will also remain in the session after it is invalidated. It can be used to store all kinds of client side data that needs to be persisted and synchronized across tabs and windows, e.g.:

this.session.set('data.locale', 'de');

Authenticators

Authenticators implement the concrete steps necessary to authenticate the session. An application can leverage several authenticators for different kinds of authentication mechanisms (e.g. the application's own backend server, external authentication providers like Facebook etc.) while the session is only ever authenticated with one authenticator at a time. The authenticator to use is chosen when authentication is triggered via the name it is registered with in the Ember container:

this.session.authenticate('authenticator:some');

Ember Simple Auth comes with 4 authenticators:

To use any of these authenticators in an application, define a new authenticator in app/authenticators, extend if from the Ember Simple Auth authenticator

// app/authenticators/oauth2.js
import OAuth2PasswordGrantAuthenticator from 'ember-simple-auth/authenticators/oauth2-password-grant';

export default class OAuth2Authenticator extends OAuth2PasswordGrantAuthenticator {}

and invoke the session service's authenticate method with the respective name, specifying more arguments as needed by the authenticator:

this.session.authenticate('authenticator:some', data);

Customizing an Authenticator

Authenticators are easily customized by setting the respective properties, e.g.:

// app/authenticators/oauth2.js
import OAuth2PasswordGrantAuthenticator from 'ember-simple-auth/authenticators/oauth2-password-grant';

export default class OAuth2Authenticator extends OAuth2PasswordGrantAuthenticator {
  serverTokenEndpoint = '/custom/endpoint';
}

Implementing a custom Authenticator

Besides extending one of the predefined authenticators, an application can also implement fully custom authenticators. In order to do that, extend the abstract base authenticator that Ember Simple Auth comes with and override the authenticate, restore and (optionally) invalidate methods:

// app/authenticators/custom.js
import Base from 'ember-simple-auth/authenticators/base';

export default class CustomAuthenticator extends Base {
  restore(data) {
    …
  }

  authenticate(options) {
    …
  }

  invalidate(data) {
    …
  }
}

Session Stores

Ember Simple Auth persists the session state via a session store so it survives page reloads. There is only one store per application that can be defined in app/session-stores/application.js:

// app/session-stores/application.js
import Cookie from 'ember-simple-auth/session-stores/cookie';

export default class ApplicationSessionStore extends Cookie {}

If the application does not define a session store, the adaptive store which uses localStorage if that is available or a cookie if it is not, will be used by default. To customize the adaptive store, define a custom store in app/session-stores/application.js that extends it and overrides the properties to customize.

Store Types

Ember Simple Auth comes with 4 stores:

Adaptive Store

The adaptive store stores its data in the browser's localStorage if that is available or in a cookie if it is not; this is the default store.

localStorage Store

The localStorage store stores its data in the browser's localStorage. This is used by the adaptive store if localStorage is available.

Cookie Store

The Cookie store stores its data in a cookie. This is used by the adaptive store if localStorage is not available. This store must be used when the application uses FastBoot.

sessionStorage Store

The sessionStorage store stores its data in the browser's sessionStorage. See the Web Storage docs for details on sessionStorage and localStorage. caniuse has up-to-date information on browser support of sessionStorage and localStorage.

Ephemeral Store

The ephemeral store stores its data in memory and thus is not actually persistent. This store is mainly useful for testing. Also the ephemeral store cannot keep multiple tabs or windows in sync as tabs/windows cannot share memory.

Customizing the Store

The session store is easily customized by setting the respective properties, e.g.:

// app/session-stores/application.js
import AdaptiveStore from 'ember-simple-auth/session-stores/adaptive';

export default class ApplicationSessionStore extends AdaptiveStore {
  cookieName = 'my-apps-session-cookie';
}

Implementing a custom Store

Besides using one of the predefined session stores, an application can also implement fully custom stores. In order to do that, extend the abstract base session store that Ember Simple Auth comes with and implement the persist, restore and clear methods:

// app/session-stores/application.js
import Base from 'ember-simple-auth/session-stores/base';

export default class ApplicationSessionStore extends Base {
  persist() {
    …
  }

  restore() {
    …
  }
}

FastBoot

Ember Simple Auth works with FastBoot out of the box as long as the Cookie session store is being used. In order to enable the cookie store, define it as the application store:

// app/session-stores/application.js
import CookieStore from 'ember-simple-auth/session-stores/cookie';

export default class ApplicationSessionStore extends CookieStore {}

If you are using the OAuth2PasswordGrantAuthenticator, or DeviseAuthenticator, you must add node-fetch to your list of FastBoot whitelisted dependencies in package.json:

{
  "fastbootDependencies": [
    "node-fetch"
  ]
}

Engines

Ember Simple Auth works with engines out of the box. The host app and any engine(s) share the same session service so they can synchronize the authentication status:

// my-engine/addon/routes/index.js
import Application from '@ember/application';
import loadInitializers from 'ember-load-initializers';

class App extends Application {
  …

  engines = {
    'my-engine': {
      dependencies: {
        services: [
          'session'
        ]
      }
    }
  }
});

…

export default App;

The session can then be authenticated or invalidated from the host app or any of the engines and the state will be synchronized via the service.

One thing to be aware of is that if the authentication route is outside of the engine (e.g. in the host app), it is necessary to use the special transitionToExternal method in the engine to transition to it. That can be done by passing a callback instead of a route name to the session service's requireAuthentication method in that case:

// my-engine/addon/routes/index.js
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';

export default class IndexRoute extends Route {
  @service session;

  beforeModel(transition) {
    this.get('session').requireAuthentication(transition, () => this.transitionToExternal('login'));
  },
}

Testing

Ember Simple Auth comes with a set of test helpers that can be used in acceptance tests.

Our helpers use the more modern testing syntax and therefore require ember-cli-qunit 4.2.0 or greater or ember-qunit 3.2.0 or greater.

We provide the following helpers:

  • currentSession() returns the current session.
  • authenticateSession(sessionData) authenticates the session asynchronously; the optional sessionData argument can be used to mock the response of an authentication request, to provide a specific authorization token or user data.
  • invalidateSession() invalidates the session asynchronously.

Which can be used as shown in the following example:

import { module, test } from 'qunit';
import { visit, currentURL } from '@ember/test-helpers';
import { setupApplicationTest } from 'ember-qunit';
import { currentSession, authenticateSession, invalidateSession } from 'ember-simple-auth/test-support';

module('Acceptance | app test', function(hooks) {
  setupApplicationTest(hooks);

  test('/login redirects to index if user is alread logged in', async function(assert) {
    await authenticateSession({
      authToken: '12345',
      otherData: 'some-data'
    });
    await visit('/login');

    assert.equal(currentURL(), '/');

    let sessionData = currentSession().get('data.authenticated');
    assert.equal(sessionData.authToken, '12345');
    assert.equal(sessionData.otherData, 'some-data');
  });

  test('/protected redirects to /login if user is not logged in', async function(assert) {
    await invalidateSession();

    await visit('/protected');

    assert.equal(currentURL(), '/login');
  });
});

If you're an ember-mocha user, we can recommend to check out this example from the test suite of ember-simple-auth itself.

Other guides

License

Ember Simple Auth is developed by and Β© Mainmatter GmbH and contributors. It is released under the MIT License.

Ember Simple Auth is not an official part of Ember.js and is not maintained by the Ember.js Core Team.

More Repositories

1

100-exercises-to-learn-rust

A self-paced course to learn Rust, one exercise at a time.
Rust
3,527
star
2

ember-test-selectors

Enabling better element selectors in Ember.js tests
JavaScript
262
star
3

rust-telemetry-workshop

Build a comprehensive toolkit to detect, troubleshoot and resolve issues with Rust applications.
Rust
224
star
4

qunit-dom

High Level DOM Assertions for QUnit
TypeScript
178
star
5

excellent

Source Code analysis gem for Ruby and Rails
Ruby
156
star
6

rails_api_auth

Lightweight Rails Engine that implements the "Resource Owner Password Credentials Grant" OAuth 2.0 flow as well as Facebook authentication
Ruby
139
star
7

highlight

Syntax Higlighting plugin for Ruby on Rails
Ruby
131
star
8

cargo-autoinherit

(Auto)DRY for your Rust dependencies
Rust
124
star
9

ember-cookies

Cookies abstraction for Ember.js that works both in the browser as well as with Fastboot on the server
JavaScript
115
star
10

rust-advanced-testing-workshop

A course to move beyond the built-in Rust testing toolkit.
Rust
95
star
11

ast-workshop

"Abstract Syntax Forestry" workshop for EmberConf 2020
JavaScript
86
star
12

breethe-client

Air Quality Data for Locations around the World
JavaScript
75
star
13

ember-cli-simple-auth

Ember CLI Adon for the Ember Simple Auth library
JavaScript
54
star
14

ember-intl-analyzer

Find missing or unused translations in your Ember.js projects
JavaScript
48
star
15

ember-hbs-minifier

Stripping whitespace out of your Handlebars templates
JavaScript
48
star
16

ember-validated-form-buffer

A validated form buffer that wraps Ember Data models for use in forms.
JavaScript
47
star
17

continue-on-error-comment

GitHub action to add comment when a continue-on-error job fails
JavaScript
41
star
18

breethe-server

Air Quality Data for Locations around the World
Elixir
40
star
19

rust-python-interoperability

A self-paced course to write Python extensions in Rust, one exercise at a time.
Rust
32
star
20

ember-promise-modals

The easy solution for rendering and handling modals in Ember.js apps. Promised.
JavaScript
31
star
21

ember-simple-auth-example

Example project showing how to use Ember Simple Auth with Ember CLI
JavaScript
30
star
22

svelte-promise-modals

Modals in Svelte made easy. Promised.🀞
Svelte
26
star
23

qunit-console-grouper

QUnit plugin that groups console messages by test
JavaScript
22
star
24

ember-cli-simple-auth-devise

Ember CLI Addon for the Ember Simple Auth Devise package
JavaScript
22
star
25

ember-asset-size-action

Comment with the diff for the asset sizes on Pull Request
JavaScript
22
star
26

ember-cli-simple-auth-oauth2

Ember CLI Addon for the Ember Simple Auth OAuth 2.0 package
JavaScript
20
star
27

ember-cli-deploy-webhooks

Ember CLI Deploy plugin for calling webhooks during deployments
JavaScript
19
star
28

rust-workshop-runner

A CLI tool to drive test-driven Rust workshops
Rust
17
star
29

ember-classy-computed

An Ember addon for Class based Computed Properties
JavaScript
16
star
30

mainmatter.com

The source code for https://mainmatter.com
Nunjucks
16
star
31

qunit-dom-codemod

Basic codemod to automatically convert your assertions to qunit-dom assertions
JavaScript
14
star
32

eslint-plugin-ember-concurrency

ESLint plugin for ember-concurrency users
JavaScript
13
star
33

playbook

A book describing the patterns and practices that Mainmatter uses to build lasting products, systematically.
HTML
13
star
34

ember-workshop

The example apps for simplabs' Ember.js Workshop
12
star
35

ember-cli-pixijs

An Ember CLI Addon that wraps pixi.js
JavaScript
12
star
36

emblem-migrator

Migrate Emblem.js to pretty Handlebars files
JavaScript
9
star
37

ember-cli-simple-auth-testing

Ember CLI Addon for the Ember Simple Auth Testing package
JavaScript
9
star
38

ember-cli-simple-auth-cookie-store

Ember CLI Addon for the Ember Simple Auth Cookie Store package
JavaScript
8
star
39

testem-gitlab-reporter

GitLab/JUnit reporter for testem
JavaScript
8
star
40

rails_api_auth-demo

Demo project using the rails_api_auth engine
Ruby
7
star
41

ember-auto-computed

JavaScript
7
star
42

ember-cli-simple-auth-torii

Ember CLI Addon for the Ember Simple Auth Torii package
JavaScript
7
star
43

ember-template-lint-plugin-css-modules

ember-template-lint plugin for ember-css-modules
JavaScript
7
star
44

ember-api-actions

Ember.js addon allowing you to easily implement non-CRUD actions for your Ember Data models
JavaScript
6
star
45

ember-error-route

JavaScript
6
star
46

mainmatter-website-mailer

Mailer for the contact form on mainmatter.com – Cloudflare worker written in Rust
Rust
5
star
47

ember-hotspots

Create interactive prototypes from scratch and design mockups using little code but the full power of the Ember.js ecosystem.
JavaScript
5
star
48

ember-scroll

A sensible default implementation of scrolling for Ember apps, aiming to mimic static site behaviour.
JavaScript
4
star
49

asset-size-reporter

Generic asset size comparison and reporting tool
JavaScript
4
star
50

mocha-diff

Mocha's diff algorithm extracted for anyone to use πŸŽ‰
JavaScript
4
star
51

svelte-workshop-music-player

JavaScript
4
star
52

ember-simple-auth-component

Bower repository for Ember Simple Auth
JavaScript
4
star
53

sheepdog

Sheepdog...herd you async task!
MDX
3
star
54

eslint-plugin-qunit-dom

An ESLint plugin for qunit-dom that automatically fixes the most common issues.
JavaScript
2
star
55

compare-fixture

JavaScript
1
star
56

ember-cli-list-addons

JavaScript
1
star
57

git-workshop

1
star
58

eslint-config-simplabs

ESLint config for all simplabs projects
JavaScript
1
star
59

auto-reveal

JavaScript
1
star
60

auto-reveal-theme-mainmatter

CSS
1
star
61

sveltekit-super-rentals

JavaScript
1
star
62

who-ran-me

Small utillity to check if script was run with npm or yarn
JavaScript
1
star