Overview

Verimi is an open digital identity platform, which implements the IETF-recommended and enterprise-grade authorization protocol OAuth 2.0., and offers 2-factor authentication, administration, permission management and payment functionalities. Verimi facilitates the end users (or Identity Owners) with storing and accessing their digital identities across the web. Users can use their digital identity (ies) to take advantage of the services, provided by Verimi partners, like flight agencies, car sharing, ecommerce, etc. These are called Service Providers (or relying parties in OpenID Connect definition). Based on the Verimi platform, the participating Service Providers are able to realize following use cases:
  • Usage of unified login functionality for all Verimi partners and further potential Service Providers
  • Potential outsourcing of:
    • strong authentication for account access and data protection based on integrated Verimi 2-factor authentication
    • Service Provider's specific customer account management to Verimi platform
  • Utilization of verified/non-verified data for increased customer convenience, Service Provider risk reduction, and creation of new Service Provider's specific use cases

Environments

Verimi offers 2 environments: "uat" for developing and testing purposes and a live environment. Verimi plans a 3rd environment "sandbox" which will have the same settings and software as live environment. Here are the domains for uat and live:

environment uat live sandbox (planned)
Web Access https://web.uat.verimi.cloud https://web.verimi.de
Api Access https://api.uat.verimi.cloud/dipp/api https://api.verimi.de/dipp/api

Request Service Provider Registration

You need an API access, if you want to implement Verimi services. Please fill out the registration form and you’ll receive your access (certificate, client_id and client_secret) within 24h, with which you can implement Verimi login (single-sign-on) without costs. Please contact our sales, if you want to use other Verimi services like user registration, 2-factor authentication, identity verification, qualified signature, payment.

Postman Collection

When you received your test api access you can use our Postman collection to execute your first requests to get an impression of how our API works.

Get started

Request access to the Verimi UAT environment (api access and/or access to web app&mobile apps). The UAT environment (by web app and by API) is only accessable with a SSL certificate. We'll provide you with a personalized SSL client certificate and if requested with a clientId and clientSecret. Along with the api access, you'll also get a zip file with the Verimi buttons as graphic files in both web and mobile formats.

Install the client ssl certificate on your local machine.

Create a test user on Verimi UAT environment by completing the registration form and confirming the email address via mailhog.If you need a validated identity, then this tutorial shows you how to create a validated identity for your test user.

If you implement Verimi by OpenID Connect (OIDC), then use scope=openid (and add additional scopes for the user data that you need, like name, address) and check the well-known OpenID configuration.


Partner settings

Send us the URLs (+ port number, if applicable) from which you send api requests to integration-support@verimi.com so that we can add them to our URL whitelisting. Before Golive of your integration, we also need to add the URLs you'll be using in the PROD environment. You can have as many URLs as you want.

Login/Registration with Verimi

To be able to call the Verimi APIs and access the user's stored data on behalf of him, the Service Provider needs to obtain a valid access token. In this section we will describe how to do it. OpenID Connect (and the underlying OAuth 2.0) Authorization Framework defines multiple flows for obtaining a valid access token. In Verimi we use the Authorization Code Flow.

The whole flow is depicted below (without requesting additional data):
With requesting additional data:

For Service Providers, this flow starts with redirection of Identity Owner (IO) to Verimi authentication endpoint with the desired parameters and ends with request for token given authorization code (we provide more details at data transfer description).

Step 1: Login and register with Verimi

This represents a best practice approach, so you wont bother your customer with too many Buttons, Mails and requests. This will also make the onboarding of new customers as smooth as possible and reduces friction in the Verimi process.

  • Add the Verimi button to your login page
  • Add your redirect url (where you are going to receive our response)
  • Add scopes (f.e. login, openid) to your first request.
  • Retrieve the token and check for the sub.
  • If you find the sub in your data the customer should be logged in.
  • If you dont find it the customer needs to register, either you automatically request the data from Verimi or you let the customer choose how to do it, thats up to you.
  • The scopes reflect the user data, that you want to retrieve from Verimi to fill your registration form. Scope "login" is mandatory.
  • After you received the data, save it along with the sub, so next time the customer drops by he will be logged in immediately.

Execute this API GET call on login:

GET https://web.uat.verimi.cloud/dipp/api/oauth2/auth?response_type=code&client_id=[client_id]&scope=login openid&redirect_uri=[provider redirect uri]

Execute this API GET call on registration:

GET https://web.uat.verimi.cloud/dipp/api/oauth2/auth?response_type=code&client_id=[client_id]&scope=login openid address&redirect_uri=[provider redirect uri]

We strongly recommend using scope "openid", which hands out the ID token, additionally to the access token. Otherwise you need to use the userinfo endpoint to retrieve the sub. If you've set the corresponding scopes, the id token can maximally hand out the user data fields "name", "email" and "address", which might be sufficient for your usecase (login or registration), so that you don't need to request additional user data by GET Basket request.

Most important reason for using OpenID is that you get the unique identifier for a given user: sub via the ID token. Use this identifier in order to check, if a user already exists in your user database. The alternative lookup by name, email, address is not exact. And a user may change his email or address and you wont be able to connect him anymore to his account. Also the user will receive an email every time you request the data.

The scopes "idcard" and "passport" aren't OpenID Connect scopes and therefore not handed out by the ID token. You'd need to execute the GET Basket request for that. These two scopes also trigger the inline verification process, if the user has no verified identity in his Verimi profile, yet. We can set the identification methods of your choice: video-ident, eID (extract data from id card by Verimi mobile app) or giro-ident (transfer data from your bank using your online-banking login).

Here's the list of all scopes.

Sending the customer to the login/registration when using tabbed flow

When using the tabbed flow you can decide which tab will be shown when your customer clicks on the login/register button on your side. The payment and identification flows are not affected.

That Feature is especially interesting in case you are using Verimi as exclusive method for your usermanagement. We use the boolean parameter "isRegistrationRequested" in the URL to send the customer to the registration tab in case of true. Below you see an example of the request for registration enabled, this one also requests the dataset email, of course you can add more if needed:

GET https://web.uat.verimi.cloud/dipp/api/oauth2/auth?response_type=code&client_id=[client_id]&scope=login openid email&redirect_uri=[provider redirect uri]&isRegistrationRequested=true

Provide user email to Verimi

In case you already know the email, or you want to enforce the customer to use exactly the email he is using for your environment you can use the parameter login_hint. You just need to apply the parameter like that:

GET https://web.uat.verimi.cloud/dipp/api/oauth2/auth?login_hint=some@email.de&response_type=code&client_id=[client_id]&scope=login openid email&redirect_uri=[provider redirect uri]&isRegistrationRequested=true

Beware, if you use the parameter the customer wont be able to change the email in the login form.

Controlling 2-factor authentication

Service Providers can dynamically request 2-factor authentication (2FA) for the Verimi login, in order to protect access to sensitive contexts. Verimi exposes this functionality via the OpenID Connect standard parameter Authentication Context Class Reference (acr).

In order to perform 2FA for the login with Verimi, the parameter “acr_values=loa.dipp.2fa” is used, e.g.

GET https://web.uat.verimi.cloud/dipp/api/oauth2/auth?acr_values=loa.dipp.2fa&response_type=code&scope=login openid&client_id=...&redirect_uri=...&state=...&nonce=...

In accordance with the OpenID Connect standard (OpenID Documentation) the information about the 2FA authentication will be submitted via id_token, when the scope openid is used. Verimi has no means of enforcing a two-factor login. You would therefore have to check if the required login was performed. This can happen via a claim in the id_token, named "acr".

There are several cases that can happen, in case you are the one requesting a login via 2FA and the user was not logged for now you will receive "acr": "loa.dipp.2fa".

When the user was already logged in via 2FA and he reuses his current Verimi Session then you will receive "acr": "loa.dipp.session".

If you did not requested a 2FA then you will receive "acr": "loa.dipp.default"

Step 2: request authorization code and token

