You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Ember Simple Auth supports all Ember.js versions starting with 1.12.
Ember Simple Auth
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.
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 authorizes requests to backend servers
it is easily customizable and extensible
How does it work?
Ember Simple Auth consists of 4 main building blocks - the session, a
session store, authenticators and (optionally) authorizers.
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.
Authorizers use the data retrieved by an authenticator and stored in the
session to generate authorization data that can be injected into outgoing
requests such as Ember Data requests.
Example App
Ember Simple Auth comes with a
dummy 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 dummy app for reference. To
start it, run
git clone https://github.com/simplabs/ember-simple-auth.git cd ember-simple-auth yarn install && ember serve
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:
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.:
The session service also provides the
authenticationSucceeded
and
invalidationSucceeded
events that are triggered whenever the session is successfully authenticated
or invalidated (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). To have these
events handled automatically, simply mix
ApplicationRouteMixin
into the application route:
The ApplicationRouteMixin automatically maps the session events to the
sessionAuthenticated
and
sessionInvalidated
methods it implements. The sessionAuthenticated method will transition to a
configurable route while the sessionInvalidated method will reload the page
to clear all potentially sensitive data from memory.
To make a route in the application accessible only when the session is
authenticated, mix the
AuthenticatedRouteMixin
into the respective route:
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:
The route to transition to if the session is not authenticated can also be
overridden
to be another one than 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), mix the
UnauthenticatedRouteMixin
into the respective route.
In order to add authorization information to outgoing API requests the
application can define an authorizer. To do so, add a new file to
app/authorizers, e.g.:
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.get('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:
To use any of these authenticators in an application, define a new
authenticator in app/authenticators, extend if from the Ember Simple Auth
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:
Authorizers use the session data acquired by the authenticator to construct
authorization data that can be injected into outgoing network requests. As
Deprecation warning: Authorizers are deprecated
the authorizer depends on the data that the authenticator acquires,
authorizers and authenticators have to fit together.
and invoke the session service's authorize method with the respective name:
{
// Use authorization data
});">this.get('session').authorize('authorizer:some',(/*authorization data*/)=>{ // Use authorization data });
Unlike in previous versions of Ember Simple Auth, authorization will not
happen automatically for all requests the application issues anymore but has
to be initiated explicitly via the service.
When using Ember Data you can mix the DataAdapterMixin in the application
adapter to automatically authorize all API requests:
Besides extending one of the predefined authorizers, an application can also
implement fully custom authorizers. In order to do that, extend the
abstract base authorizer
that Ember Simple Auth comes with and override the
authorize
method:
Authorizers and the session service's authorize method are deprecated and
will be removed from Ember Simple Auth 2.0. The concept seemed like a good idea
in the early days of Ember Simple Auth, but proved to provide limited value for
the added complexity. To replace authorizers in an application, simply get the
session data from the session service and inject it where needed.
In most cases, authorizers are used with Ember Data adapters (refer to the
Ember Guides
for details on adapters). Replacing authorizers in these scenarios is
straightforward.
Sending the Client ID as Base64 Encoded in the Authorization Header was against the spec and caused
incorrect behavior with OAuth2 Servers that had implemented the spec properly.
To change this behavior set sendClientIdAsQueryParam to true, and the client id will be correctly
sent as a query parameter. Leaving it set to false (currently default) will result in a deprecation
notice until the next major version.
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:
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.:
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:
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:
Ember Simple Auth comes with a set of test helpers that can be used in
acceptance tests.
ember-cli-qunit 4.2.0 and greater or ember-qunit 3.2.0 and greater
If your app is using ember-cli-qunit4.2.0 or
greater or ember-qunit 3.2.0 or greater,
you may want to migrate to the more modern testing
syntax. In that
case, helpers can be imported from the ember-simple-auth addon namespace.
The new-style helpers have the following function signatures:
currentSession() returns the current session.
authenticateSession(sessionData) authenticates the session asynchronously;
the optional sessionData argument can be used to mock an authenticator
response (e.g. a token or user).
invalidateSession() invalidates the session asynchronously.
New tests using the async authenticateSession helper will look like this:
module('Acceptance | super secret url',function(hooks){ setupApplicationTest(hooks);
test('authenticated users can visit /super-secret-url',asyncfunction(assert){ awaitauthenticateSession({ userId: 1, otherData: 'some-data' }); awaitvisit('/super-secret-url'); assert.equal(currentURL(),'/super-secret-url','user is on super-secret-url'); }); });
ember-cli-qunit 4.1.0 and earlier
For apps using earlier versions of ember-cli-qunit, you can use the
test helpers with the following signature:
currentSession(this.application): returns the current session of your test application.
authenticateSession(this.application, sessionData): authenticates the session; the
optional sessionData argument can be used to mock an authenticator
response - e.g. a token.
invalidateSession(this.application): invalidates the current session in your test application.
For existing apps, the test helpers are merged into your application's namespace,
and can be imported from the helpers/ember-simple-auth module like this:
The test helpers used in apps using ember-cli-qunit 4.1.0 and earlier all require access to the test application instance.
An application instance is automatically created for you once you use the moduleForAcceptance test helper
that is provided in the acceptance test blueprint.
The app instance created through moduleForAcceptance is available as this.application in your test cases:
/tests/helpers/module-for-acceptance';
// creates and destroys a test application instance before / after each test case
moduleForAcceptance('Acceptance | authentication');
test('user is authenticating', function(assert) {
// returns the instance of your test application
let app = this.application;
});">importmoduleForAcceptancefrom'/tests/helpers/module-for-acceptance';
// creates and destroys a test application instance before / after each test case moduleForAcceptance('Acceptance | authentication');
test('user is authenticating',function(assert){ // returns the instance of your test application letapp=this.application; });
Pass in your application instance as a first parameter to the test helper functions to
get a handle on your application's session store in your subsequent test cases.
Here is a full example of how an acceptance test might look like if your test suite is leveraging ember-qunit:
{
assert.equal(currentURL(), '/login');
assert.notOk(currentSession(this.application).get('isAuthent icated'), 'the user is yet unauthenticated');
// this will authenticate the current session of the test application
authenticateSession(this.application, { token: 'abcdDEF', token_type: 'Bearer' });
andThen(() => {
assert.ok(currentSession(this.application).get('isAuthentica ted'), 'the user is authenticated');
assert.deepEqual(currentSession(this.application).get('data. authenticated'), {
authenticator: 'authenticator:test',
token: 'abcdDEF',
token_type: 'Bearer'
});
});
});
});">importEmberfrom'ember'; import{test}from'qunit'; importmoduleForAcceptancefrom'simple-tests/tests/helpers/module-for-acceptance'; import{currentSession,authenticateSession}from'simple-tests/tests/helpers/ember-simple-auth';
test('user is authenticating',function(assert){ visit('/login');
andThen(()=>{ assert.equal(currentURL(),'/login'); assert.notOk(currentSession(this.application).get('isAuthenticated'),'the user is yet unauthenticated');
// this will authenticate the current session of the test application authenticateSession(this.application,{token: 'abcdDEF',token_type: 'Bearer'});
andThen(()=>{ assert.ok(currentSession(this.application).get('isAuthenticated'),'the user is authenticated'); assert.deepEqual(currentSession(this.application).get('data.authenticated'),{ authenticator: 'authenticator:test', token: 'abcdDEF', token_type: 'Bearer' }); }); }); });