Documentation

1General Idea of Web Apps

With a web app an external system can request access to specific spaces via the web service API. The user can grant access to other systems in an automated way. The OAuth 2.0 flow specifies this concept in detail.

In general, the OAuth specification provides some room for adaptations to this concrete use case.

2OAuth 2.0 Flow

The integration generally follows the OAuth specification. The flow involves the following actors:

  • User: The user can grant the web app access to a Space. The user is usually the merchant that is granting access to your app.

  • Client: The client invokes the web service API to access the data within the Space. This is your app. Your app might run on a web server too.

  • Server: We use in this documentation the term server for our system. As in the client-server pattern our system represents the server.

To keep this documentation simple we reuse the terminology from the OAuth specification.

OAuth Flow
Figure 1. The OAuth 2.0 flow with the different actors.

The above figure shows the following steps:

  1. The user requests the installation of the web app.

  2. The web app (client) returns the authorization URL to which the user grants access to the web app. The authorization URL requires that a redirect_uri is present.

  3. When the user invokes the authorization URL, the server will in turn request the user to confirm the requested permission on the space. As a result the server will return the redirect_uri to the browser.

  4. The browser invokes the redirect_uri which points to a URL controlled by the web app. The redirect_uri contains a code parameter with which the web app can request the installation of the app.

The following sections cover the above steps in further detail.

2.1Create the web app configuration

As a first step you have to set up a web app and retrieve the client_id and client_secret.

2.2Trigger the installation

There are two ways as how the user can start the installation. In both cases the web app has to forward the user to the authorization URL. See Ask for permission on how to do that.

  • Option 1: The user triggers the installation from within our application. In this case we forward the user to the Installation Redirect URL as defined in the web app configuration. In this case the user will invoke the Installation Redirect URL as a GET HTTP request. We ensure that the request contains the following parameters:

    • space_id: The app is installed into the space resolved by the space ID.

    • action: The action is set to install. The action allows to understand what operation should be carried out.

    • timestamp: The timestamp in seconds since 1970. This allows to check for replay attacks. It is recommended to deny requests that are older than a few hours.

    • hmac: A HMAC is calculated based on the space_id, action and timestamp request parameters. This allows the verification of a request is really originating from us. See HMAC Calculation.

  • Option 2: The user is triggering the installation from within the web app. In this case the web app has to ask for the space ID or use another approach to retrieve it.

2.3Ask for permission

To ask the user for the permission the user has to be forwarded to: https://checkout.postfinance.ch/oauth/authorize

The request must contain the following request parameters:

  • space_id: The app is installed into the space resolved by the space ID.

  • redirect_uri: The URI to which the user is forwarded back after the user has authorized the access. The web app configuration must contain this URL as redirection endpoint, otherwise it will be rejected.

  • scope: The scope parameter contains a space separated list of permission IDs which are requested. The permission list provides the full list of all permissions that can be requested.

  • state: A randomly generated value provided by your app (client) that is unique for each authorization request. When the user returns to the redirect_uri we include this value again. It is important that you check this value as the security can be compromised. We recommended that you ensure that the state cannot be used for replay attacks (e.g. include a timestamp and reject requests that are too old).

  • client_id: The client ID that identifies your app. The client ID can be retrieved in the web app configuration.

If you request permissions in the parameter scope that require specific features which are not available to the space we either try to activate those features or return a reduced set of permission at the installation confirmation. So we remove the permissions that we cannot grant.

Example request:

https://checkout.postfinance.ch/oauth/v2/authorize?space_id=15023&client_id=14141&redirect_uri=https%3A%2F%2Fexample.com%2Fconfirm%2Finstall&state=1609445756&scope=1432736711150%201432736711152
Note
In case you want to alter the permissions to the web app at some point, you can execute the same steps as if you would install the app from scratch. You can forward the user to the https://checkout.postfinance.ch/oauth/v2/authorize with the adapted scope parameter. All the following steps remain the same.

2.4Confirm the app installation

When the user returns to the redirect_uri the web app has to confirm the installation and retrieve the access_token according to the OAuth specification.

Along the redirect_uri the following parameters will be sent:

  • state: The state parameter contains the value as passed to the authorization URL. Verify that this value is identical to the value you have passed.

  • space_id: The ID of the space the user has granted access to.

  • timestamp: The time when the user granted access. The time is in seconds since 1970 (Unix timestamp). Check this that the grant is not too old. A feasible time here is about 10 minutes. This prevents replay attacks.

  • code: The code identifies the grant created in the previous step. You need this code for confirming the installation. See below how to do that.

  • return_url: The URL to which the user can be redirected back after the installation has been completed. The optional parameter message can be appended to the URL to display a customized message. The URL parameter type defines whether a success message or a failure message is displayed.

  • hmac: The HMAC allows to verify that the request has not been tampered. See HMAC Calculation for how you can do that.