By clicking the Verimi button, the user will automatically be redirected to the Verimi login page. By default, Verimi offers an inline-registration flow including account activiation. If you request the scopes "idcard" and/or "passport", you can decide if you want the user to create a Verimi account at the end of the flow or not (inform the integration team, they'll set the parameter). After the user logged-in or registrers at Verimi, the data consent page is shown. It lists the data, requested by the scopes. The SP must add a cancel redirect url (see below), otherwise on cancel the user will end up in his Verimi account. If the user confirms, he is redirected to the redirect url, provided by the SP in the GET .../auth request and the SP gets the authorization code, like:

http://localhost:9000/authorization/code?code=HlENEZ7z8GD_0CihRrY3p-MmfLVHGJbb9my2eE1mIzo.98woZwDJdzBxmtgcxlcadPw0aB3tKctRc_oR_q40biE&scope=offline%20openid%20login%20phone&state=766331181

which can be exchanged with a valid token by calling the token endpoint.

Use the authorization code for requesting the access or ID token:

POST https://api.uat.verimi.cloud/dipp/api/oauth2/token?grant_type=authorization_code&code=[CODE]&redirect_uri=http%3A%2F%2Flocalhost%3A8010%2Fauthorization%2Fcode
"Content-Type": "application/x-www-form-urlencoded"
"Authorization": "Basic QUNNRTpHfDQxfDBhbjE4WklzX3c=" // Client ID client and client secret of the Service Provider

Cancellation redirect

The user may click "Cancel" on the Verimi login/registration page which redirects the user to the cancellation url, provided by the Service Provider. Therefore, the Service Provider MUST define a default cancel redirect URI. Since OpenID specifies only a 'redirect_uri' as a parameter for the authorization call, Verimi does a default redirect to Verimi in case the 'cancel_uri' isn't defined.
That's how it works for the specified cases:

A specific 'cancel uri' is NOT given: Redirect to Verimi (default)
A specific 'cancel uri' is given: Redirect to 'cancel_uri'

Step 3: Decode the id token

You will receive an id token with following structure:

{
"access_token": "5sK3eebt4I8w6M2Rdlep50cVf7UrZeYeVcQOzFrGjT4.NOo1-LoylB_dbSnHAKKj7nbPmn_kzJJEUL5Z_LzRipE",
"token_type": "bearer",
"refresh_token": "BQMQKo7BOWVwKf4cpcyf12vj9o1Igx8VtW1b7Jl9U2A.kA6F0NBUSIU_5j9oDmMzuQUlT03iWD9MA_MXHEA2vSc",
"scope": "offline openid login address name email",
"id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6InB1YmxpYzphYjVjMjIzNC0wNjkzLTQyMDAtYWU1OC00OGQxMzAxYTg5MDUiLCJ0eXAiOiJKV1QifQ.eyJhY3IiOiJsb2EuZGlw
cC5kZWZhdWx0IiwiYWRkcmVzcyI6IntcImZvcm1hdHRlZFwiOlwic2Vlc2Ftc3RyYcOfZSAxMjIzLCAxMjIzMSBiZXJsaW4sIEdlcm1hbnl
cIixcInN0cmVldF9hZGRyZXNzXCI6XCJzZWVzYW1zdHJhw59lIDEyMjNcIixcImxvY2FsaXR5XCI6XCJiZXJsaW5cIixcInJlZ2lvblwiOlwiXCIsXCJwb3N0YWxfY29kZVwiOlwiMTIyM
zFcIixcImNvdW50cnlcIjpcIkdlcm1hbnlcIn0iLCJhdF9oYXNoIjoiMGo3c1VSbGdmU3JZZnVEX3JPb2lXQSIsImF1ZCI6WyJ0ZXN0Il0sImF1dGhfdGltZSI6MTU2MDg3MjYzOSwiZW1haW
wiOiJjaHJvbUB0ZXN0LmRlIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImV4cCI6MTU2MDg3NDY0NywiaWF0IjoxNTYwODcyODQ3LCJpc3MiOiJodHRwczovL3ZlcmltaS11Y
XQuY29yZXRyYW5zZm9ybS
5jb20vIiwianRpIjoiNTIwNzgzMDMtNzNiMC00ODI0LTlhOGQtYmY3NGJlZjdkOWIxIiwibmFtZSI6Imxhc3QgZmlyc3QiLCJub25jZSI6IiIsInJhdCI6MTU2MDg3MjYxOSwic3ViIjoiMzFjYjAxZjktYT
U2My00ZjIyLWJmOGUtMjliM2FjYjc4MTJjIn0.kcoU71mfsZVC01TyBnLOcPcO3zH2tQDZ4lZZUQmHbD7cmC9w0z5XxlIxBnPjs2wy-A0nzBuBfdkORjhQ8bhMIDVv1ciCYM51
DUihD0obsgvZUUV7Q2dwmUaCt5XiJUzzdR6XL3L4bPNYA0tSNVdKrzV8evyaTvlU0T38Kqw5Aqhu2KMBDDc-lzL7kos1UeaR1XESJnJBrf38e8Fm2CIFs_cbPPs9
HvKsOJ75dTrzUBLuItabQB7QdQ02ErLgOTDhXyP_08XZKXxgG6K1AaYvD
96hmDHN8GR9Z5T5WEG
sBZ3BL-BeNWWaz995pt
J-vw6nBRN_hDkAtEE1hLnMTeH4fg"
}

The id token needs to be decoded in order to get the user data. We suggest to use jwt.io/#debugger.

Javascript example:

function decodePayload(jwtToken) {
var payload = jwtToken.split('.')[1];
playload = JSON.parse(atob(payload));
return playload;
}

Structure of the ID token:

{
"acr": "loa.dipp.default",
"address": "{\"formatted\":\"sesamstrasse 1223, 12231 berlin, Germany\",\"street_address\":\"sesamstrasse 1223\",\"locality\":\"berlin\",\"region\":\"\",\"postal_code\":\"12231\",\"country\":\"Germany\"}",
"at_hash": "0j7sURlgfSrYfuD_rOoiWA",
"aud":"test",
"auth_time": 1560872639,
"email": "chrom@test.de",
"email_verified": true,
"exp": 1560874647,
"iat": 1560872847,
"iss": "https://web.uat.verimi.cloud/",
"jti": "52078303-73b0-4824-9a8d-bf74bef7d9b1",
"name": "last first",
"nonce": "",
"rat": 1560872619,
"sub": "31cb01f9-a563-4f22-bf8e-29b3acb7812c"
}

User (identity owner) identifier - sub

For each Service Provider, Verimi assigns a dedicated user identifier: Subject or sub for short. The subject can and SHOULD be used to identify the user in the Service Provider's system.
The sub is part of the ID token and/or is provided by the userinfo endpoint.

A sub is assigned to a Service Provider when the Service Provider creates an user account by using Verimi data or when a Verimi account is linked to an existing Service Provider user account. You can find details for those processes in the following sections:

Step 4: Request user data

If you've requested more scopes than login or openid, then you need to request this data with one of the folllowing methods:

Userinfo endpoint

This endpoint is limited to a certain set of data.

GET https://web.uat.verimi.cloud/userinfo
Authorization: Bearer K3LQGsBgJYgg6s0hLfeqSECveeTbibqzPvzDs9upgnc.g3xK67mlv8I7vhOD4pLwOT8wSPLUrc7gyZXz-ZPjk3s

This is the response with the maximum data set:

{"acr":"loa.dipp.default",
"address":"{
"formatted":"Berliner Straße 12, 60311 Frankfurt am Main, Deutschland",
"street_address":"Berliner Straße 12",
"locality":"Frankfurt am Main",
"region":"",
"postal_code":"60311",
"country":"Deutschland"}",
"birthdate":"1990-12-12",
"email":"user@email.com",
"email_verified":true,
"family_name":"username",
"gender":"N/D",
"given_name":"firstname",
"locale":"N/D",
"middle_name":"N/D",
"name":"firstname username",
"nickname":"Proper",
"phone_number":"49 12345678901",
"phone_number_verified":true,
"picture":"N/D",
"preferred_username":"N/D",
"profile":"N/D",
"sub":"c7648392-7459-481b-8243-d11cd4ec56ea",
"updated_at":1576532774,
"website":"N/D",
"zoneinfo":"N/D"}

GET basket request

This endpoint supports all scopes and must be used if you request one of the scopes "passport" and/or "idcard".

{
"access_token": "K3LQGsBgJYgg6s0hLfeqSECveeTbibqzPvzDs9upgnc.g3xK67mlv8I7vhOD4pLwOT8wSPLUrc7gyZXz-ZPjk3s",
"token_type": "bearer",
"refresh_token": null,
"scope": "login name address email",
"id_token": null
}

Request the user data, that you've defined as scope(s) by this api request:

GET https://api.uat.verimi.cloud/dipp/api/v2/query/baskets
Authorization: Bearer K3LQGsBgJYgg6s0hLfeqSECveeTbibqzPvzDs9upgnc.g3xK67mlv8I7vhOD4pLwOT8wSPLUrc7gyZXz-ZPjk3s

That will give you a response that looks like this:

{"basketId":"0ea6258d-9d90-453e-a71b-7d3156402f3c","serviceProviderId":"test",
"dataScopes":[
{"scopeId":"address","data":[
{"name":"firstName","value":"testo"},
{"name":"lastName","value":"testa"},
{"name":"title","value":"mr"},
{"name":"academicTitle","value":""},
{"name":"firm","value":"testi"},
{"name":"country","value":"Germany"},
{"name":"countryCode","value":"DEU"},
{"name":"zipCode","value":"12231"},
{"name":"city","value":"berlin"},
{"name":"street","value":"seesamstraße"},
{"name":"number","value":"122"},
{"name":"standard","value":"true"},
{"name":"addressInfo","value":""},
{"name":"id","value":"f6f251c7-6e9b-4f0e-9161-b31b181d024b"},
{"name":"verified","value":"false"}]},
{"scopeId":"name","data":[
{"name":"firstName","value":"last"},
{"name":"lastName","value":"first"},
{"name":"title","value":"mr"},
{"name":"academicTitle","value":""},
{"name":"verified","value":"false"}]},
{"scopeId":"email","data":[
{"name":"email","value":"chrom@test.de"},
{"name":"standard","value":"true"},
{"name":"verified","value":"true"},
{"name":"contact","value":"true"},
{"name":"id","value":"329fa8af-945b-3809-8eac-951871c92827"}]}]}

Statically typed basket endpoints

For easier integration, baskets can be used in a statically typed way instead of the dynamic approach.

You MUST use the typed-basket request, if you need to retrieve the video ident source files.

GET https://api.uat.verimi.cloud/dipp/api/v2/typed-basket
GET https://api.uat.verimi.cloud/dipp/api/v2/typed-basket Result:
{
    "basketId": "d622cd06-622d-424b-8890-6515e88479aa",
    "serviceProviderId": "[your service provider id]",
    "dataScopes": {
        "email": {
            "id": "6b688440-1eee-3ee8-a0a0-8f8c5782f939",
            "emailAddress": "email@address.com",
            "verified": true,
            "standard": true,
            "contact": true
        },
        "address": {
            "id": "a8022633-b44d-4ba3-b38e-d98327758bed",
            "addressInfo": "",
            "firstName": "Will",
            "lastName": "Smith",
            "title": "mr",
            "academicTitle": "",
            "firm": "Hollywood",
            "street": "Hollystreet",
            "number": "1",
            "zipCode": "55590",
            "city": "Meisenheim",
            "country": "Germany",
            "countryCode": null,
            "verified": false,
            "standard": false
        },
        "name": {
            "id": "e9267c09-0e73-4d6b-b5ef-2d6d71dff732",
            "firstName": "Will",
            "lastName": "Smith",
            "title": "mr",
            "academicTitle": "",
            "verified": false
        },
        "phone": {
            "id": null,
            "internationalAreaCode": null,
            "areaCode": null,
            "telephoneNumber": null,
            "verified": null,
            "standard": null,
            "contact": null
        },
        "birthDate": {
            "id": null,
            "dateOfBirth": null,
            "verified": null
        }
    }
}

Step 5: Retrieve identity verification source files

If you request an AML-compliant identity, then you not only need to request the data scopes "passport" or "idcard", but you also need to retrieve the source files.

You MUST use the endpoint "typed-basket" for requesting these scopes, since this endpoint supports adding your transaction id, which you use for mapping the source files to your transaction:

GET https://api.uat.verimi.cloud/dipp/api/v2/typed-basket?correlationId={your transaction id}

For the identity method "video ident", we send the 3 source files (PDF, MP3, MP4) as JSON payload separately to an endpoint of your choice, including your transactionId.

After we received the transactions parameter, Verimi will call the API endpoint which you provided:

POST https://your-url/{correlationId}/
{
    vendorSessionId: string,
    vendorTimestamp: number,
    pdf: string (Base64 encoded),
    mp3: string (Base64 encoded),
    webm: string (Base64 encoded)
}

Your API MUST be secured by basic authentication and 2-Way-TLS (which is the same you use to connect to the Verimi API). The certificate will be issued by you, for which you'll get a certificate request from us.

You'll receive all data in Base64 encoded strings, that you need to decode and generate files out of it.

When you request video source files for a given test user on our UAT test environment, you won't receive real video and audio files, but dummy files.

Please contact the integration team, if you want to need source data from eID (retrieve data from idcard).

eIDAS substantial login & identification

You can integrate Verimi to have a login or identification which is compliant to the eIDAS authentication level "substantial". Verimi has been certified "eIDAS substantial" for its app based authentication using the PIN. Service Providers can configure the Verimi authentication to an eIDAS substantial login by requesting a 2-factor authentication (cf. controlling 2-factor authentication) whereas the second factor is required to be a non-biometric one (cf. controlling the used authentication method.)

Using the following request URL, a service provider can request an eIDAS substantial login and identification:

GET https://web.uat.verimi.cloud/dipp/api/oauth2/auth?acr_values=loa.dipp.2fa&amr_values=pin&response_type=code&scope=login openid idcard&client_id=...&redirect_uri=...&state=...&nonce=...

Controlling the used authentication method

In addition to controlling that a 2-factor authentication shall be used, service providers can also dynamically request which kind of factor shall be used as second factor. As default the Verimi user can choose between using the PIN or using a biometric option (faceID or TouchID).

By using the OpenID Connect standard parameter Authentication Method Reference Values, the second factor can be restricted to the non-biometric option.

In order to controlling the authentication method for the second factor for the login with Verimi, the non-OIDC parameter "amr_values=pin" is used.
Note: The claim `amr_values` is an addition to the OIDC specification by Verimi.

GET https://web.uat.verimi.cloud/dipp/api/oauth2/auth?acr_values=loa.dipp.2fa&amr_values=pin&response_type=code&scope=login openid idcard&client_id=...&redirect_uri=...&state=...&nonce=...

Valid combinations for controlling the authentication

2-Factor Authentication Authentication options for second factor at Verimi Controlling the authentication factor by a service provider
acr=loa.dipp.2FA amr_values=pin Supported

