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
- Ember Simple Auth v6 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 ember-simple-authIf 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 { 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 { 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 { 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, { service } from "@ember/service";
import { handleUnauthorized } from "ember-simple-auth-oidc";
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;
};Further there is the possibilty to user the .well-known endpoint of your authentication backend (specified in the OpenID provider configuration chapter. For this to work you must at least provide a valid host configuration value.
To enforce the autodiscovery, but also providing some keys (autodiscovery will overwrite duplicate keys), set forceAutodiscovery: true.
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.
authEndpointParameters Object
Additional query parameters (e.g. acr_values or audience) that will be
passed on to the authEndpoint. Default is an empty object.
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.