Yivi frontend JavaScript packages
The yivi-frontend-packages
repository is a set of
related JavaScript packages that together form a Javascript frontend client to the
irma server
. The packages contain a state
machine package yivi-core
to which several plugin packages can be added to achieve Yivi support
for your application. We also provide a wrapper package yivi-frontend
that combines yivi-core
with some of the
plugins in a bundle. With this wrapper package you have an easy starting point for handling sessions using an embedded
web element or using a popup overlay in the browser.
Using the default styling, the browser version will look like this:
All packages are published on npm in @privacybydesign
scoped packages.
Yivi core
This package contains the state machine
for implementing Yivi flows. yivi-core
itself does not provide any real functionality. Plugins can be registered at the
state machine and the plugins then provide the functionality depending on the state the state machine is in.
The plugins can also request state modifications to the state machine.
Yivi core can be initialized in the following way:
const YiviCore = require('@privacybydesign/yivi-core');
const yivi = new YiviCore(/* options */);
yivi.use(/* Plugin A */);
yivi.use(/* Plugin B */);
yivi.start();
More information about the methods Yivi core offers to you can be found in the API overview.
Available plugins for Yivi core
Below is a list of the plugins we provide. Detailed explanations of the plugins can be found in the READMEs on GitHub (linked to in the left column).
Plugin | Functionality |
---|---|
yivi-client | Plugin to fetch a session package, to check the irma server for the current session status and optionally to fetch the result. The plugin is widely configurable, so you can also fetch a session package or a session result at custom endpoints. |
yivi-web | Plugin to handle user interaction via a web element that developers can embed within the contents of their webpages. It is designed to be used with yivi-css . yivi-css is not embedded in this package, so you have to manually include this. You are also free to use your own styling. |
yivi-popup | yivi-web element embedded in a popup overlay. The popup is displayed on top of your content and is hidden again when a session is completed, when a fatal error occurs or when the user closes the popup. It is designed to be used with yivi-css . yivi-css is not embedded in this package, so you have to manually include this. You are also free to use your own styling. |
yivi-console | Plugin to handle user interaction via the console (either the browser console or the command line console) using node.js. |
yivi-dummy | Plugin that provides a dummy implementation of the yivi-client plugin. This can be used to test the user interaction without actually having to do a session at an IRMA server. Instead, the state machine will be instructed to continue after fixed timeouts. |
If a plugin for your use case is not available, we offer you the option to construct one yourself.
Usage guide
Here we explain the scenario in which the web element is embedded within the contents of our website.
This web element will be controlled by the yivi-web
plugin. We use yivi-client
for the communication
with the IRMA server and our backend.
In the body of our HTML page we need to have an HTML element where yivi-web
can render its user interface.
We also import the yivi-css
styling to nicely style our yivi-web-form
element.
<html>
<head>
...
<link rel="stylesheet" href="assets/yivi.css" />
...
</head>
<body>
...
<section class="yivi-web-form" id="yivi-web-form"></section>
...
</body>
</html>
In our JavaScript we import yivi-core
and the relevant plugins first.
const YiviCore = require('@privacybydesign/yivi-core');
const YiviWeb = require('@privacybydesign/yivi-web');
const YiviClient = require('@privacybydesign/yivi-client');
Then we can instantiate Yivi core. Let's assume we already have an endpoint /get-irma-session
in our backend that starts a relevant IRMA session at the irma server
. Let's say the endpoint
returns a single session package (in JSON) without any backend token.
const yivi = new YiviCore({
// Enable to get helpful output in the browser console.
debugging: false,
// The option 'element' is used by yivi-web to find its HTML element in the DOM.
element: '#yivi-web-form',
// The 'session' option struct is used by yivi-client to find the session information.
session: {
// The base url of our website
url: 'http://example.com',
// The 'start' option struct specifies where yivi-client can fetch a new session package.
start: {
// Specifies how the endpoint url can be derived from the base url (see above).
url: o => `${o.url}/get-irma-session`,
// A GET request is used.
method: 'GET',
// No additional HTTP headers are needed.
headers: {},
// Note: a GET request with empty headers is fetch's default, so
// omitting these options would lead to the same result.
// All options the fetch API exposes can be used here to customize the request.
// https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch
},
// The 'mapping' option specifies how the data can be derived from a 'start' response.
mapping: {
// The only thing included in the request is the session pointer, so no additional parsing is needed.
sessionPtr: r => r,
},
// Since we did not receive a backend token, we cannot retrieve any session result here.
result: false,
},
});
Now we can add the plugins to our Yivi core instance and start running it.
yivi.use(YiviWeb);
yivi.use(YiviClient);
const promise = yivi.start();
Yivi core is now started and the Yivi web element should be visible. By looking at the state of the promise, you can detect whether the user has finished.
promise.then(() => {
// The user has completed the session.
})
.catch((err) => {
// Some fatal error has occurred.
});
Be aware that you can start an instance of yivi-core
only once.
When you want to call start()
again, you have to create a new instance.
When a promise of an earlier start()
call is not completed yet, promise rejection
can be forced by calling the abort()
method. In this way a new yivi-core
instance can be created without any risk on interference with other running instances.
When a promise is rejected because of an abort()
call, the promise will return the
error message Aborted
.
yivi.abort();
For detailed information about all available options, you can check the README of the particular plugin on GitHub. There are links in the plugin overview above.
Yivi frontend
For convenience we already bundled yivi-core
, yivi-web
, yivi-popup
and yivi-client
together with the default styling
from yivi-css
. We also added polyfills in this package to realize support for Internet Explorer 11.
The package can be installed from the npm registry.
The bundled package can also be downloaded directly here.
Please host this file as asset yourself.
The bundle can be imported in your JavaScript file by doing require('@privacybydesign/yivi-frontend')
or it can
be included directly in the HTML.
<script src="assets/yivi.js" type="text/javascript" defer></script>
You can then instantiate yivi-frontend
and start a session like this when using an embedded web element:
const exampleWeb = yivi.newWeb({
debugging: false, // Enable to get helpful output in the browser console
element: '#yivi-web-form', // Which DOM element to render to
// Back-end options
session: {
// Configure your flow here, see usage guide of yivi-core
},
...
});
exampleWeb.start()
.then(result => console.log("Successful disclosure! ๐", result))
.catch(error => console.error("Couldn't do what you asked ๐ข", error));
When you want a popup overlay to be used to, you can do the following:
const examplePopup = yivi.newPopup({
debugging: false, // Enable to get helpful output in the browser console
// Back-end options
session: {
// Configure your flow here, see usage guide of yivi-core
},
...
});
examplePopup.start()
.then(result => console.log("Successful disclosure! ๐", result))
.catch(error => console.error("Couldn't do what you asked ๐ข", error));
Be aware that you can start an instance of yivi-frontend
only once.
When you want to call start()
again, you have to create a new instance.
When a promise of an earlier start()
call is not completed yet, promise rejection
can be forced by calling the abort()
method. In this way a new yivi-frontend
instance can be created without any risk on interference with other running instances.
When a promise is rejected because of an abort()
call, the promise will return the
error message Aborted
.
exampleWeb.abort();
examplePopup.abort();
More information about the methods the Yivi frontend package offers to you can be found in the API overview.
Yivi css
For the Yivi core plugins yivi-web
and yivi-popup
we made it possible to manually include the style
that it will use. We provide to you a normal version
and a minified version
of the default styles. The CSS can be linked into your website the regular way:
<link rel="stylesheet" href="assets/yivi.css" />
When you want to adapt the design to suit for your own use case, you can take a look in the styleguide.
Based on this you can adapt the CSS and then import the modified version into your project.
Customized versions of yivi-css
can be used in combination with the yivi-web
and
yivi-popup
plugins for yivi-core
.
Customizing the design
Customizing the design is especially useful for developers that want to use an embedded web element to initiate the Yivi flow and see that the default design does not fit into the design of their website. We provide you a convenient way to alter the design and build a new, customized style. This can be done in the following way:
- Clone the
yivi-frontend-packages
repository. - Use our guide to compile the CSS styleguide locally.
- Make the desired changes in the source files. These files can be found in the
yivi-css/src
directory. - Check all pages of the locally built styleguide to check whether your local changes work for all flows.
- Build a release version for your customized CSS by running
npm run release
in theyivi-css
directory. The built CSS files can be found in theyivi-css/dist
directory. - Include the new style in your website and use Yivi core in combination
with the
yivi-web
plugin (for embedded web elements) or theyivi-popup
plugin (for a popup overlay). The plugins will use the custom CSS that you have embedded. For managing the session state we recommend you to use theyivi-client
plugin.
require('assets/my-custom-yivi-css-design.min.css');
const YiviCore = require('@privacybydesign/yivi-core');
const YiviWeb = require('@privacybydesign/yivi-web');
const YiviClient = require('@privacybydesign/yivi-client');
const yivi = new YiviCore({
debugging: true,
element: '#yivi-web-form',
language: 'en',
// Check the yivi-web README on how to customize the default texts.
session: {
// Check the yivi-client README for all options.
},
});
yivi.use(YiviWeb);
yivi.use(YiviClient);
yivi.start()
.then(result => console.log("Successful disclosure! ๐", result))
.catch(error => {
if (error === 'Aborted') {
console.log('We closed it ourselves, so no problem ๐
');
return;
}
console.error("Couldn't do what you asked ๐ข", error);
});
Make your own Yivi core plugin
If you need functionality that is not covered by one of the existing Yivi core plugins, you can also define
one yourself. In the constructor the stateMachine
and the options
from YiviCore
can be accessed.
The constructor can be omitted if you do not need it.
Furthermore, a plugin can have a start
method that is
called when the start
method of the associated YiviCore
instance is called, a stateChange
method
that is called when the state of the state machine changes, and a close
method (check the explanation below for details).
class YiviPlugin {
// Optional
constructor({stateMachine, options}) {
...
}
// Optional method
start() {
...
}
// Optional method
stateChange({newState, oldState, transition, isFinal, payload}) {
...
}
// Optional method
close() {
// May return a Promise when the closing operation is async;
// yivi-core will then wait for the Promise to be completed.
...
}
}
A plugin can request the state machine to make changes. This can be done using the transition
and
finalTransition
methods of the stateMachine
. The first parameter of these functions is the requested
transition. The possible transitions can be found in the state machine.
As second parameter payload
can be added to the transition. The payload can then be accessed by all other plugins
as it is one of the parameters of the stateChange
method. When requesting a finalTransition
, the state
machine will be locked in the new state. From then no transitions can be made anymore. For a finalTransition
the potential newState
must be in the list of possible end states. Otherwise, an error is returned. After
a finalTransition
the close
method of the plugin is called to close the plugin's state. This method should
return a Promise which resolves when the plugin finishes closing. When the close
Promises of all plugins are
resolved, the promise returned by the start
method of YiviCore
will resolve or reject (depending on the
transaction). In this way we can guarantee that plugins are not active anymore when the promise returned by the
start
method of YiviCore
is finished. Besides when calling finalTransition
, the closing procedure can also
be activated when the transition
method is used and the state machine gets in a state from which no
transitions are possible anymore.
For example, in the YiviPopup
plugin the user can press on the close button in the UI to abort the session.
When this happens the YiviPopup
plugin must request a state change at the Yivi core state machine to
notify all other plugins that the new state becomes Aborted
. This is done in the following way:
stateMachine.transition('abort', 'Popup closed');
There are no transitions possible anymore from the state Aborted
. This means that unless the transition
is not explicitly marked as final, the stateChange
method of your plugin will be called with isFinal
set
to true.