Note: If the acr_values is not loa.dipp.2fa, the amr_values parameter will be ignored.

In accordance with the OpenID Connect standard (OpenID documentation) the information about the 2FA authentication will be submitted via id_token, when the scope 'openid' is used. Verimi has no means of enforcing a two-factor login. You would therefore have to check if the required login was performed. The ID Token (JWT) contains the acr and amr claims and provide information to the service provider as to what authentication mechanisms were used. For 2FA with pin, we get "acr:loa.dipp.2fa" and "amr": ["pin"].

After a successful authentication please call the endpoint `baskets` to obtain the identification data.

example: https://api.uat.verimi.cloud/dipp/api/v2/query/baskets
Among the users data please decode `value` from `jws`. It will contain the requested eIDAS substantial data.

Important: To only receive eIDAS substantial data please ensure the `verificationMethod` always equals `EID`. The claim `verificationMethod` is received with the `baskets` endpoint.

Steps to obtain valid eIDAS substantial data

  1. Ensure to include the following parameters in your request
    • acr_values=loa.dipp.2fa
    • amr_values=pin
    • scope=login openid idcard

    example on UAT:
    https://web.uat.verimi.cloud/dipp/api/oauth2/auth?acr_values=loa.dipp.2fa&amr_values=pin&response_type=code&scope=login openid idcard&client_id=...&redirect_uri=...&state=...&nonce=...
  2. Acquire an access token.
    (see endpoint `token`)

  3. Call the endpoint `baskets`
    Ensure that `verificationMethod` is `EID`

    { "name": "verificationMethod", "value": "EID" },
    If the `verificationMethod` is not `EID` do not proceed.

  4. Decode `jws`
    {
        "name": "jws",
        "value": "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJlZWUwMzY4Zi0zNjVhLTRhM2YtYjMzMy1kOGY0MWMyYmViZDQiLCJiaXJ0aGRhdGUiOiIxOTY0LTA4LTEyIiwiYWRkcmVzcyI6eyJjb3VudHJ5IjoiRCIsInN0cmVldEFkZHJlc3MiOiJIRUlERVNUUkFTU0UgMTciLCJmb3JtYXR0ZWQiOm51bGwsInBvc3RhbENvZGUiOiI1MTE0NyIsImxvY2FsaXR5IjoiS8OWTE4iLCJyZWdpb24iOm51bGx9LCJodHRwczpcL1wvcmVmLWF1c3dlaXNpZGVudC5laWQtc2VydmljZS5kZVwvcGxhY2VPZkJpcnRoVHlwZSI6IkZSRUVfVEVYVCIsImh0dHBzOlwvXC9yZWYtYXVzd2Vpc2lkZW50LmVpZC1zZXJ2aWNlLmRlXC9iaXJ0aG5hbWUiOiJHQUJMRVIiLCJpc3MiOiJodHRwczpcL1wvcmVmLWF1c3dlaXNpZGVudC5laWQtc2VydmljZS5kZSIsImh0dHBzOlwvXC9yZWYtYXVzd2Vpc2lkZW50LmVpZC1zZXJ2aWNlLmRlXC9pc3N1aW5nU3RhdGUiOiJEIiwiaHR0cHM6XC9cL3JlZi1hdXN3ZWlzaWRlbnQuZWlkLXNlcnZpY2UuZGVcL2FkZHJlc3NUeXBlIjoiU1RSVUNUVVJFRCIsImh0dHBzOlwvXC9yZWYtYXVzd2Vpc2lkZW50LmVpZC1zZXJ2aWNlLmRlXC9yZXN0cmljdGVkSWQiOiI3YmJiMDRhYzJmY2U1YTY5MWQ0YWRiOWUzOThhNDQwNGQxMWI4ZGY4ZTJkZDI4OWQ4ODA1OWE4NWJhMWU0MWFjIiwiaHR0cHM6XC9cL3JlZi1hdXN3ZWlzaWRlbnQuZWlkLXNlcnZpY2UuZGVcL2RvY3VtZW50VHlwZSI6IklEIiwiZ2l2ZW5fbmFtZSI6IkVSSUtBIiwiaHR0cHM6XC9cL3JlZi1hdXN3ZWlzaWRlbnQuZWlkLXNlcnZpY2UuZGVcL3BsYWNlT2ZCaXJ0aCI6eyJjb3VudHJ5IjpudWxsLCJzdHJlZXRBZGRyZXNzIjpudWxsLCJmb3JtYXR0ZWQiOiJCRVJMSU4iLCJwb3N0YWxDb2RlIjpudWxsLCJsb2NhbGl0eSI6bnVsbCwicmVnaW9uIjpudWxsfSwiYXVkIjoiZERMQlI3VGxiViIsImh0dHBzOlwvXC9yZWYtYXVzd2Vpc2lkZW50LmVpZC1zZXJ2aWNlLmRlXC9hY2FkZW1pY1RpdGxlIjoiIiwiaHR0cHM6XC9cL3JlZi1hdXN3ZWlzaWRlbnQuZWlkLXNlcnZpY2UuZGVcL2RhdGVPZkV4cGlyeSI6IjIwMjctMDQtMDUiLCJuYW1lIjoiRVJJS0EgTVVTVEVSTUFOTiIsImh0dHBzOlwvXC9yZWYtYXVzd2Vpc2lkZW50LmVpZC1zZXJ2aWNlLmRlXC9uYXRpb25hbGl0eSI6Ik5PVF9PTl9DSElQIiwiaWF0IjoxNjA1NTQ1NjQ5LCJmYW1pbHlfbmFtZSI6Ik1VU1RFUk1BTk4iLCJqdGkiOiI5YjU2NzY2Ni03ZTZmLTQxOTYtYmRjZS1iOWRiYmM0ODlhMTEifQ.RyjkvH1Ot9M8fAO8ivJH1ysXw6X00ekCBt4O3jpJGKhPAe8scug6_SgYEpDFs1EYIy9vMJ9NGeTbp2m0JisSzRkEGxxk_dgnWaqBl68U0pB8iYguUHdytnIVAcplrooXuIvlIWgl3N8j1Al933DZ97RevHqvx1eLYjyOhUQwTSgHT6-Hayo2LbuqVGLOxPkvbUqcUyu685o0UiEikqAXYsnSdLGepbDDKo71aqVHv9b_I4vBP2QhMzN4mqJ5-_tP1eDMTFaR7sFpO_RPLNkDiP8Jxb7ayMtAVzasH3cJRWDQJ4uB0W6GFMd-nAqz2_6qgUTzJBhp7eTRP9XodYqUHw"
    },
                                    

  5. Verify `jws`
    Please use the `kid` to verify the authenticity of the `jws`.
    You find the `kid` in the decoded `id_token` which you received together with your `access_token`.
    {
        "kid": "public:74affdfa-195c-4be0-8cef-8f499b6e5b47",
        "alg": "RS256"
    }
                                    

  6. Ensure acr is loa.dipp.2fa and amr contains pin
    "amr": ["pin"], "acr": "loa.dipp.2fa"

    Note: If the `amr` is not set to `pin` the user data is not eIDAS substantial as the authentication of the user was not according to the eIDAS substantial guidelines.

JSON Web Signature (JWS)


Verimi provides all information which were received by the eID in the JSON Web Signature (JWS). The JWS is signed by Verimi.

Standard claims
All non prefixed claims and their values are according the JWT specification.
`iat` claim - (outside of the document claim) contains the time when the Verimi signature was applied.

Customized claims
Customized claims are prefixed with `https://ref-ausweisident.eid-service.de/`.
Note:The aggregated claim `document` is not prefixed.

document claim
The claim `document` contains all collected data from the eID.
The stucture of the claim `document` is based on: https://ref-ausweisident.eid-service.de/.well-known/openid-configuration
`iat` claim - (inside of the document claim) contains the time when the eID was saved.

Verify JWS

  1. Get the RSA key from https://web.uat.verimi.cloud/dipp/api/.well-known/jwks.json
  2. Use the following website to convert the key to pem https://8gwifi.org/jwkconvertfunctions.jsp
  3. Use the following website to decode you JWS and add the signature https://jwt.io


JSON Web Token example The example was retrieved with the `baskets` and is the `value` of endpoint.
{
    "iat": 1606407401,
    "document": {
        "sub": "e746a29e-3625-4f6a-91c4-c92821dc8549",
        "birthdate": "1946-01-25",
        "address": {
        "country": "D",
        "streetAddress": "WEG NR. 12 8E",
        "postalCode": "22043",
        "locality": "HAMBURG"
        },
        "https://ref-ausweisident.eid-service.de/placeOfBirthType": "FREE_TEXT",
        "https://ref-ausweisident.eid-service.de/birthname": "Weiß",
        "iss": "https://ref-ausweisident.eid-service.de",
        "https://ref-ausweisident.eid-service.de/issuingState": "D",
        "https://ref-ausweisident.eid-service.de/addressType": "STRUCTURED",
        "https://ref-ausweisident.eid-service.de/restrictedId": "31974c6c3c11d90e3a57b16c042a657eda5466acb3aa45a416e81604bd8679f9",
        "https://ref-ausweisident.eid-service.de/documentType": "ID",
        "given_name": "Hans-Günther",
        "https://ref-ausweisident.eid-service.de/placeOfBirth": {
        "formatted": "BREMERHAVEN"
        },
        "aud": "FHtQpD5U3t",
        "https://ref-ausweisident.eid-service.de/academicTitle": "Dr.eh.Dr.",
        "https://ref-ausweisident.eid-service.de/dateOfExpiry": "2027-04-05",
        "name": "Hans-Günther von Drebenbusch-Dalgoßen",
        "https://ref-ausweisident.eid-service.de/nationality": "NOT_ON_CHIP",
        "iat": 1587738463,
        "family_name": "von Drebenbusch-Dalgoßen",
        "jti": "97d141aa-4260-492d-888a-aa32750071e5"
    }
    }

IDTOKEN

{
    "acr": "loa.dipp.2fa",
    "at_hash": "VCj-IEnIqReHuEMvu7f58Q",
    "aud": "lufthansa",
    "auth_time": 1596021334,
    "exp": 1596024943,
    "iat": 1596021343,
    "iss": "https://www-dev9.dev.sandbox.verimi.cloud/",
    "jti": "2265fd91-74bf-419a-a888-5302147e83da",
    "nonce": "",
    "rat": 1596021311,
    "sid": "3ea5d215-5e1d-479d-8364-7db94b7e5271",
    "sub": "1c9ed1b8-08d1-4f36-91f2-f2deb70f67c3",
    "amr": ["pin"]
}

When you receive `"acr":"loa.dipp.2fa"` and `"amr":"pin"`, than the authentication has been successful using the 2FA and PIN.

When you receive "acr":"loa.dipp.2fa" and no amr value, than the authentication has been successful using the 2FA and either PIN or the biometrics.

Application tokens

Verimi provides three types of tokens for API calls:

  • access token
  • id token
  • refresh token

Access Token

Access Token is the main object for sharing authorization information between parties in OAuth2.0 protocol flow.

Access token is hash string value.
It is resolved on Verimi side to full business object of consents given by Identity Owner to Service Provider.

An example value looks like following:

"access_token": "DKwdOFKbSEMkCDI8ooN3uzCtcmX1BHOUSn69Tx2Qx6w.uiI6SU3QOt5Ok_6C5tAh-dYdQ53BE5vqHoGrEwaLXtQ"

ID Token

ID Token comes from OpenId Connect 1.0 specification and is an object which represents Verimi best knowledge about Identity Owner identity.
ID token is JWS (JWT with signature) object, example value:

"id_token": "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiI1NmUwODNjZS0wN2E3LTQ5MGUtOGY1YS0yNjFjZThiYzZiY2IiLCJpYXQiOjE1MDc3MjAyNzAsImV4cCI6MTUwNzcyMTE3MCwiaXNzIjoiaHR0cHM6Ly9hcGkuZGlwcC5kZSIsImF1ZCI6ImNkOWUyMTNmLTE1NDEtNDFiYS05OWY1LWY2YTNhNTcxNTBlMSIsIm5vbmNlIjoibi0wUzZfV3pBMk1qIiwiYWNyIjoibG9hLmRpcHAuZGVmYXVsdCIsImV1dWlkIjoiNTZlMDgzY2UtMDdhNy00OTBlLThmNWEtMjYxY2U4YmM2YmNiIn0.OLlR-2V5zqDckIehgUC4iOqpQgxhZB_jzTI-OoghWZI64Au69l2YpX7xHPrDtWeEb4CfVK0Mo8aJX4FE9bPklqGtHLOADs9ie5vCpwX8Jl5Da1uprDrsRTzW_ijG5JqhnYDL5dM9Ubu-OrLpEI1tiPaBGeyoXNZ9S3hOCH0XS7zOGzg4V4-zbvtcyXq7uuK8lVGUWan1Xib_zfpGugOnf3W7YNVicxac2rotmoIvwZjai8zB3QSybXRVmPPha3INqC7aGFoWTR1yHlZbcfKFIqWMDAFhXYbzU3ddMsu-tr_Iyr8etK0E-EYPF7zgRSXWs9r4H6oA-nRm58vzEJsB5w"
This ID token decodes into following data:
{
    "1fa_password_reset_time": 1556182725,
    "acr": "loa.dipp.default",
    "sub": "56e083ce-07a7-490e-8f5a-261ce8bc6bcb",
    "aud":"Your Client ID",
    "auth_time": 1557306724,
    "exp": 1507723870,
    "iat": 1507720270,
    "iss": "https://web.uat.verimi.cloud/",
    "jti": "f8cae93e-07c9-4ad8-b34b-d7937acfc571",
    "nonce": "",
    "rat": 1557306680
}

Refresh Token

Refresh token is part of the OAuth2.0 specification. With its usage the Service Provider can again get a valid Access Token once a previous Access Token has expired. To use this you need to add the offline scope to your auth request. Please refer to the specification.

To get a new access token and a corresponding refresh token you need to do a POST request to the token endpoint with the following parameters. You will receive a new access token and a new refresh token. You need to add Authorization (Base64 client:secret) to the header in order to get a valid response.

Example value (This is not JWT Token):

 POST https://api.uat.verimi.cloud/oauth2/token?grant_type=refresh_token&refresh_token=tU2rp6ZuvAFP-i8J9I9c...
"refresh_token": "tU2rp6ZuvAFP-i8J9I9ca0YopotXdgx-lrTiyQ07dYo.9m0pRYVBQLTGqZF8gmG1T-iIQGcMk1btREqdR7bumiE",

Token Signature verification

The header part of a JWS(JWT) specifies the cryptographic operations applied to the JWT and optionally, additional properties of the JWT. Token signature can be verified against public key of Verimi.

Verimi token signature public key is available as JWKS_URI parameter of discovery service.

Scopes

The scopes are separated into two kinds mandatory and optional scopes.

Optional scopes: The user has the option whether he wants to share personal information via the consent page.
Mandatory scopes: The user has no option whether he wants to share personal information via the consent page.

All scopes:


login
openid
offline
write_basket
email
name
phone
birthdate
age_above_16
age_above_18
age_above_21
address
idcard
passport
taxid
paymentdata
frequent_travel_route_start // currently unsupported
frequent_travel_route_destination // currently unsupported
frequent_travel_route_seat_preference // currently unsupported
frequent_travel_route_class_preference // currently unsupported
frequent_travel_route_language_preference // currently unsupported
frequent_travel_route_food_preference // currently unsupported

Optional scopes:


name
address
birthdate
age_above_16
age_above_18
age_above_21
taxid
paymentdata

Mandatory scopes:


email
phone
idcard
passport                

Basket examples

Email: The field "contact" acts as a flag that determines if a particular e-mail address should be used to send notifications messages to the Identity Owner. Verimi requires the verification of email addresses via OTP to add them to an identitiy owner's account.

{
   "scopeId":"email",
   "data":[
      {
         "name":"email",
         "value":"mustermann@example.com"
      },
      {
         "name":"standard",
         "value":"true"
      },
      {
         "name":"verified",
         "value":"true"
      },
      {
         "name":"contact",
         "value":"true"
      }
   ]
}

Example of basket data: name, where title is gender specific:

{
   "scopeId":"name",
   "data":[
      {
         "name":"firstName",
         "value":"ERIKA"
      },
      {
         "name":"lastName",
         "value":"MUSTERMANN"
      },
      {
         "name":"title",
         "value":"ms"
      },
      {
         "name":"academicTitle",
         "value":"DR."
      },
      {
         "name":"verified",
         "value":"true"
      }
   ]
}
Phone: The field "contact" acts as a flag that determines if a particular phone number should be used to send notifications messages to the Identity Owner. Verimi requires the verification of phone numbers via OTP to add them to an identitiy owner's account.
{
   "scopeId":"phone",
   "data":[
      {
         "name":"phone",
         "value":"789101112"
      },
      {
         "name":"countryCode",
         "value":"34"
      },
      {
         "name":"standard",
         "value":"false"
      },
      {
         "name":"verified",
         "value":"true"
      },
      {
         "name":"contact",
         "value":"false"
      }
   ]
}
Birthdate: (The format is an ISO 8601 date, i.e. YYYY-MM-DD)
{
   "scopeId":"birth",
   "data":[
      {
         "name":"dateOfBirth",
         "value":"1987-11-23"
      },
      {
         "name":"verified",
         "value":"true"
      }
   ]
}
age_above_??: Scope will be returned only when IO has a verified document with date of birth.
{
   "scopeId":"age_above_16|age_above_18|age_above_21",
   "data":[
      {
         "name":"valid",
         "value":"true"
      }
   ]
}

Example of Address:

{
   "scopeId":"address",
   "data":[
      {
         "name":"firstName",
         "value":"ERIKA"
      },
      {
         "name":"lastName",
         "value":"MUSTERMANN"
      },
      {
         "name":"title",
         "value":"ms"
      },
      {
         "name":"academicTitle",
         "value":"DR."
      },
      {
         "name":"firm",
         "value":""
      },
      {
         "name":"country",
         "value":"Germany"
      },
      {
         "name":"countryCode",
         "value":"DEU"
      },
      {
         "name":"zipCode",
         "value":"12059"
      },
      {
         "name":"city",
         "value":"Berlin"
      },
      {
         "name":"street",
         "value":"MUSTERSTR"
      },
      {
         "name":"number",
         "value":"12 A"
      },
      {
         "name":"standard",
         "value":"true"
      },
      {
         "name":"addressInfo",
         "value":""
      },
      {
         "name":"verified",
         "value":"true"
      }
   ]
}

Example of idcard|passport:

{
   "scopeId":"idcard|passport",
   "data":[
      {
         "name":"documentType",
         "value":"ID_CARD|PASSPORT"
      },
      {
         "name":"idCardNumber|passportNumber",
         "value":"L12345678"
      },
      {
         "name":"academicTitle",
         "value":"DR."
      },
      {
         "name":"firstName",
         "value":"JOHANNA EDELTRAUT LISBETH"
      },
      {
         "name":"lastName",
         "value":"MUSTERMANN"
      },
      {
         "name":"maidenName",
         "value":""
      },
      {
         "name":"pseudonym",
         "value":""
      },
      {
         "name":"birthDate",
         "value":"1987-11-23"
      },
      {
         "name":"birthPlace",
         "value":"Berlin"
      },
      {
         "name":"citizenship",
         "value":"german"
      },
      {
         "name":"issueDate",
         "value":"2015-11-23"
      },
      {
         "name":"issuingAuthority",
         "value":"Berlin"
      },
      {
         "name":"validUntil",
         "value":"2025-11-23"
      },
      {
         "name":"address",
         "value":"MUSTERSTR 12 A 12059 Berlin Germany"
      },
      {
         "name":"verificationMethod",
         "value":"VIDEO_LEGITIMATION"
      },
      {
         "name":"added",
         "value":"2018-12-31"
      },
      {
         "name":"title",
         "value":"ms"
      },
      {
         "name":"verificationDate",
         "value":"2018-12-31T15:30:15.678"
      }
   ]
}

Example of taxid - This value must exclusively be used for tax related processing, i.e. no other use cases are permissible.

{
   "scopeId":"taxid",
   "data":[
      {
         "name":"taxId",
         "value":"12345678901"
      },
      {
         "name":"verified",
         "value":"false"
      }
   ]
}

Example of paymentdata:

{
   "scopeId":"paymentdata",
   "data":[
      {
         "name":"bic",
         "value":"DEUTDEFFXXX"
      },
      {
         "name":"iban",
         "value":"DE00500700101234567800"
      },
      {
         "name":"fullName",
         "value":"ms Erika Mustermann"
      },
      {
         "name":"verified",
         "value":"true"
      },
      {
         "name":"paymentActive",
         "value":"false"
      }
   ]
}

Example of frequent_travel_route_: (currently unsupported)

[
   {
      "scopeId":"frequent_travel_route_start",
      "data":[
         {
            "name":"value",
            "value":"TXL"
         }
      ]
   },
   {
      "scopeId":"frequent_travel_route_destination",
      "data":[
         {
            "name":"value",
            "value":"FRA"
         }
      ]
   },
   {
      "scopeId":"frequent_travel_route_seat_preference",
      "data":[
         {
            "name":"value",
            "value":"WINDOW"
         }
      ]
   },
   {
      "scopeId":"frequent_travel_route_class_preference",
      "data":[
         {
            "name":"value",
            "value":"ECONOMY_CLASS"
         }
      ]
   },
   {
      "scopeId":"frequent_travel_route_language_preference",
      "data":[
         {
            "name":"value",
            "value":"EN"
         }
      ]
   },
   {
      "scopeId":"frequent_travel_route_food_preference",
      "data":[
         {
            "name":"value",
            "value":"VEGETARIAN"
         }
      ]
   }
]

Data transfer to Verimi

In this section, we explain how a Service Provider can send the Identity Owner's data to Verimi. The transfer of Identity Owner's data from the Service Provider is similar to the transfer from Verimi, however this time the Service Provider is the one who will fill the Basket.

Used scopes
login (required)

The big difference to previous example is that, after obtaining an Access Token, the Service Provider should call the Fill Basket Data endpoint. This is the:

PUT https://api.uat.verimi.cloud/command/serviceprovider-data-import-baskets

under the API Documentation. The response looks like following:

Response of the step FillBasketResponse :
{
    "basketId":"50554d6e-29bb-11e5-b345-fefms5lcdc9f",
    "dataScopes":[
        {
            "name":"phone",
            "value":"789101112"
        }
    ],
    "links" : [
        {
            "rel": "confirmation_redirect",
            "href": "/account.html#/services/myservices/p/dataoverview?loginSuccess=true&method=spimport&basketId=50554d6e-29bb-11e5-b345-fefms5lcdc9f"
        }
    ]
}

Now Identity Owner needs to confirm the data transfer. Therefore, following steps needed to be taken:

Confirm basket redirection

