Security
Basic Authentication
To securely access the API, most requests must comply with the following requirements:
Authentication Method
Every request must include valid credentials using Basic Authentication (username and password).
Credential Usage
The username and password must be provided with each API request.
Secure Transport
All requests must be sent over HTTPS to ensure data is encrypted in transit.
Content Type
Requests must include the following header:
Content-Type: application/json
JWT based Bearer Authentication
As part of our commitment to providing secure and robust integration options, the Scan To Pay (STP) Remote API now also supports JWT Bearer Authentication for managers. This new authentication method allows API Integrators (e.g., Banks, Partners) to establish a more advanced mutual trust model through PKI certificate-based authentication.
This guide outlines how to configure, obtain, and utilize JWT tokens to interact with the STP Remote API successfully.
1. Configuration on Masterpass Backoffice (Done by the support team)
To ensure backward compatibility, JWT Authentication is strictly Opt-In.
To enable this feature for your account, please contact the Support team to perform the following configuration on your Manager Profile in the Backoffice:
- Auth Type Configuration: The Authentication Type for the manager must be changed from
BasictoBearer,or a new one must be created. - Once configured as
Bearer, your traditional password credentials for the STP API will be disabled and cleared out. Only valid JWTs will be accepted for incoming API requests.
3. Obtaining the Access Token (Identity/Password + PKI)
The PKI authentication uses pairs of public/private keys held by the client and ScanToPay to prove their identity.
In order to authenticate with PKI, the flow is as follows:
Prerequisites
-
Client generates a 4096bit RSA public/private keypair as follows and stores the private key in a secure location and never exposes it to anyone (not even ScanToPay):
openssl req -nodes -x509 -sha256 -newkey rsa:4096 -keyout "PrivateKey.key" -out "PublicKey.crt" -days 99999 -
Client extracts the public key and shares it with our support team to be configured for their manager. The base64 encoded public key can be extracted from
PublicKey.crtas follows:openssl x509 -in PublicKey.crt -pubkey -noout | grep -v "\-\-\-" | base64 -d | base64 -w0 -
Client has requested and received ScanToPay’s public key out of band (e.g. sent by email by ScanToPay support).
Authentication Flow
- Client calls
GET https://ragstest.oltio.co.za/authentication/login-challengespassing 2 query parameters: theidentitythey want to authenticate with and base64 encoded data calledclientChallenge.clientChallengeis generated by the client by generating 64 bytes of random data and encrypting it with the ScanToPay public key using RSA/ECB/OAEPWithSHA-1AndMGF1Padding and then base64 encoding the result. The client should store a SHA256 hash of the pre-encrypted 64 bytes so that it can verify the response sent by the server. NB: Base64 has characters such as "+" which is decoded as a space. Make sure the base64 is URLEncoded on the query path. - ScanToPay receives the request and decodes and decrypts
clientChallengeusing ScanToPay’s private key and does a SHA256 of the result and base64 encodes that and puts it in response fieldbase64EncodedClientChallengeResponse. Next ScanToPay generates its own random challenge and encrypts it with the public key stored against the identity passed by the client and puts the base64 encoded result in fieldbase64EncodedChallenge. ScanToPay stores a SHA256 hash of its pre-encrypted challenge in order to verify the client's response to the challenge coming in the next step. - The client checks that the server's
base64EncodedClientChallengeResponseis correct and if so knows the server must be in possession of the ScanToPay private key. Assuming its correct (if not stop immediately), the client decryptsbase64EncodedChallengeand gets a SHA256 hash of the decrypted data and puts this as base64 in a field calledbase64EncodedChallengeResponsein a body soon to be posted to the/authentication/loginendpoint. It also does a SHA256 hash of the challenge it was asked to decrypt so that the server knows what challenge this is in response to. This is put as base64 in fieldbase64EncodedChallengeHash. The identity and password fields are populated. This is posted tohttps://ragstest.oltio.co.za/authentication/login(This must be done within 600s of step 2 on sandbox and 60s on live). - The server receives the login request and verifies that the client's response to the challenge is correct as well as the identity/password being correct. The server thus knows that the client must be in possession of the private key for the public key it has on record for the identity being authenticated. The server responds with a JWT as before.
Example Code and Requests
package com.mycompany.crypt;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import javax.crypto.Cipher;
public class Main {
public static void main(String[] args) {
try {
if ("generate-challenge".equals(args[0])) {
generateChallengeAndExpectedResponse();
} else if ("generate-response".equals(args[0])) {
decryptServerChallenge(args[1]);
} else {
System.out.println("Mode can be generate-challenge or generate-response");
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void generateChallengeAndExpectedResponse() throws Exception {
// Get from your own configuration somewhere. This will have been sent to you out of band as per prerequisite 3
// E.g. This is the ScanToPay sandbox public key hardcoded as an example
byte[] x509PublicKey = Base64.getDecoder().decode("MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA2cJSqdgRyq2mvH0I0pg+pEBTKvuiQc/Bz2jIv8gvqXEx7l8AnBgaqFi53WnaCpumYF0+xjEqvfjlSWQGv6pkI/ZFCRJtZsehEvJ0DUo4qqByBgRCf28yGCSNxq3Nr/EFEqR+rmwSeegFo1x/QgtHzdOT/32EI0i19vY1eVARM1f2YWfHxZR+zP6g+pm2RKI8cQ01udez4MMB/rhyy3id0/BAk/x/kx3+Y2qyKqqfhTSqjhs/gqnXg9VTvizMzyJoxQZs+jgJJmbw+7WssstdvbqLe7+lJVgpDUdBs3768UPlaoYPDBkWtnS7U7W6zFHSsYu/wk3aYQIMKZqq/vZmBqzV65oajK9Bt2viCRj5RvNClshQW+OLSeIkUrna5Xq1YkhYJTHDHd5qfaOdBDxFXFscfKkn9CM1LZT/siLeNkuBPTpaoRR+Wtp1SheUIc+IC0f3CSYZrwRnuOn2tnGPbkLkEnDPxyx6sU97MEqjtQwgNPZTRI6YD4D4o/NRcmJ76OOtIJuoaMNoWv8p4FIFJ2AxVwQVxRav9LEcByIEZukrMU1wTRnRvhfQgjG3idbg3XiUeTYdgGg1XjYB39JJ16w7cv4QW+J1kpl3BGL+ZgzlIiBZ8vd9Ou/4Ft12PT5TqVd0z4yciiZRL5eDHxn+KiM5GbRzapczI/jEA+vDutcCAwEAAQ==");
byte[] randomData = new byte[64];
SecureRandom.getInstanceStrong().nextBytes(randomData);
String expectedResponseFromScanToPay = Base64.getEncoder().encodeToString(MessageDigest.getInstance("SHA-256").digest(randomData));
Cipher oaepFromAlgo = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
X509EncodedKeySpec publicSpec = new X509EncodedKeySpec(x509PublicKey);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
oaepFromAlgo.init(Cipher.ENCRYPT_MODE, keyFactory.generatePublic(publicSpec));
String challengeToScanToPay = Base64.getEncoder().encodeToString(oaepFromAlgo.doFinal(randomData));
System.out.println("challengeToScanToPay (What ScanToPay should be able to decrypt): " + challengeToScanToPay);
System.out.println("expectedResponseFromScanToPay (What ScanToPay should return to the login-challenge call): " + expectedResponseFromScanToPay);
}
public static void decryptServerChallenge(String serverChallengeBase64) throws Exception {
// Your private key created with:
// openssl req -nodes -x509 -sha256 -newkey rsa:4096 -keyout "PrivateKey.key" -out "PublicKey.crt" -days 99999
// Then for the public key to set on ScanToPay identity: openssl x509 -in PublicKey.crt -pubkey -noout |grep -v "\-\-\-\-\-"| base64 -d| base64 -w0
// Then for the private key to encrypt with: cat PrivateKey.key |grep -v "\-\-\-\-\-"| base64 -d| base64 -w0
// This is just an example private key. DO NOT USE THIS IN PROD. The corresponding public key is:
// MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwTWV08TJLo/y+N67DjlFh0RFO4oyULlF5oWBain5xWzycFvbDTdiPJvyfdACNsnEBcssjz5zD5ZOOQrJrYRgI2MzkhxmiXUWJAndZOixk6dhQObKru46xSRyOnI97UxFE4oBG+BLlC/jQucc7SF3yGUCzdbugd4poLpn0CcVPGMfcPoVKNgK2XmyZAmZO13vgZctyS9VHHWwB1sOEtIUr4zkcon1lBZRqBv49y0d/QMkQ6Aeqi/C3T4VPqhCDJNvG7h8RUTbroQav5YSnNwf4j+2rE4U1QO2Sc/noMWK1Xd8V792Nx61itfITNGhZEenPPyzVb9n8G6AlXsx+Kk8WpJB9zbBvn8yqcXAZR5cHUz5fpmGnCIs4t1LclOxoqsXyrEtwT1dGGi22QD9zAEhw97NjXqhAH3rR4Z75qp51zFWnLgm4IrC+Blu6mA7cG8pbER7CU6y7+tEVGtYgRiGT16Wmi4JKrjOlxcup+uQ+N+rBA8nfN3WP28Va6W0aXoWMnEao2PtHv3UYj7+TWZ/xDXb88D6fh+DngBHeI5axuh++Z74akXYzD9frsDKk9y/wWQv2a20HftQIlVMLYu84ZBUnyQj7VxqYQ2yuNu9jJKY38SlAlgWKgAQF6owtG8YUFdGgBGig/Odd9bU94SGwwi24LnT2mSsN6O+OyUo+lcCAwEAAQ==
byte[] pkcs8EncodedPrivateKey = Base64.getDecoder().decode("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");
PKCS8EncodedKeySpec privateSpec = new PKCS8EncodedKeySpec(pkcs8EncodedPrivateKey);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] serverChallenge = Base64.getDecoder().decode(serverChallengeBase64);
Cipher oaepFromAlgo = Cipher.getInstance("RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
oaepFromAlgo.init(Cipher.DECRYPT_MODE, keyFactory.generatePrivate(privateSpec));
byte[] decryptedChallenge = oaepFromAlgo.doFinal(serverChallenge);
String base64EncodedChallengeResponse = Base64.getEncoder().encodeToString(MessageDigest.getInstance("SHA-256").digest(decryptedChallenge));
String base64EncodedChallengeHash = Base64.getEncoder().encodeToString(MessageDigest.getInstance("SHA-256").digest(serverChallenge));
System.out.println("base64EncodedChallengeResponse: " + base64EncodedChallengeResponse);
System.out.println("base64EncodedChallengeHash: " + base64EncodedChallengeHash);
}
}End to End Example on Sandbox
Here is an end to end example done with the above code on sandbox:
-
Configure Manager Identity: Ensure a public/private key has been configured against a manager identity as per the prerequisites above.
-
Run java program to generate login challenge request:
challengeToScanToPay (What ScanToPay should be able to decrypt): 1TGcy5l3ZnmndwWPGk1s++IqVMOpOx46AH+8MgTIX07byn485agyYIdLsNaHyph4K/gDfjeXdbc+fyOCkqAaQYsMhhE5ttcAmBEuX0NueEXDccCoJ9A3IwGEzOzqHZjp3egiYrsU5Jn23nDT1XHMSrRY21p1DdilwnoYlnOXO+uRf8xRSrZLKUekPx43O6Rpy/AShRl2gwQug9zgAap40blT6aSEIL27bq0zzk9VJoeiQfncL7P3OM/kI3eP146enUuML8pmyqnUf0NX9lkkXLO4rz9ZCdw/UZHMicuvc2Jcwt4AEDnxZlbQLT6eYptMqzg6XcbgOfZvC2fKOGhN0rfgZVw81/e6UjC6ZipBkco2jqpwSgh6qeqy2MnGvg/VTtD7RNUT9VigqLiIDTDraD2y5sdXtP8SewcfDFX4eUUAzq2dG9yb6//5w9TmkdefSwFUKqJsqQldpOF0iHI9cvzaTgidsJwrZzgGtXYewm18cRRGvtzdYkl7+YQje9AtnwRmSKse6Z068YALqxDvxBbK0TLa8pS+O0j0GfJI3hLS9DAGA5NS/8UIp9/Yq3S8HxJUnP0k5SZd8K4pY9xb9TzQOoyGNy/g1qjzC2tAe0+aw0eMxFAlwtyzIw5A2PYkfW5NdSXTaz5cEDpelBrRfGNdJ+KGwl0fLM5CIbY+MbQ= expectedResponseFromScanToPay (What ScanToPay should return to the login-challenge call): DeAObN6HXiBfXPNEczTpEmAJxTwakG/RSGOe0w8+e8U= -
Send request to get login challenge:
curl -X GET "https://ragstest.oltio.co.za/authentication/login-challenges?clientChallenge=1TGcy5l3ZnmndwWPGk1s%2B%2BIqVMOpOx46AH%2B8MgTIX07byn485agyYIdLsNaHyph4K%2FgDfjeXdbc%2BfyOCkqAaQYsMhhE5ttcAmBEuX0NueEXDccCoJ9A3IwGEzOzqHZjp3egiYrsU5Jn23nDT1XHMSrRY21p1DdilwnoYlnOXO%2BuRf8xRSrZLKUekPx43O6Rpy%2FAShRl2gwQug9zgAap40blT6aSEIL27bq0zzk9VJoeiQfncL7P3OM%2FkI3eP146enUuML8pmyqnUf0NX9lkkXLO4rz9ZCdw%2FUZHMicuvc2Jcwt4AEDnxZlbQLT6eYptMqzg6XcbgOfZvC2fKOGhN0rfgZVw81%2Fe6UjC6ZipBkco2jqpwSgh6qeqy2MnGvg%2FVTtD7RNUT9VigqLiIDTDraD2y5sdXtP8SewcfDFX4eUUAzq2dG9yb6%2F%2F5w9TmkdefSwFUKqJsqQldpOF0iHI9cvzaTgidsJwrZzgGtXYewm18cRRGvtzdYkl7%2BYQje9AtnwRmSKse6Z068YALqxDvxBbK0TLa8pS%2BO0j0GfJI3hLS9DAGA5NS%2F8UIp9%2FYq3S8HxJUnP0k5SZd8K4pY9xb9TzQOoyGNy%2Fg1qjzC2tAe0%2Baw0eMxFAlwtyzIw5A2PYkfW5NdSXTaz5cEDpelBrRfGNdJ%2BKGwl0fLM5CIbY%2BMbQ%3D&identity=testidentity" -H "accept: application/json"Response:
{ "expires": "2021-01-28T16:07:13.020Z", "base64EncodedChallenge": "dL2TobNg1K6ISkw2L80zH8c6E7K7yQNndHDA/ntKuw/K6k0IsIbEUfkwQf5vUwNQSjApSne5QORXx3ZCFe4BroP3HRLxR7v3dPhYiSBbZGPP3XMdfq3Zm4eztSVcNtv+JZOiAJvghbO4hvqHy8rjvEFBTAhqlcMB6wejpvZBxdd3cqQCwrCACrgQ2GzRvo1nA505qVyB/2v7IpX/AXyjHxiqX9PZxJz9gLdRSHNllL6mrjWL9B8ldvCjBwdk0nYc1zfX46Lw1/2kdsdZYMkOsiFwy5E9lYSBssdYAXzRmuwkko16vSiw4BPEb3VZl4WpGkjtvgnEpnqAGlTq+fuVXwl4fDEG53OxkNOn0uLGV3s6hlgtkYdpqOzmy10g1wIHuzEZo3q7SXpBL+mQC+NWS2uFOE9RFDolI+JGwk6ZVknNGeCalv7wwF+d6eQD5Ip8VRKD72d1Ce8g6JJYp3viUWDMEPbz0HNkos4NQlLG3uxLovXn/Jq/b+9Aygw3jIMHXDin6ETkkymqfNdmqjoC0jpl07kxVK+Ny3NLE7jYoKFmAT6UNodGOwC4TSdWu6ebL/4SNAVThC4ZzaLtnTypSRSRhDWEbUj63478RgSE+z3Iy+paChwhSr8q3VnyPMrFzsrsvjxUL4uXVqWAF44oP7PO+bW0AkwUcsGgZVS+LeE=", "base64EncodedClientChallengeResponse": "DeAObN6HXiBfXPNEczTpEmAJxTwakG/RSGOe0w8+e8U=" } -
Verify Response: Check importantly that
base64EncodedClientChallengeResponse(DeAObN6HXiBfXPNEczTpEmAJxTwakG/RSGOe0w8+e8U=) matches theexpectedResponseFromScanToPaythe java snippet predicted. This ensures you are speaking to the authentic ScanToPay API. -
Pass
base64EncodedChallengeto the java program to generate the challenge response:base64EncodedChallengeResponse: wadU7UwGsjY2Zs10mbqZxLNvy/EjPSLdb7WmwdpAnwc= base64EncodedChallengeHash: jNyQxcZIXgvuzDOfEOQQ1ACisGQ2uKa8RqG4/JWPUCM= -
Login using the challenge response, hash, identity, and password:
curl -X POST "https://ragstest.oltio.co.za/authentication/login" \ -H "accept: application/json" \ -H "Content-Type: application/json" \ -d "{\"base64EncodedChallengeHash\":\"jNyQxcZIXgvuzDOfEOQQ1ACisGQ2uKa8RqG4/JWPUCM=\",\"base64EncodedChallengeResponse\":\"wadU7UwGsjY2Zs10mbqZxLNvy/EjPSLdb7WmwdpAnwc=\",\"identity\":\"testidentity\",\"password\":\"testpassword\"}"Response:
{ "headerName": "Authorization", "headerValue": "Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIi...<snip>...", "sessionId": "c056b2d8-d6d1-4ea6-8752-e87f684b2903", "expires": "2021-01-28T16:30:13.341Z" }
4. Making API Requests
When making an API call to the STP Remote API, attach the retrieved JWT in your HTTP headers.
Include the token in the standard Authorization header with the Bearer prefix:
Authorization: Bearer <your_obtained_jwt_token_here>Example Request:
curl --location --request GET '{{stpBaseUrl}}/remote/v4/menu' \
--header 'Authorization: Bearer eyJhbGci...<snip>...xyz'5. Token Validation Strategy (Internal Process Overview)
Once your request hits the STP Remote API, the following validation takes place:
- Expiration Check: The token is analyzed to verify it hasn't expired.
- Backend Authorization Lock Check: The STP Gateway verifies that your Manager entity has explicitly enabled
BearerAuth Type.
If any of these constraints fail, the API will immediately reject the packet with standard HTTP 401 Unauthorized.
6. Troubleshooting
| Symptom / Error Response | Potential Cause & Resolution |
|---|---|
401 Unauthorized - Invalid or expired JWT token | Token has expired due to TTL or the signature is invalid. Fetch a new JWT token. |
401 Unauthorized - Invalid JWT token | Missing or completely invalid structure for the Authorization header. Ensure it is pre-fixed precisely with Bearer (including the space). |
401 Unauthorized - JWT Authentication disabled for this account | JWT authentication hasn't been enabled on your Profile by EFT Corp. Contact the support team to switch Auth Type to Bearer. |
Need further assistance?
For support related to JWT provisioning or Backoffice configuration, please contact your Technical Account Manager or the Integrations Helpdesk.
Updated 18 days ago
