Personal Token SCA

As part of our ongoing improvements in compliance with requirements specified in the Payment Services Directives (PSD2), we no longer support signing API requests to complete strong customer authentication on personal Wise accounts. You can no longer retrieve account statements or fund payments using this method. It is still possible to create draft transfers using our API and fund them from your multi-currency account using our website or mobile applications.
Things To Note

By default, in our sandbox environment, strong customer authentication is disabled. You can enable it for your account on the public keys management page.

When making a request to an SCA protected endpoint, a 403 Forbidden HTTP status code is returned together with a one-time token (OTT) value which needs to be signed and the resulting signature included in the retry of the original request.

Below is a sequence diagram showing this flow.

Example SCA flow

We use a digital signature scheme based on public-key cryptography. It involves creating a signature using a private key on the client side and verifying the signature authenticity on the server side using the corresponding public key the client has uploaded.

To call the endpoints requiring additional authentication:

  • Create a key pair consisting of a public and a private key
  • Upload the public key to Wise
  • Set up response handling to retry with the signed OTT

Creating the key pair

Keys can be generated with the OpenSSL toolkit:

bash script

$ openssl genrsa -out private.pem 2048 $ openssl rsa -pubout -in private.pem -out public.pem

The following requirements apply:

  • The cryptographic algorithm has to be RSA
  • The key length has to be at least 2048 bits
  • The public key should be stored in PEM file format using a .pem file extension

Managing uploaded public keys

The public keys management page can be accessed via the "Manage public keys" button under the API tokens section of your Wise account settings.

You will be prompted to perform 2FA when uploading new public keys.

The maximum number of public keys you can store is limited to 5.

Signing the data

We will only accept the signatures created with SHA256 with RSA (SHA256 hash of the data is signed with RSA) algorithm. There are different ways of creating the required digital signature, for example:

The shell one-liner to sign a string, encode it with Base64 and print to standard output:

signing the data

$ printf '<string to sign>' | openssl sha256 -sign <path to private key.pem> | base64 -w 0

  • OpenSSL

    The CLI toolkit command is openssl sha256 -sign private.pem data.bin (consult the openssl man pages for additional info if required). Note that the signature returned by OpenSSL (to standard output in the example above) is in a binary format and to send it over HTTP it should be encoded to Base64 (RFC 4648)).

    There is also an extensive C API available.

  • Our reference implementation Java library

  • Reference implementations in Python & Go

Detailed workflow

Here is a step-by-step workflow with example commands (which may vary slightly depending on the exact versions of utilities used).

Client makes a request which requires strong authentication.

$ curl -i -X POST '{profileId}/transfers/{transferId}/payments' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <your api token>' \
-d '{"type": "BALANCE"}'

The request is declined with HTTP status 403 / Forbidden with the following response headers * X-2FA-Approval-Result: REJECTED * X-2FA-Approval containing the one-time token (OTT) value which is what needs to be signed

Response before Signing
HTTP/1.1 403 Forbidden
Date: Fri, 03 Jan 2020 12:34:56 GMT
Content-Type: application/json;charset=UTF-8
x-2fa-approval-result: REJECTED
x-2fa-approval: be2f6579-9426-480b-9cb7-d8f1116cc8b9
"timestamp": "2020-01-03T12:34:56.789+0000",
"status": 403,
"error": "Forbidden",
"message": "You are forbidden to send this request",
"path": "/v3/profiles/{profileId}/transfers/{transferId}/payments"

Client signs the OTT with the private key corresponding to a public key previously uploaded for signature verification.

$ printf 'be2f6579-9426-480b-9cb7-d8f1116cc8b9' | openssl sha256 -sign private.pem | base64 -w 0

with private.pem private key:


Client repeats the initial request with the OTT provided in the X-2FA-Approval request header and the signed OTT in the X-Signature request header.

Request after signing
$ curl -i -X POST '{profileId}/transfers/{transferId}/payments' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <your api token>' \
-H 'x-2fa-approval: be2f6579-9426-480b-9cb7-d8f1116cc8b9' \
-H 'X-Signature: RjXBO5SpAuMGdgTyqPryOt8AyIKY0t5gHqj36MzR2UwH9SvSY1V1wKIQCqXRvLMLyWBGXDkLvv9JdAni+H87k3hsClRiyfpdzcg3uOP+d/jSagNDSjHixPh4/rWQh+eEhBRo4V+pPBH+r5APtIwFY/fvvdMbZ/QnnmcPHxi/t7uS7+qvRZCC17q47T0ZpSwEK9x+nG/wcJ4S4Yrk0E2yQlLz8F35C+E2gt/KGTt6Tf5z6GonM1H2gJWoHpxuOUomh09b/k3teLjIfEirWmnO2XuOe0oDCUH8i10dokzk+QrM4t/Yv/Rb18JvTeugDAKMydGo7KTgqKGCXZauicX0Ew==' \
-d '{"type": "BALANCE"}'

Customer has authenticated and the action is completed. You will receive a response with x-2fa-approval-result status APPROVED in headers.

Response after signing
HTTP/1.1 200 OK
Date: Fri, 03 Jan 2020 12:34:56 GMT
Content-Type: application/json;charset=UTF-8
x-2fa-approval-result: APPROVED
"type": "BALANCE",
"status": "COMPLETED",
"errorCode": null

As the name implies, a one-time (OTT) token can be used only once. If it was successfully processed then further requests with the same token signature will be rejected.