Ember addon providing an Ember Simple Auth authenticator for ACM/IDM in a semantic.works stack.
To add the ember-acmidm-login addon to your application, execute the following install commands:
ember install @lblod/ember-acmidm-login
npm install ember-simple-auth@6
Make sure you've configured the acmidm-login-service (or any fork) correctly in your project. The addon currently requires the service endpoints to be available under /sessions
.
Configure the following variables via ENV.acmidm
in config/environment.js
. The values must match the config provided to the backend login service.
// config/environment.js
module.exports = function (environment) {
const ENV = {
...,
acmidm: {
clientId: 'your-client-id',
baseUrl: 'https://authenticatie.vlaanderen.be/op/v1/auth',
redirectUrl: 'https://myapp.vlaanderen.be/authorization/callback',
logoutUrl: 'https://authenticatie.vlaanderen.be/op/v1/logout',
scope: 'openid vo profile'
}
};
}
In your application route initialize the Ember Simple Auth session service:
// app/routes/application.js
import Route from '@ember/routing/route';
import { service } from '@ember/service';
export default class ApplicationRoute extends Route {
@service session;
beforeModel() {
return this.session.setup();
}
}
First step is to create a login link/button which redirects to ACM/IDM. The login URL can be constructed as follows in the controller/component where you want to show a login button:
import Controller from '@ember/controller';
import ENV from 'your-app/config/environment';
import buildUrlFromConfig from '@lblod/ember-acmidm-login/utils/build-url-from-config';
export default class LoginController extends Controller {
loginUrl = buildUrlFromConfig(ENV.acmidm);
}
In the template simply add the following:
Next, set up a route and controller to handle the callback from ACM/IDM after authentication. The route must match the path of the redirect URL and will receive an authorization code as query param:
// app/routes/authentication/callback.js
import Route from '@ember/routing/route';
import { service } from '@ember/service';
export default class AuthenticationCallbackRoute extends Route {
@service session;
beforeModel() {
// redirect to index if already authenticated
this.session.prohibitAuthentication('index');
}
model(params) {
this.session.authenticate('authenticator:acm-idm', params.code);
}
}
// app/controllers/authentication/callback.js
import Controller from '@ember/controller';
export default class AuthenticationCallbackController extends Controller {
queryParams = ['code'];
}
The model hook will pass the authorization code to the ACM/IDM authenticator which will on its turn communicate with the backend to log in the user.
Have a look at the Ember Simple Auth documentation to learn how to protect your routes from unauthenticated access.
Create a controller (or component) that injects the session
service and handles a session invalidation action as follows:
// app/controllers/application.js
import Controller from '@ember/controller';
import { service } from '@ember/service';
import { action } from '@ember/object';
export default class ApplicationController extends Controller {
@service session;
@action
async invalidateSession() {
try {
await this.session.invalidate();
}
catch(e) {
// error handling
}
}
}
Next, show a logout button on your page if the user is logged in
Finally, we need to make sure to terminate the session on the ACM/IDM side as well by redirecting the user to a logout URL when the session gets invalidated. To do so, extend Ember Simple Auth's session service to hook into the handleInvalidation
event:
// app/services/session.js
import BaseSessionService from 'ember-simple-auth/services/session';
import ENV from 'your-app/config/environment';
export default class SessionService extends BaseSessionService {
handleInvalidation() {
const logoutUrl = ENV.acmidm.logoutUrl;
super.handleInvalidation(logoutUrl);
}
}
Extend Ember Simple Auth's session service to hook into the handleAuthentication
event and request some user info from the backend. For example:
// app/services/session.js
import { service } from '@ember/service';
import BaseSessionService from 'ember-simple-auth/services/session';
export default class SessionService extends BaseSessionService {
@service userInfo;
handleAuthentication(routeAfterAuthentication) {
super.handleAuthentication(routeAfterAuthentication);
// do anything you want to load user info from the backend
this.userInfo.load();
}
}
Sometimes an app needs to allow users to switch their session (e.g. to another administrative unit they belong to). In this scenario, the session in our application will be ended and the user is redirected to an URL on ACM/IDM where he can execute the switch. ACM/IDM will then call the callback URL and the user gets logged in with his new identity as in the regular login flow.
First, add a switchUrl
to the acmidm
config in config/environment.js
:
// config/environment.js
ENV.acmidm = {
... // other config
switchRedirectUrl: 'url-to-your-callback-route'
};
Next, set up a route authentication.switch
which will redirect the user to the switch URL of ACM/IDM.
// app/routes/authentication/switch.js
import Route from '@ember/routing/route';
import { service } from '@ember/service';
import ENV from 'your-app/config/environment';
import buildSwitchUrlFromConfig from '@lblod/ember-acmidm-login/utils/build-switch-url-from-config';
export default class AuthenticationSwitchRoute extends Route {
@service router;
@service session;
async beforeModel(transition) {
// ensure the user is logged in
this.session.requireAuthentication(transition, 'login');
try {
await this.session.invalidate();
const switchURL = buildSwitchUrlFromConfig(ENV.acmidm);
window.location.replace(switchURL);
} catch (error) {
// Handle error
}
}
}
Finally, add a link to the switch-route in your template
The following options can be configured via the ENV.acmidm
object in config/environment.js
. It's important that the values match the configuration of the backend login service.
The following options are required:
baseUrl
: ACM/IDM auth endpoint, typically https://authenticatie.vlaanderen.be/op/v1/authclientId
: the client ID of this applicationredirectUrl
: URL of the page ACM/IDM needs to redirect to after authentication. You'll need to set up a route to capture this. E.g. https://myapp.vlaanderen.be/authorization/callbackscope
: Space-separated string of scopes ACM/IDM must grant access for. E.g.'openid vo profile'
The following options can be optionally provided:
logoutUrl
: ACM/IDM logout endpoint, typically https://authenticatie.vlaanderen.be/op/v1/logoutswitchRedirectUrl
: URL of the page ACM/IDM needs to redirect to after switching. You'll need to set up a route to capture this. Typically the same value as forredirectUrl
can be used.