Example request:

https://example.com/confirm/install?state=1609445756&space_id=14141&timestamp=1609449756&code=AdF7812311414312312387483&hmac=8jAYtV4R7FFTjl3UqWpkmBy78PVQdDygJ1NbM7v_-1AcAMWMhv45PPJA-nYkNT4gCNZ2XECYF3-N5W29ZXGJ6Q

To confirm the installation you have to send an API call. You have to send the following POST message to the URL https://checkout.postfinance.ch/api/web-app/confirm. See also in the web service documentation. As this is a regular web service endpoint:

{
   "code": "AdF7812311414312312387483"
}

As this is a regular web service endpoint you have to provide the authentication credentials in the header of the HTTP request. For this purpose use the client_id as the user ID and for the client_secret the secret.

The response looks similar the following example:

{
    "access_token": "dummy-value",
    "token_type": "web-service-hmac",
    "state": "1609445756",
    "scope": "1432736711150 1432736711152",
    "space": {
      "id": 14141,
      "name": "Test",
      "postalAddress": {
         "city": "Winterthur",
         "country": "CH",
         "dependentLocality": null,
         "emailAddress": "[email protected]",
         "familyName": null,
         "givenName": null,
         "organizationName": "Muster AG",
         "postalState": null,
         "postcode": "8400",
         "postCode": "8400",
         "salesTaxNumber": null,
         "salutation": null,
         "sortingCode": null,
         "street": "General-Guisan-Strasse 47"
      },
      "primaryCurrency": "CHF",
      "state": "ACTIVE",
      "technicalContactAddresses": [],
      "timeZone": "Europe/Zurich",
      [...]
   }
}

In this step you should check whether the returned scope list correspond to what you have requested and if the returned set does fulfill your requirements. As indicated before not in all situations all requested permissions can be granted.

The next step covers how to actually invoke the web service API.

2.5Access the web service API

Once the app has been successfully installed in the space, the client_id and client_secret can be used to access the web service API for the given space. On the web service API you have to use the client_id as the user ID and the client_secret as the secret. The invocation of the API is the same as if you would create an application user and use their credentials to invoke the API.

2.6Configure the web app

The configuration redirect URL allows to define a page on which the web app can be configured by the user. That means the web app listing will contain a Configure button that redirects the user to the URL defined within the web app configuration.

The redirection URL contains the following parameters:

  • space_id: The space ID identifies the space for which the configuration should be adjusted.

  • timestamp: The timestamp in seconds since 1970. This allows to check for replay attacks. It is recommended to deny requests that are older than a few hours.

  • action: The action is set to configure. The action allows you to understand the action triggered by the user.

  • return_url: The URL to which the user can be returned after the configuration has been completed. The optional parameter message can be appended to the URL. This message will be displayed to the user. The parameter type defines whether a success message or a failure message is displayed.

  • hmac: A HMAC is calculated based on the space_id, action, return_url and timestamp request parameters. This allows the verification of a request is really originating from us. See HMAC Calculation.

Note
You should always check the hmac to avoid that an attacker can modify the configuration for an app. The hmac guarantees that the user is allowed to carry out the configuration operation.

3Notifications

To keep your web app in sync with the installation state within the different spaces we recommend to set up a notification URL. Any change to the installation of the web app within a space will be reported on that URL.

We send a HTTP POST message with the following body:

{
	"space_id": 15023,
	"client_id": "14141",
}

Whether the app has been installed or deinstalled is not part of the message. Use the web service API to find out what was the trigger that we send out this message.

The above behavior is intentionally. It solves the following issues:

  • It may occur that a notification is sent out with a delay because we were having difficulties reaching your system. In that case you still read the installation state from one single source that holds the correct state.

  • There are no security risks as no critical information within the HTTP request are included. The message is just a trigger that you fetch the correct information through the web service API.

  • In some cases the web app and our system might get out of sync due to temporary outages. With the above approach you can decide to retrieve the current installation state at any point.

Note
Be aware that an outage of your system might prevent us from sending notifications to your system. In that case we send an email if specified in your web app configuration.

4HMAC Calculation

To verify the requests are originating from our system we append an HMAC with SHA-512. It guarantees that the parameters are not modified during the transmission. This is similar to a normal hash but in a secure way. Most programming languages provide an implementation for the HMAC calculation.

Generally we have to construct a string first and secure it later with the client_secret. To do so we take all the parameters, order them alphabetically, and concatenate them with an | (pipe).

See the following PHP example with the parameters: space_id, timestamp, client_id and scope

<?php

