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.
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.
The above figure shows the following steps:
The user requests the installation of the web app.
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.
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.
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.
As a first step you have to set up a web app and retrieve the client_id
and client_secret
.
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.
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.
|
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×tamp=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.
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.
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.
|
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. |
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
.
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.
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.
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.');
}
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.