privacybydesign / irma-frontend-packages

Collection of frontend related packages, that together form a Javascript "client" to the IRMA server.
7 stars 4 forks source link

:warning: Only use this package for backwards compatibility reasons. Please use yivi-frontend-packages instead.

IRMA frontend packages

Welcome to this repository! IRMA frontend packages is a collection of related packages, that together form a Javascript "client" to the IRMA server. The primary purpose of these packages is to have a very flexible IRMA client in the web browser, but you can also use them to build a NodeJS client.

The browser version, by default, will look like this:

IRMA flow in the browser

The client is designed in such a way that you can combine the plugins you need and configure them in many useful ways. So the above design can easily be removed (for your NodeJS application) or swapped out for a popup version.

Supported IRMA flows

This library is designed to be flexible in usage. In this section we describe how some imaginable IRMA flows and how these flows map onto this library. We also mention points of attention for developers when implementing such a flow.

As a general best practice, we don't want to allow the web browser to freely initialize the IRMA flow. Also, in many cases we don't want the result of the disclosure or signing request to be revealed to the browser. The code that is running in the web browser could be compromized by the user or third party injected code.

Talking to IRMA server through a proxy

The most highly recommended flow that combats these issues is one where the Javascript client talks to the IRMA server through your own application back-end, that functions as a proxy.

Starting a session through a proxy Starting a session through a proxy

In this flow the IRMA Javascript client requests the back-end for a new session to be started, and it is the back-end that actually starts the session. In this flow the web browser can't manipulate the session that gets started or see the contents of the resulting disclosure:

Getting the session result through a proxy Getting the session result through a proxy

How to implement this flow #### Client side You can use either the wrapper package [`irma-frontend`](irma-frontend) or combine your own packages if you have a build system like webpack. ##### `irma-frontend` ```html ``` ##### Combining your own packages ```bash $ npm install --save-dev irma-css irma-core irma-web irma-client ``` ```javascript require('@privacybydesign/irma-css'); const IrmaCore = require('@privacybydesign/irma-core'); const Web = require('@privacybydesign/irma-web'); const Client = require('@privacybydesign/irma-client'); const irma = new IrmaCore({ debugging: false, // Enable to get helpful output in the browser console element: '#irma-web-form', // Which DOM element to render to // Back-end options session: { // Point this to your controller: url: 'https://my-server.domain/irma-endpoint', start: { url: o => `${o.url}/start`, method: 'GET' }, result: { url: (o, {sessionPtr, sessionToken}) => `${o.url}/result`, method: 'GET' } } }); irma.use(Web); irma.use(Client); irma.start() .then(result => console.log("Successful disclosure! 🎉", result)) .catch(error => console.error("Couldn't do what you asked 😢", error)); ``` #### Server side Then, on the server side, you have to implement the `start` and `result` actions on your `irma-endpoint` controller. `start` should make a call to the IRMA server and start the IRMA session there. Then, strip the result token from the response JSON and send the rest back in the response to the client. `results` can fetch the result from the IRMA server using the result token that we stripped off the response earlier. Whatever you return to the client will be what the Promise on the last few lines resolves to. As you saw in the snippets above, you can change the request types and URLs of both actions. But you can also change the request body, headers and how to get the QR code from the server's response. See the [`irma-client`](plugins/irma-client) documentation for details.

Talking to IRMA server directly, with signed request

Another supported flow is one where the Javascript client talks to the IRMA server directly, and is itself responsible for starting the session there.

Starting a session directly, with signed request Starting a session directly, with signed request

The payload that gets sent to the IRMA server to start the session is signed by your application back-end, so the web browser can't alter the request and the IRMA server can verify its authenticity.

However, the web browser will be able to see the resulting disclosure or signed contract, because the web browser will be the one receiving the result token from the IRMA server. This may be an issue for some scenario's. In these scenarios you can get signed JWTs as result from the IRMA server. This flow with receiving JWTs as result is not elaborated in this example.

Getting the session result directly Getting the session result directly

How to implement this flow #### Client side You can use either the wrapper package [`irma-frontend`](irma-frontend) or combine your own packages if you have a build system like webpack. ##### `irma-frontend` ```html ``` ##### Combining your own packages ```bash $ npm install --save-dev irma-css irma-core irma-web irma-client ``` ```javascript require('@privacybydesign/irma-css'); const IrmaCore = require('@privacybydesign/irma-core'); const Web = require('@privacybydesign/irma-web'); const Client = require('@privacybydesign/irma-client'); const irmaRequest = document.getElementById('irma-request').value; const irma = new IrmaCore({ debugging: false, // Enable to get helpful output in the browser console element: '#irma-web-form', // Which DOM element to render to // Back-end options session: { // Point this to your IRMA server: url: 'https://irma-server.my-server.domain/', start: { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: irmaRequest } } }); irma.use(Web); irma.use(Client); irma.start() .then(result => console.log("Successful disclosure! 🎉", result)) .catch(error => console.error("Couldn't do what you asked 😢", error)); ``` #### Server side On the server side you have to render the signed request in your view, or provide some other method of getting the signed request to the client. This is left as an exercise for the reader, because there are too many ways in which to do it. The Promise in the last few lines of the code above will now resolve to the result of the disclosure or signing flow. Please note that it is now also up to you to transfer this information to your back-end **and also to check that the result has been properly signed by your IRMA server** and the browser has not altered the results in any way.

