A Ember Simple Auth addon which implements the OpenID Connect Authorization Code Flow.
- Ember.js v4.12 or above
- Ember CLI v4.12 or above
- Node.js v18 or above
Note: The addon uses Proxy in its implementation, if IE browser support is necessary, a polyfill needs to be provided.
$ ember install ember-simple-auth-oidc
If you're upgrading from 3.x to 4.x see the upgrade guide.
To use the oidc authorization code flow the following elements need to be added to the Ember application.
The login / authentication route (for example the Ember Simple Auth default /login
)
needs to extend from the OIDCAuthenticationRoute
, which handles the authentication
procedure. In case the user is already authenticated, the transition is aborted.
// app/routes/login.js
import OIDCAuthenticationRoute from "ember-simple-auth-oidc/routes/oidc-authentication";
export default class LoginRoute extends OIDCAuthenticationRoute {}
Authenticated routes need to call session.requireAuthentication
in their
respective beforeModel
, to ensure that unauthenticated transitions are
prevented and redirected to the authentication route. It's recommended to
await the beforeModel
hook, to make sure authentication is handled before
other API calls are triggered (which might lead to 401
responses, potentially
causing redirect loops).
// app/routes/protected.js
import Route from "@ember/routing/route";
import { inject as service } from "@ember/service";
export default class ProtectedRoute extends Route {
@service session;
async beforeModel(transition) {
await this.session.requireAuthentication(transition, "login");
}
}
To include authorization info in all Ember Data requests override headers
in
the application adapter and include session.headers
alongside any other
necessary headers. By extending the application adapter from either of the
provided OIDCJSONAPIAdapter
or OIDCRESTAdapter
, the access_token
is
refreshed before Ember Data requests, if necessary. Both the OIDCJSONAPIAdapter
and the OIDCRESTAdapter
also provide default headers with the authorization
header included.
// app/adapters/application.js
import { inject as service } from "@ember/service";
import OIDCJSONAPIAdapter from "ember-simple-auth-oidc/adapters/oidc-json-api-adapter";
export default class ApplicationAdapter extends OIDCJSONAPIAdapter {
@service session;
get headers() {
return { ...this.session.headers, "Content-Language": "en-us" };
}
}
ember-simple-auth-oidc
also provides a middleware which handles authorization
and unauthorization on the apollo service provided by ember-apollo-client
.
Simply, wrap the http link in apolloMiddleware
like so:
// app/services/apollo.js
import { inject as service } from "@ember/service";
import ApolloService from "ember-apollo-client/services/apollo";
import { apolloMiddleware } from "ember-simple-auth-oidc";
export default class CustomApolloService extends ApolloService {
@service session;
link() {
const httpLink = super.link();
return apolloMiddleware(httpLink, this.session);
}
}
The provided adapters and the apollo middleware already handle authorization and
unauthorized requests properly. If you want the same behaviour for other request
services as well, you can use the handleUnauthorized
function and the
refreshAuthentication.perform
method on the session. The following snippet
shows an example of a custom fetch service with proper authentication handling:
import Service, { inject as service } from "@ember/service";
import { handleUnauthorized } from "ember-simple-auth-oidc";
import fetch from "fetch";
export default class FetchService extends Service {
@service session;
async fetch(url) {
await this.session.refreshAuthentication.perform();
const response = await fetch(url, { headers: this.session.headers });
if (!response.ok && response.status === 401) {
handleUnauthorized(this.session);
}
return response;
}
}
Ember Simple Auth encourages the manual setup of the session service in the beforeModel
of the
application route, starting with version 4.1.0.
The relevant changes are described in their upgrade to v4 guide.
There are two ways to invalidate (logout) the current session:
session.invalidate();
The session invalidate
method ends the current ember-simple-auth session and therefore performs a
logout on the ember application. Note that the session on the authorization server is not invalidated
this way and a new token/session can simply be obtained when doing the authentication process again.
session.singleLogout();
The session singleLogout
method will invalidate the current ember-simple-auth session and after that
call the end-session
endpoint of the authorization server. This will result in a logout of the
ember application and additionally invalidate the session on the authorization server which will logout
the user of all applications using this authorization server!
The addon can be configured in the project's environment.js
file with the key ember-simple-auth-oidc
.
A minimal configuration includes the following options:
// config/environment.js
module.exports = function (environment) {
let ENV = {
// ...
"ember-simple-auth-oidc": {
host: "http://authorization.server/openid",
clientId: "test",
authEndpoint: "/authorize",
tokenEndpoint: "/token",
userinfoEndpoint: "/userinfo",
},
// ...
};
return ENV;
};
Here is a complete list of all possible config options:
host <String>
A relative or absolute URI of the authorization server.
clientId <String>
The oidc client identifier valid at the authorization server.
authEndpoint <String>
Authorization endpoint at the authorization server. This can be a path which
will be appended to host
or an absolute URL.
tokenEndpoint <String>
Token endpoint at the authorization server. This can be a path which will be
appended to host
or an absolute URL.
endSessionEndpoint <String>
(optional)
End session endpoint endpoint at the authorization server. This can be a path
which will be appended to host
or an absolute URL.
userinfoEndpoint <String>
Userinfo endpoint endpoint at the authorization server. This can be a path
which will be appended to host
or an absolute URL.
afterLogoutUri <String>
(optional)
A relative or absolute URI to which will be redirected after logout / end session.
scope <String>
(optional)
The oidc scope value. Default is "openid"
.
expiresIn <Number>
(optional)
Milliseconds after which the token expires. This is only a fallback value if the authorization server does not return a expires_in
value. Default is 3600000
(1h).
refreshLeeway <Number>
(optional)
Milliseconds before expire time at which the token is refreshed. Default is 30000
(30s).
tokenPropertyName <String>
(optional)
Name of the property which holds the token in a successful authenticate request. Default is "access_token"
.
authHeaderName <String>
(optional)
Name of the authentication header holding the token used in requests. Default is "Authorization"
.
authPrefix <String>
(optional)
Prefix of the authentication token. Default is "Bearer"
.
loginHintName <String>
(optional)
Name of the login_hint
query paramter which is being forwarded to the authorization server if it is present. This option allows overriding the default name login_hint
.
amountOfRetries <Number>
(optional)
Amount of retries should be made if the request to fetch a new token fails. Default is 3
.
retryTimeout <Number>
(optional)
Timeout in milliseconds between each retry if a token refresh should fail. Default is 3000
.
enablePkce <Boolean>
(optional)
Enables PKCE mechanism to provide additional protection during code to token exchanges. Default is false
.
unauthorizedRequestRedirectTimeout <Number>
(optional)
Debounce timeout for redirection after (multiple) 401
responses are received to prevent redirect loops (at the cost of a small delay). Set to 0
to disable debouncing. Default is 1000
.
This project is licensed under the LGPL-3.0-or-later license.