To finalize and save the created basket content in Verimi, a redirection back to the Verimi portal is required. The redirection url is defined in the response's 'links' section.

Confirmation links are to be found in the Link item, whose 'rel' property is 'confirmation_redirect'.

The found item's 'href' property defines the redirection URI.

The confirmation URI might be extended by an extra query parameter called redirectOnContinue, which defines where to end the flow (either Verimi Portal - default or back on Service Provider pages).

Decision about when to end the flow is taken at the Service Provider's side. Service Provider also needs to decorate the Bearer Authorization header in the request.

Extra Parameter Description

Name Mandatory Description
redirectOnContinue No Parameter is defined: At the end of the basket confirmation flow, the browser will be redirected to the URL defined in this parameter!

Parameter is NOT defined: No navigation will happen. The control stays on the Verimi Portal.

Accounts linking

Connect User Accounts

When the Service Provider retrieved the basket and the sub is linked to the User on Service Provider side, then the Service Provider MUST invoke the linkage endpoint in order to explicitly notify Verimi that the connection has been established on Service Provider's side. As a result of this call, Verimi marks the Service Provider for the Identity Owner as linked, so the user can review the list of Service Providers that his account is connected to.

When the account is linked to a certain Service Provider the Service Provider Tile goes to the topmost of the Identity Owners account, so he can directly login via Verimi Tile to the service Provider site.

PUT https://api.uat.verimi.cloud/users/linkage
Header:
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxND...     //Access token of user

Disconnect User Accounts

The connection created between user and Service Provider can be terminated on both ends.

Disconnect on Service Provider's side

When the user decides to disconnect the Verimi account from the Service Provider account on the portal of the Service Provider, then the Service Provider MUST call the disconnect notification endpoint in order to notify Verimi about the user's intent.

DELETE https://api.uat.verimi.cloud/users/linkage
Header:
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxND...     //Access token of user

In some scenarios the Service Provider does not have a valid access token or refresh token for the given Identity Owner. In this case the Service Provider shall call the below endpoint for letting Verimi know about the disconnection of the Identity Owner's Verimi account from the Service Provider. The below endpoint requires basic authentication in the Authorization header.

DELETE /users/{sub}/linkage
Headers:
Authorization: Basic QUNNRTpHfDQxfDBhbjE4WklzX3c=     // base64(client_id:client_secret)
Parameters:
sub                    // path parameter, the sub of the user who wants to disconnect

Disconnect on Verimi side

On Verimi side, disconnect can be performed using two approaches: either with using the notification endpoint or using a Service Provider Site. Decision on this is given to Service Provider. If SP would like to be the active part of disconnection, it needs to provide a disconnect URL.

When Service Provider does not provide a disconnect URL, then SP needs to call the dedicated notification API to learn about disconnection. Please have in mind that entries older than a week are evicted by Verimi and cannot be retrieved.
Verimi provides list of sub's of the users who have decided to disconnect their accounts by following call (to be used in polling on SP side):

GET /service-providers/disconnected-users?start=1524666367&end=1524666668
Header:
Authorization: Basic QUNNRTpHfDQxfDBhbjE4WklzX3c=     // base64(client_id:client_secret)
Parameters:
Name Mandatory Description
start Yes EPOCH format in UTC timezone, only those entries are going to be returned where the disconnectTime > start
end No EPOCH format in UTC timezone, only those entries are going to be returned where the disconnectTime > start
Parameter not definedEndpoint returns all the disconnected users from the specified start timestamp
Response format:
[{
  "sub":"e58637de-4896-11e8-842f-0ed5f89f718b",
  "disconnectTime": 1524666368
},
{
     "sub":"e58637de-4896-11e8-842f-0ed5f89f718a",
     "disconnectTime": 1524661234
}]
            

If the Service Provider has provided a disconnect URL during the integration with the Verimi platform, then Verimi will forward the user to it, to finish the disconnection procedure. The Service Provider can then offer incentives, in order to motivate the user to stay connected with the Service Provider. If the Identity Owner decides to stay connected, then the flow ends. If not, then the disconnect MUST be triggered from Service Provider and the Service Provider MUST notify Verimi using the same delete linkage endpoint, which is used when the flow is started on Service Provider's side.

Retrieve updates on user data

The Service Provider may call Verimi notification endpoint in order to retrieve changes in user data. Once notification is available, Service Provider has to connect with username password and certificate (see Data transfer from Verimi), and can query changed data.

Flow:


There are 2 variants, we suggest using variant B, since it additionally hands out status (published or deleted).

Variant A

GET https://api.uat.verimi.cloud/notifications?start=1524666367&end=1524666668

Returns a JSON Object containing the notification entries matching with the query parameters.

Headers:

Content-Type: application/json
Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l //base64(clientid:clientsecret)

The service provider shall use basic authentication using the Verimi clientId and clientSecret pairs.

Parameters:

Name Mandatory Description
start Yes EPOCH format in UTC timezone, only those entries are going to be returned where the changeTime > start
end No EPOCH format in UTC timezone, only those entries are going to be returned where the changeTime < end
Parameter not definedEndpoint returns all the changed users till the current time
  • The date range must be smaller than 1 week (7 days)
  • Entries older than a week are evicted and cannot be retrieved any more
  • Requests for more than 7 days wont be handled at all

Response Format:

{
            "version": "https://jsonfeed.org/version/1",
            "title": "Service Provider notification endpoint",
            "_serviceProviderName": "Your client ID",
            "home_page_url": "https://www.verimi.com",
            "feed_url": "https://api.uat.verimi.cloud/notifications",
            "items": [{
                "id": "b569d17f-2697-4d08-bd8e-d91b1e7897ad",
                "content_text": "autopush",
                "_sub": "805a0561-4314-4b5f-af87-1b05cf656fc0""url": "https://api.uat.verimi.cloud/v2/query/baskets/e4942fc8-9ffc-4aec-bd19-7aa6f80a7b33?type=autopush",
                "date_published": "2018-05-08T14:04:00+01:00"
            },
            {
                "id": "ab9970ed-6397-4f22-af43-3a14be543c7e",
                "content_text": "autopush",
                "_sub": "805a0561-4314-4b5f-af87-1b05cf656fc0""url": "https://api.uat.verimi.cloud/v2/query/baskets/e4942fc8-9ffc-4aec-bd19-7aa6f80a7b34?type=autopush""date_published": "2018-05-08T15:04:00+01:00"
            }]
        }

The Feed items contain the following fields:

  • id (mandatory): the unique id of the notification entry.
  • content_text (mandatory): the type of the notification. (autopush). The Service Provider must accept any kind of notification. Based on the type, the Service Provider can decide to process or drop the notification.
  • _sub (mandatory): if content_text = "autopush" then this attribute is present. The field contains the identifier of the user.
  • url (optional): if content_text = "autopush" then this attribute is present. The field contains the URL for the basket which holds the data the user intended to push to Service Provider.

We recommend polling the notification endpoint once in a minute.

