OAuth2 and OpenID Connect API

GII is using OAuth2 and OpenID Connect. It is easy to integrate with GII since GII is using standards. Client libraries are available for most plattforms that will handle the integration with a minimum of configuration.

Examples

API Overview

The OpenID Connect API is documentet using OpenID Connect Discovery at the standard webfinger URL /.well-known/openid-configuration. The URL for test is (https://demo-api.gii.cloud/.well-known/openid-configuration) and the URL for prod is (https://api.gii.cloud/.well-known/openid-configuration).

The interesting endpoints and properties, documented at the webfinger URL, are:

  • authorization_endpoint: URL called by an end user using a browser. This URL will be accessed with query parameters. See Authentication Request for standard parameters. scope should normally be openid. Extra parameters that are supported are ui_id (the ID of the relying party UI - see documentation for api/rp/ui endpoint) and identity_provider (valid values are e.g. bankid-se and bankid-no) if only one identity provider should be supported.
  • token_endpoint: URL called by relying party to complete a login. See Token Request.
  • revocation_endpoint: URL called by relying party to logout an end user. See OAuth 2.0 Token Revocation.
  • jwks_uri: URL to get valid public keys used by GII to sign JWT. The OpenID Connect client library should automatically call this endpoint to verify tokens.
  • userinfo_endpoint: URL to get user information. See UserInfo Endpoint.
  • scopes_supported: List of scopes that are supported. See Requesting Claims using Scope Values.
  • response_types_supported: List of response types that are supported. code simply tells tells client libraries that GII only supports Authorization Code Flow (not implicit or hybrid).
  • grant_types_supported: Only Authorization Code Flow is supported.
  • display_values_supported: Both full page (page) and iframe (popup) is supported.
  • claims_parameter_supported: Relying party can send claims-parameters (see Requesting Claims using the “claims” Request Parameter). See below for supported claims.

API Control Flow

The control flow always contains four required parts and two optional parts.

  1. Enduser is browsing to website belonging to relying party (merchant).
  2. Relying party is redirecting enduser to authorization_endpoint. See Authorization endpoint.
  3. GII redirects enduser back to the redirect_uri of the relying party together with a code.
  4. Relying party calls the token_endpoint with the code retrieved in step (3) and gets an access_token and id_token. See Token endpoint.
  5. Optional: relying party validates id_token against public keys from jwks_uri.
  6. Optional: relying party calls userinfo_endpoint for possibly additional enduser information (KYC information, PEP, address) using access_token.
OpenID Connect API Flow
alt text
Step 1 - enduser at relying party website Step 2 - authorization endpoint Step 3 & 4 - token endpoint called
alt text alt text alt text

Claims

Claim Identity Provider BankID SE BankID NO NemID Freja eID Yoti Sum&Sub Veriff
Note
sub global unique ID x x x x x x x
iss issuer - api.gii.cloud x x x x x x x
aud your clientID x x x x x x x
nonce prevent replay attacks x x x x x x x
iat issued at -timestamp x x x x x x x
exp expires at - timestamp x x x x x x x
enduser_id same as sent to auth x x x x x x x
rat requested at - timestamp x x x x x x x
cloud.gii:identity_provider identity provider used. x x x x x x x
ssn unique ID of user for IP x x x x
ssn_type type of SSN. x x x x
email email if any x x x
email_verified true if email is verified x x
picture url of profile picture x
name name of user x x x x x
preferred_username preferred username
given_name given name x o x x x
family_name family name x o x x x x
birthdate birthdate in ISO-format x x x x x x
gender male or female x x x x x
phone_number phone number x
address::formatted full address o o o
address::street_address street address o o o x
address::locality locality o x
address::region region x
address::postal_code postal code o o o x
address::country country o o o x x
screening::is_pep true if PEP
screening::pep_checked_at date PEP was checked
screening::pep_risk risk (1-5) of being pep
screening::pep_details details (in xml)
screening::is_sanction true if under sanction
screening::sanction_checked_at date sanction was checked
screening::sanction_risk risk (1-5)
screening::sanction_details details (in xml)
self_exclusion if self excluded o o
self_exclusion_verified when excl. was verified o o
self_exclusion_checked exclusion was checked o o

Cells marked with o are for onboarding service.

  • ssn: Different national identification numbers or social security numbers. See ssn_type for the type of this value.
  • ssn_type: Possible values are “personnummer” (Swedish national identification number) or “CPR” (Danish national identification number)
  • cloud.gii:identity_provider: The identity provider for the login. Possible values are: bankid-se, bankid-no, nemid, yoti, freja, veriff, sumsub, linkedin, google, facebook, microsoft, twitter.

Self Exclusion

GII can check against self exclusion services when the user is logging in. Currently only spelpaus (Swedish self exclusion system) is implemented.

Supply the scope=openid,self_exclusion (se below) for check against spelpaus.

Authorization endpoint

The first step in the flow. See Authorization Endpoint for general specification. The request parameters that are supported are:

  • client_id: required. Your ID in GIIs system
  • redirect_uri: required. Your registered callback URI
  • response_type: required. Only valid value is code
  • scope: required. Only valid values are openid and self_exclusion. If scope=openid,self_exclusion then GII will return the claims self_exclusion, self_exclusion_verified, and self_exclusion_checked.
  • state: required. Passed to the redirect_uri when a login is complete. Can be used for user migration/matching.
  • nonce: optional. Placed in the ID token when a login is complete. Can be used for user migration/matching.
  • identity_provider: optional. If the user should be redirected to an identity provider without seeing the list of possible IPs first. Valid values are: bankid-se, bankid-no, nemid, yoti, freja, veriff, linkedin, google, facebook, microsoft, twitter.
  • display: optional. Either page (default) or popup. popup will adapt the login UI for iframe/popup.
  • ui_id: optional. ID of a created UI configuration
  • ui_friendly: otional. A friendly name of a created UI configuration
  • claims: optional. See Requesting Claims using the “claims” Request Parameter for specification. The supported claims are birthdate, email, email_verified, gender, phone_number and phone_number_verified. The claim ssn can be used with a specified value in order to prepopulate SSN (e.g. for Swedish BankID).
  • bankid-se-device: optional. Either same, other, or unknown. Swedish BankID will work for BankID on same device if same, go directly to personal number input if other, or display a list where you get to pick alternative if unknown.
  • bankid-se-qr: optional. Either true or false. No QR-code for Swedish BankID will be displayed if false.
  • bankid-se-redirect-url: optional. As URL string. If using iframe and want the BankID-app to redirect back to the browser: supply the outer frames location.href. Only send the parameter if user is using Safari on iOS or Firefox on iOS.. For Firefox on iOS the URL should be firefox://{path without protocol to outer frame}. Chrome on iOS do not allow redirecting back to the same tab so this functionality wont work for Chrome-users on iOS. Chrome on iOS can be detected by checking navigator.userAgent.indexOf('CriOS') !== -1.
  • bankid-se-redirect: optional. Either true or false. If not using iframe and want the BankID-app to redirect back to the browser: send true. Only send true if user is using Safari on iOS or Firefox on iOS.. Firefox on iOS can be detected by checking navigator.userAgent.indexOf('FxiOS') !== -1.
  • bankid-no-variant: optional. Either bim (BankID over mobile), xid (xID) or vipps.
  • sumsub-variant: optional. Only valid value is telegram which causes the Telegram Passport integration for Sum&Substance to be used.
  • ui_locales: optional. Space separated list of languages/cultures. E.g. ui_locales=sv-SE en
  • verification_callback_uri: optional. URI that GII will call when a verification (by e.g. Veriff) is finished. Otherwise redirect_uri is used.
  • enduser_id: optional. Unique ID on your user that can be used by GII to e.g. keep state of a verification. Do not use values that can be guessed here! Recommendation is to use a combination of a describing value (e.g. an email address) and a checksum/HMAC based on public and private values in combination with a (per environment) secret key. The enduser_id will be returned in the token so it can be verified.
  • platform: optional. If the has the value native (as in native app): BankID will trigger using location.href even if display=popup. display=native tells the UI to behave as if inside a UIWebView or similar.

All other request parameters will be silently ignored.

Example request to authorization_endpoint:

GET /api/oauth/auth
    ?client_id=my-client
    &redirect_uri=https%3A%2F%2Fdemo-app.gii.cloud%2Fcallback
    &response_type=code
    &scope=openid
    &state=6129484611666145821
    &nonce=sdwekjfeiik83kd9dk
    &identity_provider=nemid
    &display=popup
    &ui_id=89
    &claims=%7B%22id_token%22%3A%20%7B%22phone_number%22%3A%20%7B%22essential%22%3A%20true%7D%7D%7D
Host: https://demo-api.gii.cloud

Example response to redirect_uri:

GET /callback?
    code=74un8cTaWsEQSaaWgDdeBRh-SofFY0VYRqjQfkoyefo.heq23S0kqXbMIbsfbkPofOoV5hsignywcCLiuGW3Aeg
    &scope=openid
    &state=6129484611666145821
    &display=popup
Host: demo-app.gii.cloud

The redirect_uri is an URL to your system (e.g. https://example.com/login/completed).

GII is also providing an extra query parameter display with the possible values popup, page, and headless. This value needs to be handled if you started the login from an iframe or if you use async KYC-services (only Veriff.me at the moment). The URL verification_callback_uri will be called instead of redirect_uri for display=headless if verification_callback_uri is provided.

  • display=popup tells your system that the login completed in an iframe.
  • display=page tells your system that the login completed in the top frame, not the iframe it started in. This can happen for identity providers that do not support being iframed.
  • display=headless is returned when the login completed with a double callback. The first response to your redirect_uri will be a normal response where the user is redirected to the redirect_uri while the second response to your redirect_uri will be a server request from GII with display=headless. This happens when using Veriff.me since the verification that Veriff.me is doing is happening after the user has finished the process. One could see display=headless as a webhook.

Token endpoint

The second step in the flow. See Token Endpoint for general specification. Take the code from the redirect_uri and call (server to server) the token_endpoint (from the webfinger) using basic auth.

Example request for the code above if the client my-client got the password my-password.

POST /api/oauth/token HTTP/1.1
  Host: https://demo-api.gii.cloud
  Content-Type: application/x-www-form-urlencoded
  Authorization: Basic bXktY2xpZW50Om15LXBhc3N3b3Jk

  grant_type=authorization_code
    &code=74un8cTaWsEQSaaWgDdeBRh-SofFY0VYRqjQfkoyefo.heq23S0kqXbMIbsfbkPofOoV5hsignywcCLiuGW3Aeg
    &redirect_uri=https%3A%2F%2Fdemo-app.gii.cloud%2Fcallback

The token endpoint will on success answer with an access_token and id_token.

HTTP/1.1 200 OK
  Content-Type: application/json
  Cache-Control: no-store
  Pragma: no-cache

  {
   "access_token": "4sjHob0fgOxMpKa1LkJ6D-Y5PMiS4yugAw9rO5oio34.mm40QY1ULclxvN1D_fzjd34bzIUjY16mcY0jSko7i4o",
   "token_type": "Bearer",
   "expires_in": 3600,
   "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJteS1jbGllbnQiLCJiaXJ0aGRhdGUiOiIxOTYzLTA1LTAxIiwiY2xvdWQuZ2lpOmlkZW50aXR5X3Byb3ZpZGVyIjoibmVtaWQiLCJlbWFpbCI6IiIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiZXhwIjoxNTI0NDk3MzQyLCJmYW1pbHlfbmFtZSI6IiIsImdlbmRlciI6Im1hbGUiLCJnaXZlbl9uYW1lIjoiIiwiaWF0IjoxNTI0NDc1NzQyLCJpc3MiOiJodHRwczovL2RlbW8tYXBpLmdpaS5jbG91ZCIsIm5hbWUiOiJUaW1vdGV1cyBMYXVyc2VuIiwibm9uY2UiOiJzZHdla2pmZWlpazgza2Q5ZGsiLCJwaG9uZV9udW1iZXIiOiIiLCJwaWN0dXJlIjoiIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiIiwicmF0IjoxNTI0NDc1NzQyLCJzc24iOiIwMTA1NjMwMTExIiwic3NuX3R5cGUiOiJDUFIiLCJzdWIiOiJSRXREVUZJd01UQTFOak13TVRFeFhWaG1QMmdvTzJkWGVIcGlJMFpHT24xdFRWMDVNVElwUytPd3hFS1kvQndVbXZ2MHlKbHZ1U1FucmtIa1pKdVRUS1NWbVJ0NFVyaFYifQ.APNkOjCQFyQqb01HtyqJP0zbRbghMEFx4vCQwcmVA5HCjpNlGqFlIUUVygFfiBCmzF0w3nADcT-yuzPK7N1LAp3sE3cpLlbbCONu8mxlCj6KxUPoXeuQjlDJ_ykeV_6nTfZ68HybszxLOqyNhTVTayYMX-E_zzWUQza0OZ67IjgK-sGpr55C7DM84ES-Ms5XfLenjI0kGXZeGC9ZeHUu1Pvsz-exPo-ozefnw0C1E5JOoUcJTp6CSfeOe4eFoVuK5moo55nKj0v7o4NtYmXu1MVq7lB0gGJvwQ-7EH1TnExkPTCeQ6c7xatjfrzyzNkAVU8U-pkfGF1tfMbzsAc2KA"
  }

The id_token is a JWT. The JWT consists of a JOSE Header, followed by a base 64 encoded JWT Claim followed by an encoded JWS, all separated by dots. It is possible to easily inspect a JWT using jwt.io.

Decoding the id_token above by splitting the token on the .s and base64 decoding the first two segments yields the JOSE Header

{
  "alg": "RS256",
  "typ": "JWT"
}

and the JWT Claims

{
  "aud": "my-client",
  "birthdate": "1963-05-01",
  "cloud.gii:identity_provider": "nemid",
  "email": "",
  "email_verified": false,
  "exp": 1524497342,
  "family_name": "",
  "gender": "male",
  "given_name": "",
  "iat": 1524475742,
  "iss": "https://demo-api.gii.cloud",
  "name": "Timoteus Laursen",
  "nonce": "sdwekjfeiik83kd9dk",
  "phone_number": "",
  "picture": "",
  "preferred_username": "",
  "rat": 1524475742,
  "ssn": "0105630111",
  "ssn_type": "CPR",
  "sub": "REtDUFIwMTA1NjMwMTExXVhmP2goO2dXeHpiI0ZGOn1tTV05MTIpS+OwxEKY/BwUmvv0yJlvuSQnrkHkZJuTTKSVmRt4UrhV"
}

See Claims for a complete matrix over all claims from GII.

UserInfo endpoint

For additional onboarding data it is possible to call the userinfo endpoint. See UserInfo Endpoint for general specification.

Cost: The cost of calling this API depends on which data source(s) are used. Usually the cost is per request.

Take the access_token from the token endpoint-response and call the userinfo_endpoint (from the webfinger).

Example request:

GET /api/oauth/userinfo
Host: demo-api.gii.cloud
Authorization: Bearer 4sjHob0fgOxMpKa1LkJ6D-Y5PMiS4yugAw9rO5oio34.mm40QY1ULclxvN1D_fzjd34bzIUjY16mcY0jSko7i4o

where the string after Bearer is the access_token for the user.

The endpoint will on success answer with a UserInfo-response containing claims (see Claims) for onboarding, such as address information.

Example response:

HTTP/1.1 200 OK
  Content-Type: application/json
  Cache-Control: no-store
  Pragma: no-cache

  {
   "address": {
     "country": "SE",
     "formatted": "Sveavägen 49, 113 59 Stockholm",
     "locality": "Stockholm",
     "postal_code": "113 59",
     "street_address": "Sveavägen 49"
   },
   "sub": "REtDUFIwMTA1NjMwMTExXVhmP2goO2dXeHpiI0ZGOn1tTV05MTIpS+OwxEKY/BwUmvv0yJlvuSQnrkHkZJuTTKSVmRt4UrhV"
  }

Request parameters

  • scopes: optional. List of requested scopes (profile (default), pep, sanction). profile is available for customers with address lookup while pep/sanction is available for customers with e.g. Trapets.

UserInfo endpoint for not logged in customers

GII is also exposing a userinfo endpoint for customers that are not logged in through GII.

Cost: The cost of calling this API depends on which data source(s) are used. Usually the cost is per request.

Call the endpoint with basic auth (your client_id:cliend_password) and supply ssn_type and ssn. Valid values for ssn_type are personnummer (Sweden) and CPR (Denmark).

Example request:

GET /api/data-sources/userinfo?ssn_type=personnummer&ssn=198101011337
Host: demo-api.gii.cloud
Authorization: Basic bXktY2xpZW50Om15LXBhc3N3b3Jk

Example response:

HTTP/1.1 200 OK
  Content-Type: application/json
  Cache-Control: no-store
  Pragma: no-cache

  {
   "address": {
     "country": "SE",
     "formatted": "Sveavägen 49, 113 59 Stockholm",
     "locality": "Stockholm",
     "postal_code": "113 59",
     "street_address": "Sveavägen 49"
   },
   "sub": "REtDUFIwMTA1NjMwMTExXVhmP2goO2dXeHpiI0ZGOn1tTV05MTIpS+OwxEKY/BwUmvv0yJlvuSQnrkHkZJuTTKSVmRt4UrhV"
  }

Login and Signup

It is possible to use GII for both login and signup. Matching existing customers can be done in two ways:

  • Match user accounts using a claim, such as e-mail or national identification number.
  • Match user accounts using a nonce or state presented to an existing customer, e.g. through an e-mail sent to the customer. It is possible for a relying party to match a login from GII with an existing user if the relying party sends a state och nonce to the authorization endpoint that can be backtracked to an existing user account.

Environments

There is a mock environment, test environment and a production environment.

Mock environment

Host: mock.gii.cloud

OpenID Connect Discovery endpoint: https://mock.gii.cloud/.well-known/openid-configuration

The environment is not integrated with real identity providers but can be used for automated tests and as a way to test the integration without going through the identification steps all the time.

Test environment

Host: demo-api.gii.cloud

OpenID Connect Discovery endpoint: https://demo-api.gii.cloud/.well-known/openid-configuration

This environment is using test certificates and must be accessed with test keys (not production keys). Depending on the identity provider test users must be used.

Production environment

Host: api.gii.cloud

OpenID Connect Discovery endpoint: https://api.gii.cloud/.well-known/openid-configuration

This environment is using production certificated and must e accessed with production keys (not test keys). Real users must be used.

Identity Providers

BankID Sweden

See instructions on how to configure a test client that can access BankIDs test servers.

Visit demo bank to issue a test certificate for the test.

Iframe

When integrating with GII using an iframe you need to handle the special case that GII might need to open applications with universal links (e.g. opening BankID). GII will send a message using window.postMessage to inform the parent frame to open the universal link. The parent frame needs this code:

window.addEventListener("message", (e: MessageEvent) => {
    if (e.data) {
        try {
            const data = JSON.parse(e.data);
            if (data.href && data.action === "gii::location_href") {
                // try to open the universal link
                location.href = data.href;
            }
        } catch (err) {
            // nothing
        }
    }
}, false);

Callback URL

Read the extra query parameter display with the values popup and page in the callback URL in order to find out if a login completed from within an iframe of the top frame.

Some identity providers do not support being iframed. GII will detect that the login is happening inside an iframe (by reading the display query parameter) and from the list view (over all identity providers) open the login method in the top frame (technically target=_top).

References