Talking to IRMA server directly, with plain request

This flow is "tolerated" but not recommended in the browser. It's useful for making quick demos and for local development, but is probably not a good idea for production. Unless you're writing a NodeJS client, maybe.

The Javascript client talks directly to the IRMA server and starts an arbitrary session there. The back-end does not need to be involved at all.

How to implement this flow You can use either the wrapper package [`irma-frontend`](irma-frontend) or combine your own packages if you have a build system like webpack. ##### `irma-frontend` ```html ``` ##### Combining your own packages ```bash $ npm install --save-dev irma-css irma-core irma-web irma-client ``` ```javascript require('@privacybydesign/irma-css'); const IrmaCore = require('@privacybydesign/irma-core'); const Web = require('@privacybydesign/irma-web'); const Client = require('@privacybydesign/irma-client'); const irma = new IrmaCore({ debugging: false, // Enable to get helpful output in the browser console element: '#irma-web-form', // Which DOM element to render to // Back-end options session: { // Point this to your IRMA server: url: 'https://irma-server.my-server.domain/', start: { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ '@context': 'https://irma.app/ld/request/disclosure/v2', 'disclose': [ [ [ 'pbdf.pbdf.email.email' ], [ 'pbdf.sidn-pbdf.email.email' ], ] ] }) } } }); irma.use(Web); irma.use(Client); irma.start() .then(result => console.log("Successful disclosure! 🎉", result)) .catch(error => console.error("Couldn't do what you asked 😢", error)); ```

Skipping the server altogether for development

For development or testing purposes it is often easier to just skip having an IRMA server at all. You can use the irma-dummy package to this end.

How to implement this flow ```bash $ npm install --save-dev irma-css irma-core irma-web irma-dummy ``` ```javascript require('@privacybydesign/irma-css'); const IrmaCore = require('@privacybydesign/irma-core'); const Web = require('@privacybydesign/irma-web'); const Dummy = require('@privacybydesign/irma-dummy'); const irma = new IrmaCore({ debugging: false, // Enable to get helpful output in the browser console element: '#irma-web-form', // Which DOM element to render to dummy: 'happy path' // Specify which flow to emulate }); irma.use(Web); irma.use(Dummy); irma.start() .then(result => console.log("Successful disclosure! 🎉", result)) .catch(error => console.error("Couldn't do what you asked 😢", error)); ``` See [`irma-dummy`](plugins/irma-dummy) for more options.

Additional documentation

Want to know more about any of the packages? Each package has its own README file with features, options and purpose of the package:

Examples

Also, we have several examples available that show how you can use specific combinations of plugins to achieve different effects:

Development

When developing a certain package, you can test the package using one of the examples.

We provide development scripts to install all packages, configure all needed links between the packages and then build the packages. The setup is tested on Linux (debian), MacOS and Windows (in git bash).

These scripts require npm (Node.js) to be installed. We recommend you to use npm version 7 or above. On npm version 6 (LTS) the --no-save option of npm install is not working correctly. This leads to unintended changes being made to the package.json files.

These development scripts are automatically triggered when installing and building the examples.

npm install   # To install the dependencies for all packages
npm run link  # To make sure local changes in other irma-frontend-packages propagate
npm run build # To trigger the building steps for irma-css and irma-frontend

Linter

To make a valid PR, your code changes should pass the linter and code formatter. We use ESLint for JavaScript and Stylelint for SCSS. You can run these in the following way:

npm install
npm run lint

Releasing

To release a new version of the irma-frontend-packages you have to run the scripts below. You need access to the NPM repository in order to release new versions. Otherwise, these scripts will fail.

./prepare-release.sh <command line parameters for npm version>
# Check whether all steps succeeded properly, otherwise undo changes and fix issues first.
./release.sh

./prepare-irma-popup.sh
# Check whether all steps succeeded properly, otherwise fix issues in irma-popup first.
cd ./plugins/irma-popup && npm publish --access public

cd ../.. # Go back to root of the repository
./prepare-irma-frontend.sh
# Check whether all steps succeeded properly, otherwise fix issues in irma-frontend first.
cd ./irma-frontend && npm publish --access public

cd .. # Go back to root of the repository
git add -u ./\*package.json ./\*package-lock.json
git commit -m "Version bump"

Documentation

More documentation on how to use this package can be found in the IRMA documentation.