According to the JSONFeed specification (https://jsonfeed.org) the feed schema can be extended with custom fields. Modifications are planned to be added in backward compatible way. In order to roll out these changes continuously and smoothly the Service Provider is required to ignore the not-specified fields.

Variant B

This variant provides additional markers to allow identify blocks of data and reason of change (if applicable):

GET https://api.uat.verimi.cloud/dipp/api/v2/query/baskets/{basketId}?type=autopush

Parameters:

  • basketId required, id of autopush basket
  • type required, allowed value autopush

Headers:

Content-Type: application/json
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ... 

The Access Token must contain read_basket scope


Response:

This example shows an address entity, which has been shared recently.

{
            "basketId": "abe98c85-b4f4-4109-bfca-0b4dc6bfb021",
            "dataScopes": [{
                "scopeId": "address",
                "entityId": "1ef7699b-24d0-4403-a4c3-35382218b09f",
                "status": "published",
                "data": [{
                    "name": "firstName",
                    "value": "John"
                },
                {
                    "name": "lastName",
                    "value": "Smith"
                },
                {
                    "name": "title",
                    "value": ""
                },
                {
                    "name": "country",
                    "value": "Austria"
                }]
            }]
        }

The basket endpoint will deliver entityId and status fields in addition to the data sets.

Using the entityId field, Service Provider will be able to identify subsequent updates for a certain entity.

The status field enables the Service Provider to differentiate the following scenario:

  • published – object was created or updated
  • deleted – object has been deleted on Verimi side

If the country has been changed, the basket content will be:

{
            "basketId": "abe98c85-b4f4-4109-bfca-0b4dc6bfb022",
            "dataScopes": [{
                "scopeId": "address",
                "entityId": "1ef7699b-24d0-4403-a4c3-35382218b09f",
                "status": "published",
                "data": [{
                    "name": "firstName",
                    "value": "John-updated"
                },
                {
                    "name": "lastName",
                    "value": "Smith-updated"
                },
                {
                    "name": "title",
                    "value": "Mr"
                },
                {
                    "name": "country",
                    "value": "Germany"
                }]
            }]
        }

And finally when the user removes this address in Verimi, then Service Provider can get notified about this with the following basket content:

{
            "basketId": "abe98c85-b4f4-4109-bfca-0b4dc6bfb023",
            "dataScopes": [{
                "scopeId": "address",
                "entityId": "1ef7699b-24d0-4403-a4c3-35382218b09f",
                "status": "deleted",
                "data": null
            }]
        }

For writing the basket, the following endpoint can be used:

PUT https://api.uat.verimi.cloud/dipp/api/v2/typed-basket
{
    "serviceProviderId": "[your service provider id]",
    "dataScopes": {
        "email": {
            "id": "6b688440-1eee-3ee8-a0a0-8f8c5782f939",
            "emailAddress": "email@address.com",
            "verified": true,
            "standard": true,
            "contact": true
        },
        "address": {
            "id": "a8022633-b44d-4ba3-b38e-d98327758bed",
            "addressInfo": "",
            "firstName": "Will",
            "lastName": "Smith",
            "title": "mr",
            "academicTitle": "",
            "firm": "Hollywood",
            "street": "Hollystreet",
            "number": "1",
            "zipCode": "55590",
            "city": "Meisenheim",
            "country": "Germany",
            "countryCode": null,
            "verified": false,
            "standard": false
        },
        "name": {
            "id": "e9267c09-0e73-4d6b-b5ef-2d6d71dff732",
            "firstName": "Will",
            "lastName": "Smith",
            "title": "mr",
            "academicTitle": "",
            "verified": false
        },
        "phone": {
            "id": null,
            "internationalAreaCode": null,
            "areaCode": null,
            "telephoneNumber": null,
            "verified": null,
            "standard": null,
            "contact": null
        },
        "birthDate": {
            "id": null,
            "dateOfBirth": null,
            "verified": null
        }
    }
}

Payload Signing

Personal documents (e.g. ID card) can contain additional signature information in the basket response depending on the verification method used during data import. Currently there are two use cases supported:

  • Document source signs the data (e.g. JWS from AusweisIDent service) and this information is passed as proof of origin
  • Verimi obtains unsigned data and signs it (e.g. the verified document was imported from Deutsche Bank)

In the basket response signature information is available in an additional jws field:

{
            "basketId": "bbdc7b18-f4fa-48cd-b694-98decf3f7cae",
            "dataScopes": [{
                "scopeId": "idcard",
                "data": [{
                    "name": "documentType",
                    "value": "ID_CARD"
                },
                {
                    "name": "idCardNumber",
                    "value": "2536051176"
                },
                {
                    "name": "academicTitle",
                    "value": ""
                },
                {
                    "name": "firstName",
                    "value": "Beatrice"
                },
                {
                    "name": "lastName",
                    "value": "Verimi"
                },
                {
                    "name": "maidenName",
                    "value": ""
                },
                {
                    "name": "pseudonym",
                    "value": ""
                },
                {
                    "name": "birthDate",
                    "value": "1976-09-09"
                },
                {
                    "name": "birthPlace",
                    "value": "Berlin"
                },
                {
                    "name": "citizenship",
                    "value": "german"
                },
                {
                    "name": "issueDate",
                    "value": "2017-05-02"
                },
                {
                    "name": "issuingAuthority",
                    "value": "Stadt Bonn"
                },
                {
                    "name": "validUntil",
                    "value": "2027-05-01"
                },
                {
                    "name": "address",
                    "value": "Jacobistr 6 40211 Düsseldorf Germany"
                },
                {
                    "name": "verificationMethod",
                    "value": "DEUTSCHE_BANK"
                },
                {
                    "name": "added",
                    "value": "2019-01-15"
                },
                {
                    "name": "titleOfNobility",
                    "value": "Freifrau von"
                },
                {
                    "name": "cavState",
                    "value": "APPROVED"
                },
                {
                    "name": "cavTimestamp",
                    "value": ""
                },
                {
                    "name": "title",
                    "value": "ms"
                },
                {
                    "name": "verificationDate",
                    "value": "2019-01-15T12:10:42.431"
                },
                {
                    "name": "jws",
                    "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImprdSI6Imh0dHBzOi8vdmVyaW1pLXVhdC5jb3JldHJhbnNmb3JtLmNvbS8ud2VsbC1rbm93bi9qd2tzLmpzb24iLCJraWQiOiJwdWJsaWM6YWE1NDc3NzUtNDc1OS00MGY5LTljMzEtYmQyMzQwMTkxMzIzIn0.eyJpYXQiOjE1NDc1NTQyNDIsImRvY3VtZW50Ijoie1wiZG9jdW1lbnRUeXBlXCI6XCJJRF9DQVJEXCIsXCJpZENhcmROdW1iZXJcIjpcIjI1MzYwNTExNzZcIixcImFjYWRlbWljVGl0bGVcIjpcIlwiLFwidGl0bGVPZk5vYmlsaXR5XCI6XCJGcmVpZnJhdSB2b25cIixcInRpdGxlXCI6XCJtc1wiLFwibWFpZGVuTmFtZVwiOlwiXCIsXCJiaXJ0aERhdGVcIjpcIjE5NzYtMDktMDlcIixcImJpcnRoUGxhY2VcIjpcIkJlcmxpblwiLFwiY2l0aXplbnNoaXBcIjpcIkRFVVwiLFwiaXNzdWVEYXRlXCI6XCIyMDE3LTA1LTAyXCIsXCJpc3N1aW5nQXV0aG9yaXR5XCI6XCJTdGFkdCBCb25uXCIsXCJ2YWxpZFVudGlsXCI6XCIyMDI3LTA1LTAxXCIsXCJjYXZTdGF0ZVwiOlwiQVBQUk9WRURcIixcImFkZHJlc3NcIjpcIkphY29iaXN0ciA2IDQwMjExIETDvHNzZWxkb3JmIGRlXCIsXCJpZFwiOlwiNjMyY2ZjOTgtMGI5Yy00NTc2LTk2ODAtMjE3NzNkMWYxNGQ2XCIsXCJ0eXBlXCI6XCJJRF9DQVJEXCIsXCJmaXJzdE5hbWVcIjpcIkJlYXRyaWNlXCIsXCJsYXN0TmFtZVwiOlwiVmVyaW1pXCIsXCJhZGRlZFwiOntcInllYXJcIjoyMDE5LFwibW9udGhcIjoxLFwiZGF5XCI6MTV9LFwidmVyaWZpY2F0aW9uUHJvY2Vzc1N0YXR1c1wiOntcImRvY3VtZW50U3RhdGVcIjpcIlZFUklGSUVEXCIsXCJ2ZXJpZmljYXRpb25NZXRob2RcIjpcIkRFVVRTQ0hFX0JBTktcIn0sXCJ2ZXJpZmljYXRpb25EYXRlXCI6e1wiZGF0ZVwiOntcInllYXJcIjoyMDE5LFwibW9udGhcIjoxLFwiZGF5XCI6MTV9LFwidGltZVwiOntcImhvdXJcIjoxMixcIm1pbnV0ZVwiOjEwLFwic2Vjb25kXCI6NDIsXCJuYW5vXCI6NDMxMDAwMDAwfX19In0.wGouq4n1D_6zOf1hD5gneQmWKRhkAg5a7NCFRZLwNZ2aSQu7e8iCK6ZEikjP3pOFOQhGyx3bCzwCPDbPQS9kGr0qPah8GYnLJa7xwgYe9-FfYlkrExLlUsoCfwstvLC1Q1ueTYppg477v9Gi6IbHxIphzDvAF0vEKZHaR-r0jqGcY77Ul_Dvb-MYvoEkp71MxLw5a4A8ZXpT90TzlfzcCzN5EYDLeQHP4jg34eTEPgoo9jXhn9E40R_ph4xHXk-aLg00JQ99UMurscSWH46h1wJViSrSWmJm-7TAOA-sJtBeGM6kwDwkOCaWGeOK5bSQqcibn4ElqwCqIrjXJydFQw"
                }]
            }]
        }

The JWS value:

{
    "name":"jws",
    "value":"yJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImprdSI6Imh0dHBzOi8vdmVyaW1pLXVhdC5jb3JldHJhbnNmb3JtLmNvbS8ud2VsbC1rbm93bi9qd2tzLmpzb24iLCJraWQiOiJwdWJsaWM6YWE1NDc3NzUtNDc1OS00MGY5LTljMzEtYmQyMzQwMTkxMzIzIn0.eyJpYXQiOjE1NDc1NTQyNDIsImRvY3VtZW50Ijoie1wiZG9jdW1lbnRUeXBlXCI6XCJJRF9DQVJEXCIsXCJpZENhcmROdW1iZXJcIjpcIjI1MzYwNTExNzZcIixcImFjYWRlbWljVGl0bGVcIjpcIlwiLFwidGl0bGVPZk5vYmlsaXR5XCI6XCJGcmVpZnJhdSB2b25cIixcInRpdGxlXCI6XCJtc1wiLFwibWFpZGVuTmFtZVwiOlwiXCIsXCJiaXJ0aERhdGVcIjpcIjE5NzYtMDktMDlcIixcImJpcnRoUGxhY2VcIjpcIkJlcmxpblwiLFwiY2l0aXplbnNoaXBcIjpcIkRFVVwiLFwiaXNzdWVEYXRlXCI6XCIyMDE3LTA1LTAyXCIsXCJpc3N1aW5nQXV0aG9yaXR5XCI6XCJTdGFkdCBCb25uXCIsXCJ2YWxpZFVudGlsXCI6XCIyMDI3LTA1LTAxXCIsXCJjYXZTdGF0ZVwiOlwiQVBQUk9WRURcIixcImFkZHJlc3NcIjpcIkphY29iaXN0ciA2IDQwMjExIETDvHNzZWxkb3JmIGRlXCIsXCJpZFwiOlwiNjMyY2ZjOTgtMGI5Yy00NTc2LTk2ODAtMjE3NzNkMWYxNGQ2XCIsXCJ0eXBlXCI6XCJJRF9DQVJEXCIsXCJmaXJzdE5hbWVcIjpcIkJlYXRyaWNlXCIsXCJsYXN0TmFtZVwiOlwiVmVyaW1pXCIsXCJhZGRlZFwiOntcInllYXJcIjoyMDE5LFwibW9udGhcIjoxLFwiZGF5XCI6MTV9LFwidmVyaWZpY2F0aW9uUHJvY2Vzc1N0YXR1c1wiOntcImRvY3VtZW50U3RhdGVcIjpcIlZFUklGSUVEXCIsXCJ2ZXJpZmljYXRpb25NZXRob2RcIjpcIkRFVVRTQ0hFX0JBTktcIn0sXCJ2ZXJpZmljYXRpb25EYXRlXCI6e1wiZGF0ZVwiOntcInllYXJcIjoyMDE5LFwibW9udGhcIjoxLFwiZGF5XCI6MTV9LFwidGltZVwiOntcImhvdXJcIjoxMixcIm1pbnV0ZVwiOjEwLFwic2Vjb25kXCI6NDIsXCJuYW5vXCI6NDMxMDAwMDAwfX19In0.wGouq4n1D_6zOf1hD5gneQmWKRhkAg5a7NCFRZLwNZ2aSQu7e8iCK6ZEikjP3pOFOQhGyx3bCzwCPDbPQS9kGr0qPah8GYnLJa7xwgYe9-FfYlkrExLlUsoCfwstvLC1Q1ueTYppg477v9Gi6IbHxIphzDvAF0vEKZHaR-r0jqGcY77Ul_Dvb-MYvoEkp71MxLw5a4A8ZXpT90TzlfzcCzN5EYDLeQHP4jg34eTEPgoo9jXhn9E40R_ph4xHXk-aLg00JQ99UMurscSWH46h1wJViSrSWmJm-7TAOA-sJtBeGM6kwDwkOCaWGeOK5bSQqcibn4ElqwCqIrjXJydFQw"
}

Decoded payload data:

{
    "iat": 1547554242,
    "document": {
        "documentType": "ID_CARD",
        "idCardNumber": "2536051176",
        "academicTitle": "",
        "titleOfNobility": "Freifrau von",
        "title": "ms",
        "maidenName": "",
        "birthDate": "1976-09-09",
        "birthPlace": "Berlin",
        "citizenship": "DEU",
        "issueDate": "2017-05-02",
        "issuingAuthority": "Stadt Bonn",
        "validUntil": "2027-05-01",
        "cavState": "APPROVED",
        "address": "Jacobistr 6 40211 Düsseldorf de",
        "id": "632cfc98-0b9c-4576-9680-21773d1f14d6",
        "type": "ID_CARD",
        "firstName": "Beatrice",
        "lastName": "Verimi",
        "added": {
            "year": 2019,
            "month": 1,
            "day": 15
        },
        "verificationProcessStatus": {
            "documentState": "VERIFIED",
            "verificationMethod": "DEUTSCHE_BANK"
        },
        "verificationDate": {
            "date": {
                "year": 2019,
                "month": 1,
                "day": 15
            },
            "time": {
                "hour": 12,
                "minute": 10,
                "second": 42,
                "nano": 431000000
            }
        }
    }
}

The header contains the JWKS location and key ID as well which can be used to verify the signature. Example JWKS value:

{
    "keys": [
        {
            "kty": "RSA",
            "e": "AQAB",
            "use": "sig",
            "kid": "public:aa547775-4759-40f9-9c31-bd2340191323",
            "n": "06GQrL_yLyMBiQS8aoe5u0AvT-fgssOITOFT-X9LrRazxXJeZNgxmrMm66-TOIzn6IW--9QpHN6BPcItEQ8uxPIgvJjIL2q-Lc56jCuu7Wo07w7qhlaUGs-UofIRjduZMZ9Nyvi2EQofYo_eIjiootmU4_Dk7uTxT8TYA63-DDMrtElQezRIHZQ_vQzyj-10jNV1ILOniY4Iq6sWqmHTubKGjRgqKKdPF0aBNBkQ5RxdylZFlsqNU9YAciYiAa5TshOxRwvlF8hs3Jx8pG2Ss3dJoxZQwfTWummsR18GfrfIzfh4GZDI6IpdR0Ts1K842D9htQk1nVGvkjrCRUwGPQ"
        }
    ]
}

Using Refresh Token

When Service Provider receives notification about user data change, a valid Access Token is required to call the basket endpoint and get updated user data.

The Access Token - that was obtained when the user shared data - could be used. However, the Access Token by definition is a short living object which is valid for some minutes (e.g.: 15 minutes), so it might have expired. In such cases Service Provider can use refresh token that was obtained together with Access Token. This is a long living object which remains valid for longer time (e.g.: 30 days). Solely purpose of Refresh Token is to provide a way to get new valid Access Token, therefore Service Provider should persist it and use whenever Access Token is expired.

Example call for new valid Access Token with usage of Refresh Token

POST https://api.uat.verimi.cloud/dipp/api/oauth2/token?grant_type=refresh_token&refresh_token=[refresh_token]
"Content-Type": "application/x-www-form-urlencoded"
"Authorization": "Basic QUNNRTpHfDQxfDBhbjE4WklzX3c=" // Client ID client secret of Service Provider
Result:
{
    "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI1NmUwODNjZS0wN2E3LTQ5MGUtOGY1YS0yNjFjZThiYzZiY2IiLCJzY29wZSI6WyJsb2dpbiIsIndyaXRlX2Jhc2tldCJdLCJiYXNrZXRfaWQiOiI4ZjFiNjlhNS00MTU5LTQwZjYtYTIxYS1jOGU4YjI4MTk1NzEiLCJleHAiOjE1MDc3MjU2MzMsImlhdCI6MTUwNzcyMjAzMywiYXV0aG9yaXRpZXMiOlsiUk9MRV9UUlVTVEVEX0NMSUVOVCIsIlJPTEVfQ0xJRU5UIl0sImp0aSI6IjQzMTIyZWNlLTdkZDktNDJjMC1iZmZhLWRlZGRjMWNiMDcyYSIsImNsaWVudF9pZCI6IkFDTUUifQ.Azt-qxLbhnJcKh2-pmkuqDZj_yrnkxSbqmtyj9IVXglM1Sd1WOFWGFp-ahApgXs3gPIwzfcf_EXWl-ESEvIfFhLHz_Fa56NHI4JFPFVkIGH1vFGnzcK17IBK3dr0WFpU7xKr9q3NQnhquNC5zX7ft6N8_sWDoVYKXC5lgWGw03Bds9CMLc2DStNnogyYs9U2DMIiJreza-YVA2J21Ej3qvxC2QPiiWh4Ehe-x1gQ9A-mCwhCJQxicUpq_UQZQSqeAnoMGKokeBqSVoGQT9_UqhQOsb5zaDInkqfRjttXfkKt88nbMSmetlX6VR3LESUtPijhyVnuorHyOO2-oVAM5A",
    "token_type": "bearer",
    "refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI1NmUwODNjZS0wN2E3LTQ5MGUtOGY1YS0yNjFjZThiYzZiY2IiLCJzY29wZSI6WyJsb2dpbiIsIndyaXRlX2Jhc2tldCJdLCJhdGkiOiJlYzZmNTJiZi00Zjc3LTQwZjUtOTQyZC04MWVhZTNjNjk0MzYiLCJiYXNrZXRfaWQiOiI4ZjFiNjlhNS00MTU5LTQwZjYtYTIxYS1jOGU4YjI4MTk1NzEiLCJleHAiOjE1MTAzMTQwMzMsImlhdCI6MTUwNzcyMjAzMywiYXV0aG9yaXRpZXMiOlsiUk9MRV9UUlVTVEVEX0NMSUVOVCIsIlJPTEVfQ0xJRU5UIl0sImp0aSI6IjQzMTIyZWNlLTdkZDktNDJjMC1iZmZhLWRlZGRjMWNiMDcyYSIsImNsaWVudF9pZCI6IkFDTUUifQ.FHelImSV1kaYxX-Bhu-2RXRzh9AwG1Pi8q8BXUC_v8PNw02kjtHsA-HEEPTh8sSd7-DVlWjbJDf3lKhRojGVhcvJiIX3JlKUblSXe_ihPEErJYtlvGueX7syAv5kF_E8gMvhsx6r0pF2hM0AtNH0ExaaBR-lZiFewd87ZWowbpifnqI9667BcNz2ZQMdcLPKgH37qnTcV6lyyIiyOfwiP24RzYLct-ETSN8kggUfqIoWtWDeIIHp2oT-w2U47OPbOM4rLVxWkbVczTfyI0Xrf3AC7dqhZ79-3jWoGLyjVvoeraAUjn6gF41LWzcmSRGmiX6BQbLe8OaBCBPIUVVZGQ",
    "scope": "login",
    "id_token": "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiI1NmUwODNjZS0wN2E3LTQ5MGUtOGY1YS0yNjFjZThiYzZiY2IiLCJpYXQiOjE1MDc3MjIwMzMsImV4cCI6MTUwNzcyMjkzMywiaXNzIjoiaHR0cHM6Ly9hcGkuZGlwcC5kZSIsImF1ZCI6ImNkOWUyMTNmLTE1NDEtNDFiYS05OWY1LWY2YTNhNTcxNTBlMSIsIm5vbmNlIjoibi0wUzZfV3pBMk1qIiwiYWNyIjoibG9hLmRpcHAuZGVmYXVsdCIsImV1dWlkIjoiNTZlMDgzY2UtMDdhNy00OTBlLThmNWEtMjYxY2U4YmM2YmNiIn0.fCaS_N_DB5Rh2k_lSf2qVUKhApTcJADiyPtNxCP0hyRgaqiskCvFN7ePTi13CoamKH7mER9TkejcUNN9NIdJTL1Wab-f0KH-EukWZat6W3S-CtP6UjcZ3bG4cvASJ60XkR6k6R9irOpLSSpAyfc5_bEDs_8jsa2Adgv1UbvLQEsXznI9rFnvEHC231_RFjWhiAzg3oTQFAFefGPPQC43nHJJhw2LP0u5AR_2tgDrWEcwIgkovWTjpzH8wcmMS2hAt5sjxl0X_0eJvvJvQj7HCn5xjMvXioOXV4dTfTtvuzilWHTOc60vnti1I3gzzuziRF_8mWLYX1IcUhaPfDVLUA"
}

Verimi QES (preliminary)

General

The QES is easy to use, just follow these steps to make it work. First of all you need a Customer that is eligible to sign a document. That can be established in two ways, the first one is quite simple, the customer adds his data on his own to the verimi Platform, the second is as simple as the first one, you request identification data from us and we will do the identification process with the customer. Now you send the customer to through the sign flow, as follows.

Start QES process

When starting the QES you need the following things:

  • Verimi credentials (clientID and client Secret)
  • Verimi Certificate
  • A verified customer (preferably ID card)
  • A document to sign

You need to create a JSON to submit the document that you want to get signed by your customer. The Session is determined by the externalReference, here you can use any ID that serves your needs. The Data in there should look as following. We have some restrictions on that. The redirects should always be https to ensure a safe connection. To establish the SSL you need to use the certificate provided by Verimi. As you see in the documents section it is an array, which allows you to add multiple clusters that get the names of the clusterTitle Attribute and are ordered accordingly to the clusterOrder Field. Each cluster can contain one to many documents. These get defined with the files Attribute. Within the files you can set some values, the most important is the documentData which is the document shown later on in the dialogue, when subscribing. It needs to be a String Base64 encoded. The Attribute showDocument is relevant in case the customer needs to open the Document or not, in case of "MANDATORY" the customer needs to open the document before he can sign, "OPTIONAL" allows him to skip the opening and sign right away. The "signatures" field defines size and position of the signature, if not provided it will always be the end of the document.

POST https://api.uat.verimi.cloud/dipp/api/v1/vas/sign

{
"onSuccessUrl" : "https://your/qes/success/url",
"onFailureUrl" : "https://your/qes/fail/url",
"externalReference" : "7b01456d-3ac2-443f-bd14-021333fdc397",
"fixedSignatory"  : false,
"documents" : [
   {
       " clusterTitle" : "Test documents to sign",
       " clusterOrder" : 1,
       " files" : [
           {
               "documentId" : "Document_2131",
               "order" : "1",
               "documentData" : "base64PDF",
               "filename" : "Finanzierungsvertrag",
               "title" : "Finanzierung",
               "description" : "Finanzierungsschein",
               "showDocument" : "OPTIONAL",
               "legalEntity" : "Finanzierer",
               "signatures" : [
                   {
                       "signatureFieldName" : "signatureFieldName",
                       "signaturePage" : 1,
                       "signaturePosX" : 100,
                       "signaturePosY" : 100,
                       "signatureWidth" : 200,
                       "signatureHeight" : 60
                   }
               ]
           }
       ]
   }
]
}
                    

On success you will receive a link, this link is valid for two weeks and leads to the signature process on Verimi side.

{
    "redirectUrl": "https://web.uat.verimi.cloud/forward.html/inline-qes?processId=a7ca5a95-503f-41e0-ae6c-c45fdbe82bbd"
}

On success you receive a code as parameter appended to your success URL, that you defined in the signature request. With this code you can retrieve the documents from Verimi. The code matches the processId that was appended to the redirect that you got. Here is an example response:

https://web.uat.verimi.cloud/success.html?code=a7ca5a95-503f-41e0-ae6c-c45fdbe82bbd

Retrieve signed documents

After you received a call to your success URL with the code/processId, you can retrieve the documents via a simple GET.

GET https://api.uat.verimi.cloud/dipp/api/v1/vas/sign

This request should go to the same endpoint as the initial sign request, with the same security measures applied, these are a SSL connection, secured with the Certificate that we created for you and with Base64 encoded authorization header containing your clientId and clientSecret. The GET should contain the following Parameter:

code=a7ca5a95-503f-41e0-ae6c-c45fdbe82bbd

An example request should look like this:

GET https://api.uat.verimi.cloud/dipp/api/v1/vas/sign?code=a7ca5a95-503f-41e0-ae6c-c45fdbe82bbd

As response you will receive a JSON containing your signed documents in Base64 encoded String

{
    "externalReference": "7b01456d-3ac2-443f-bd14-021333fdc397",
    "creationTime": "2020-06-26T11:34:53.258Z",
    "status": "SUCCESS",
    "signedDocuments": {
        "pdfs": [
            {
                "documentId": "Document_2131",
                "documentData": "JVBERi0xLjQKMSAwIG9iago8PAovVGl0bGUgKP7/KQovQ3JlYXRvciAo{...}"
            }
        ]
    }
}

Fetch the documentData and convert the Base64 encoded String into a PDF file again.

Verimi Pay

Create a Payment

Verimi Pay can be used to charge the Verimi user according to the shopping cart he has at the service partner in a secure way. For the service provider there is additional security in charging the customer via Verimi, as the bank account is verified and the Verimi user is forced to use 2FA to confirm the payment.

When requesting a payment you need to call the following endpoint:

POST https://api.uat.verimi.cloud/dipp/api/payments
"Content-type ": "application/json"
"Authorisation header": "basic auth"

Example body to charge the customer an amount of 123€:

{
  "amount": {
    "value": 123,
    "currency": "EUR"
  },
  "redirectUrlOnAuthorize": "www.example.com/after-authorize?paymentId=1234567",
  "redirectUrlOnCancel": "www.example.com/after-cancel?paymentId=1234567",
  "cart": [
    {
      "name": "Washing the car",
      "price": "12.30",
      "currency": "EUR",
      "quantity": "10",
      "description": "The washing car service",
      "digitalGood": "false"
    }
  ],
  "merchantPaymentId":"OrderId 12345",
  "paymentMethod": "ONEOFF_PAYMENT"
}                   

The amount consists of value, which is the total amount of the transaction and the currency "EUR" which is the only one supported by now. The "paymentMethod" actually supports two payment types: ONEOFF_PAYMENT (direct debit), ONEOFF_PAYMENT_GUARANTEED (secured direct debit, please contact Verimi Sales for further information).

The service provider has now the opportunity to add information via API:

  • The structure is as follows : verimi*[merchant input]*[payment ID ].
  • the allowed length in characters is 70.
  • No special characters are allowed.
  • if merchant does not provide any input only payment ID will be transmitted as remmittance info.

To provide the shopping cart is optional and can be omitted.

In case the payment got created you will receive a 201 response from the server. Your customer will see a success page by Verimi and will be redirected to the specified redirect URL.

Refund a Payment

In case your customer returns the full cart or just parts of it, you can use the refund endpoint which gives you the control to refund either partial or full amount of the upfront requested payment. You need to provide the paymentId to identify the transaction.

The refund request:

POST https://api.uat.verimi.cloud/dipp/api/payments/{paymentId}/refund

And has the following body:

{
  "amount": {
    "value": 123,
    "currency": "EUR"
  },
  "reason": "I need my money back"
}                   

You can only refund as much as you initially requested from the customer, in case you try more it will run into our fraud protection system.

In case the refund got accepted you will receive a 200 response from the server.

Payment updates endpoint

You can request updates for your issued payments, if you call this endpoint. It will return the current status.

GET https://api.uat.verimi.cloud/dipp/api/service-provider/payments/{paymentId}

The payment ID will be the identifier for your issued payment. The response could look like this:

{
  "paymentId": "s0819022-a4ee-4506-a6d2-2471ec9e1921",
  "status": "PENDING",
  "paymentMethod": "ONEOFF_PAYMENT",
  "paymentType": "Direct Debit",
  "creationDate": "2019-02-19",
  "totalAmount": {
    "value": 123,
    "currency": "EUR"
  },
  "shippingAddress": {
    "street": "string",
    "houseNumber": "string",
    "postCode": "string",
    "city": "string",
    "country": "string"
  },
  "merchantId": "testshop",
  "transactions": "[\"28959108-a5da-48c0-a97b-5ddf69a03792\"]"
}                   

Access to Service Provider context API

There are some APIs that do not require Identity Owner's context, and Service Provider should be able to call them directly without Identity Owner's token. For those cases we have a simple approach: Authorization Client Credentials.
In this approach a Service Provider can acquire an Access Token by making a request with the respective Client Identifier and Client Secret parameters. These two parameters are provided to the Service Providers after registration with Verimi.

Business Use Cases overview

There are several use cases for the integrated Service Providers. Here we list some of most popular ones.

Registration from Verimi Web Application

The Identity Owner can select a registered Service Provider from the list of Service Providers on the Verimi Web Application and can start the registration process. For that we use the registration URI introduced during the registration of the Service Provider to our platform. The Service Provider's system needs to go through the OAuth flow (the same flow as described in Login with Verimi).
During the OAuth flow, an sub is generated for the user, which is to be used by Service Provider as a unique identifier for that user. The usage of the sub to map an Identity Owner between Verimi and the Service Provider account is strongly recommended. The responsibility of performing this mapping stays by the Service Provider.

Description of steps:

  • 1. Identity owner chooses Service Provider registration link on Verimi portal
  • 2. Verimi portal redirects IO to SP url
  • 3. Web browser of IO request registation page from SP
  • 4. SP receives request from browser and prepares for registration. Here SP needs to start process of getting information about IO, this process requires getting AccessToken of IO and then basket data (please check login flow)
  • 5. - 12. SP starts Verimi authorization & authorization process, as outcome it gets authorization code, then exchanged to Access Token and finally Basket of IO data
  • 13. - 17. SP internal registration process, step 16. is optional information about linkage done by SP

The usage of the Basket with Identity Owner's data is optional during the registration. The process of creating and sharing the data, using the Identity Owner's data-basket, is described in section Transferring Data to Service Provider.

Used scopes
login (required)

Service Provider's account creation can be started also from Service Provider's Application. The flow is almost the same as described in section registration of account — triggered from Verimi. They differ only on the first step, which is not needed.

Please see the below flow:

Used scopes
login (required)

An Identity Owner can select a registered Service Provider from a list of Service Providers inside the Verimi Web Application, and can start an account creation process by clicking in the respective link. For that we are using the Account Link URI introduced during the onboarding of the Service Provider into Verimi platform. Service Provider's system needs to go through the OAuth flow (same flow as described in Login with Verimi). An sub is generated during the OAuth flow and it will be available for linking accounts.
The usage of the sub to map an Identity Owner between Verimi and the Service Provider account is strongly recommended. The responsibility of performing this mapping stays by the Service Provider.

The "account linkage" flow starts with the redirection of the Identity Owner from Verimi Web Application to the predefined Account Link URI.
The Service Provider then needs to trigger the login Authorization Code Grant flow.
Once the flow has been successfully finished, the account linkage process can progress with the sub available in the Access Token.

Optionally, during this flow the Basket can be used to import data from the Service Provider. The Basket ID is also available in the Access Token. The usage of the Basket to import data is described in section Transferring Data from Service Provider. If a data import is happening, then the browser will be redirected to Verimi "Data Import"-page.

Used scopes
login required
write_basket optional

An account linkage can also be started from the Service Provider Application. The flow in this case is almost exactly the same as with a linkage started from the Verimi page, they only differ by the first step.
Please see flow below:

For the use case login and write_basket scopes are required.

Used scopes
login required
write_basket optional

API Messages

You can view Verimi API in Swagger format here.

Verimi API is based on REST, following the HATEOAS principles, including HAL (Hypertext Application Language) specification, and uses JSON as the data format for payloads.
REST APIs define resources that correspond to business objects (such as personal data, transaction, bank account data, etc.), which are referenced by URLs.

Messages Structure

A JSON object MUST be at the root of every JSON API request and response, which contains data. This object defines a document's "top level".
A document MUST contain at least one of the following top-level members:

data;   // The document's "primary data"
errors; // An array of error objects
meta;   // A meta object that contains non-standard meta-information.

The members data and errors MUST NOT coexist in the same document.

Error handling

Verimi API provides structured way of error handling, following the JSON API guidelines.
For that, we assume that a server MAY choose to stop processing, as soon as a problem is encountered, or it MAY continue processing and encounter multiple problems. For instance, a server might process multiple attributes and then return multiple validation problems in a single response.

When a server encounters multiple problems for a single request, the most generally applicable HTTP error code SHOULD be used in the response. For instance, "400 Bad Request" might be appropriate for multiple 4xx errors or "500 Internal Server Error" might be appropriate for multiple 5xx errors.

Error Objects

Error objects provide additional information about problems encountered while performing an operation. Error objects MUST be returned as an array, keyed by errors in the top level of a JSON API document. (see our swagger API documentation)

An error object MUST have the following members:

id;        // A unique identifier for this particular occurrence of the problem
timestamp; // Timestamp of the error recorded by the service (GMT)
code;      // An application-specific error code, expressed as a string value
detail;    // A human-readable explanation specific to this occurrence of the problem. Like title, this field's value can be localized.
title;     // A short, human-readable summary of the problem that SHOULD NOT change from occurrence to occurrence of the problem, except for purposes of localization.

An error object MAY have the following members:

status;    // The HTTP status code applicable to this problem, expressed as a string value.
links;     // A links object containing the following members:
about;     // A link that leads to further details about this particular occurrence of the problem.
source;    // An object containing references to the source of the error, optionally including any of the following members:
pointer;   // A JSON Pointer [RFC6901] to the associated entity in the request document [e.g. "/data for a primary data object, or "/data/attributes/title" for a specific attribute].
parameter; // A string indicating which URI query parameter caused the error
meta;      // A meta object containing non-standard meta-information about the error.

Error Codes and Messages

The error codes and messages follow the hierarchy of the services. The error codes follow the naming convention of Java packages. Every error code, related to a single service, have the prefix of the top level package name of the service it belongs to. This is followed by the description of the error (i.e. com.Verimi.user.command.error.password). Every error code, related to multiple services, have the prefix of global. The internationalized error codes and message are stored within the error-translation-query-service.

Security of communication channels

Verimi introduces several layers of security into its workflows. This approach starts at the transport layer. As explained before, all services APIs use HTTP REST standard. To secure the data transport, Verimi applies the TLS protocol.

TLS was developed by the Working Group Transport Layer Security WG in the IETF (Security Area). The goal of TLS is to ensure data security on the layers above the TCP/IP. The protocol allows data encryption, authentication of servers, and message integrity for TCP/IP communication. Verimi relies on TLS; with the latest version, which is 1.2, and the usage of X.509v3 server certificates, to communicate with external entities. The versions prior to Version 1.0 is no longer allowed, nor supported. Verimi can only be accessed via encrypted channels (HTTPS).

The mutual certificate-based authentication principle ("Mutual Authentication" or "2-way TLS") is used, in all possible situations, for identifying and authenticating the client systems. This applies to all capable Service Providers for server-to-server communications, e.g. Service Provider to Verimi. All Service Providers and Identity Providers working with Verimi must follow this rule. For non-sensitive data One-way TLS can be applied.

With 2-way TLS, the server prompts the client for an X.509 certificate and checks if a trustworthy certification authority has issued it. Furthermore, the server has stored a list of subjects ("Distinguished Name"), which are allowed to access the respective service (white list), and checks whether the subject of the client certificate is contained inside that list.

The 2-Way TLS is not suitable for encrypting communication's channel with the Identity Owner's browser; otherwise an individual certificate would have to be issued for each IO. Therefore, the simpler one-way TLS approach is used for this situation, which corresponds to the market standard and — assuming a fault-free implementation — can be considered also as a secure protocol.

Besides the usage of TLS in its latest version, Verimi takes further security measures, like limiting the set of cipher suites that are used: ECDHE-RSA-AES256-CBC-SHA384. All of these ciphers are recommended by the German Federal Office for Information Security (BSI), and can be used at least up to 2021.

  • ECDHE provides Perfect Forward Secrecy and requires fewer resources than DHE.
  • AES256 is a strong encryption specification
  • RSA is the basic procedure for the private/public key material.
  • SHA384 is a fast to encrypt and slow enough to decrypt hash function for signatures and provides protection against length-extension attacks (as opposed to SHA256).
  • CBC is a cipher block mode for AES, which establishes an authenticated encryption.

The described cipher suites have the following basic properties for the communication between Verimi and external partners:

  • The TLS connection is confidential: The TLS handshake establishes a common secret key without exchanging it, which is then used as a symmetric key in the extended TLS session, using asymmetric encryption.
  • The integrity of TLS payload is assured: message transport includes message integrity verification using so-called "Message Authentication Codes" (MACs). Secure hash functions are used for the MAC calculations.
  • 2-way TLS allows validation of the authenticity of any client (Service Provider or Identity Provider) by forcing all communication parties (Verimi and client) to authenticate themselves via their own certificates.
  • TLS includes protection mechanisms against man-in-the-middle attacks on TLS link between two connected systems. For this purpose, it uses internal counters and "shared secrets" and also secures the handshake with signed summaries of previously exchanged data against such an attack.