$client_secret = "OWOMg2gnaSx1nukAM6SN2vxedfY1yLPONvcTKbhDv7I=";

$requestParameters = array();
$requestParameters['client_id'] = "14141";
$requestParameters['state'] = "87ggfr456zghjui876tgvbji";
$requestParameters['space_id'] = 15023;
$requestParameters['scope'] = "1432736711150 1432736711152";

// We sort the array elements by the element's key
ksort($requestParameters);


$parametersToSecure = array();

foreach ($requestParameters as $key => $value) {
	$parametersToSecure[] = $key . "=" . $value;
}

$toSecure = implode('|', $parametersToSecure);

$decodedSecret = base64_decode($client_secret);
$hmacValue = base64_encode(hash_hmac("sha512", $toSecure, $decodedSecret, true));

// Replacing "+" with "-" and "/" with "_"
$cleanedMac = $url = strtr($hmacValue, '+/', '-_');

// Remove padding
$cleanedMac = rtrim($cleanedMac, '=');

echo $cleanedMac;

Remarks:

  • We do not URL encode the parameters when we calculate the HMAC. When your infrastructure (e.g. server) does not decode the URL parameter automatically you might need to do that before calculating the HMAC.

  • The client_secret provided in the web app configuration page is encoded as Base64 string. We first need to convert it into a binary.

  • The parameters that need to be secured depend on the use case. We are using the HMAC in multiple situations so please adapt the parameters accordingly. We only include the parameters that are explicitly listed. We never include all parameters. So if the request contains further parameters, ignore them.

  • The HMAC included in the request is always Base64 encoded. When you compare the calculated HMAC with the passed HMAC, please compare them in the binary format or make sure you unify the different Base64 formats first. Due to the fact that we are passing the HMAC as an URL parameter we have to use a URL safe implementation without padding. See RFC 4648

  • The parameter values might originating from a JSON object. In that case the values have to be converted into strings before concatenating them. Floating point numbers are represented with a dot and with the same number of digits as within the JSON and also optional zeros at the end will be appended. Booleans have to be represented as true or false.

5Remote Invocation

In some cases your app endpoints will be invoked by a HTTP request that is sent via our servers to your servers. So the request happens without the user (browser). This section covers important information about these invocations.

5.1Intention of Remote Invocations

In some cases we need to inform you about certain activities that your app has to carry out. In those cases we are using remote invocations. Those invocations are sending to a URL defined by you a HTTP request with some data. The data that we send depend on the concrete use case. We cover here only the things that are been applied to all types of invocations.

So please use the corresponding documentations for the concrete use cases to understand what the actual request body will contain.

5.2Security

Each request contains a header x-mac-value. This header contains an HMAC that allows you to verify that the request is really originating from our systems. Futhermore the request contains also the header x-timestamp which indicates the time when the request was created. The timestamp contain the time in seconds since the first of January 1970. So it is a unix timestamp.

To verify the HMAC you have to concatenating the value of x-timestamp with a pipe (|) and the request body. The resulting string you have to pass to the HMAC with SHA-512 algorithm. For the secret you can use the secret generated for the app. So the same secret as you also use to invoke our web service API.

See below also an example in PHP:

<?php

$client_secret = "OWOMg2gnaSx1nukAM6SN2vxedfY1yLPONvcTKbhDv7I=";

$hmac = $_SERVER['HTTP_X_MAC_VALUE']; // PHP requires to transform the header name.
$timestamp = $_SERVER['HTTP_X_TIMESTAMP']; // PHP requires to transform the header name.

$body = file_get_contents('php://input'); // That might be different depending on how you run PHP

$toSecure = $timestamp . '|' . $body;

$decodedSecret = base64_decode($client_secret);
$calculatedHmac = base64_encode(hash_hmac("sha512", $toSecure, $decodedSecret, true));

if (strtolower($calculatedHmac) !== strtolower($hmac)) {
	die('The calculated HMAC does not match with the supplied one.');
}

$allowedOffset = 15 * 60; // 15 minutes
if ($timestamp < time() - $allowedOffset) {
	die('The request has expired. This seems like a reply attack.');
}

5.3Reliability

In case your server is offline we repeat the requests multiple times. We consider the request to be delivered successfully when we receive a HTTP response with a HTTP status code 2XX. Any other response will be considered as failed. That includes redirects via HTTP status code 302 or 301.

Also when the server is not reachable we will repeat the call. There is also a timeout of 30 seconds. You server must answer within 30 seconds otherwise we repeat the call. When we repeat the call we include updated headers (x-mac-value and x-timestamp) and potentially also the body might contain updated data.

It is up to you how you handle multiple invocations. Depending on the use case there are different alternatives. Please check the corresponding documentation to see what possibilities you have to prevent the execution of the corresponding operation more than once.