Personal Token SCA
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.
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:
$ 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:
$ 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.
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 'https://api.sandbox.transferwise.tech/v3/profiles/{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
HTTP/1.1 403 ForbiddenDate: Fri, 03 Jan 2020 12:34:56 GMTContent-Type: application/json;charset=UTF-8x-2fa-approval-result: REJECTEDx-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 01ZCN1MIDdmonOJvNQvsCxRHMXihsqZ/xNvybhb3oYNQgRkyj2P0hCVVaWUbr313LicFGwRTW8kcxTwvpXQdeurGtcN2zGoweVTopI06dmJ8vQMfTkrqjMZG3UUX0EcU+tJaDlBemvS7gv2aNGyHDMiRPZOZRPA6TH0LPJvLdVRMsEbXrbj8HqEopczmf1jChRxftmg2XoeQUMhhlOiSjSbJmlyAIegioI40/BTii+Q7f/HWZqk6N2vmHWPomwHQMz8Hy6frLYJb5tchjg/i+RRvZjEVbUH53QfG8Tbmx4JM/wN1LYeR8rebSdGEpLOd8QRcjuDur54qHNWXvKRM8aQ==
with
private.pem
private key:
-----BEGIN RSA PRIVATE KEY-----MIIEpAIBAAKCAQEA3qfjPkhbTPKJQqLm+KfHP14wJ2U318Rh/4TV8xLi605xFW7rApzXzLLxBb7zSkBc9wFIEH7wU7/BaFivg440R7ktYR07/QXZi+i0grKbfhEBW1nUjkI2eZxT3vE4VIK7Yt2jr84JiCYmjL2b/w1DatXZM9Xoa3j9YHda5cKLOfCCIeTsI3beYI9UmSnidYaXpX7q4gfHME+A/1F/19L8jFvX+c7MXapdCdY/NUHXBCJFfBzgOBlXbPKhdjtEnx+Hg5Sq/Frsld6dKwF1CDMO96YoeXBdi58JkLL/CLyy1i7UcXTbSRy0Gbd3NWSAamWdpJDDg51UR6yRJdXjtnhpdQIDAQABAoIBAGs3/4bzgvvH428yUPU2ng0WxyuBY2XEzMgl6H04AAv95xjCI+tLKeQJ22S/8ho0alALzu8aoZJCydj8s/Au32AWfRLB6CxMz9i+w4YYiiYn/DZISMIEgoUHUaAPGugfWCsgvf0fw5lLfc7SU7d7ZJaiyghbHqP6TFFSyHPRvge0so+8eRvPMxIGdfJR3gagJqKB5JbTGAnQ5Kn2eb5flDOobmCfpLDfIYH9u94Yj671xymtNYXwWxic4gA+aqWJKaqkI8JL7bFdjvjnJer5RHeXmY9UZtDhSrZWSEKniw3m02QPpgqhhhPr8xToNA/7G1/P9994fuDKKnRkedn3FEECgYEA8iTPuaNGswyx/3zCkOPHHu82CSoWRZQapegN0YoXKsCyEWcHgQJVjZlF39OK14+y4PWWPJ3AlkhrchAJbUbgpw0P+G1uiRmXVpqcaEI4xIFd8oSOG+Vrs06YZYUrb7C73mi8x3sXdOpmehMYdnSbuxrRXyk0MwpLO2PkC0wXzEUCgYEA62WYRuZMAHR4PEZA+sDBG6XzjZGw1QQvF7H6u+JOfgPbRgZ+NnLQ/ZpYiDvrJGqAp1B1NIj69zVBN0p7PuRg2v9I4tHlmWC4eyYnVCf3dM6Fhsyb7RC3MHXVNOErxRoHVNqL4iywSUnboJyvdyKc7S5wQ5gdM14HogD/i1FHs3ECgYBMuMcsfYRgJOydE82eFN25ene3jaNC5ntPB+ig9M0EWcvR4cAp6zBqTh8qnR9Hz5sQ1h+FE0K7GzUYDea+vg9ePrBJuXqla/tckF5wVlMgSBEZT1CrnBR02rlEqV4q5GeSP8NYvTKgc8iGc1hz59yT+xpNuYN1jJRru+m8fp6ntQKBgQDd7Iglv5TDkQqR+MHmJbdpM4lsXIBUM3+aXUc/vtm1YDlnyVNQTerOTKdOuP609FuaYfY9sy63xVNYpzWOU40kqiyy+qP1eAQ0xgGqC4v2aYXlUh1m4K10WILLOcYkKqfiza+3ad5BGgqfX1jlfpJn4bIhZ9WPygR0LXC+jcCFYQKBgQCelF/LIocwZ1ZW/Cx2OqMi/lkI56nLeBl7jRNiBSNIs0deN0cw4sHpBN9459NojAKBKJK1pyqajzrHae66V+8/2Zz2/gmTK1dDjznyw6TZmV3QyHTFhUtYNI7wvIG9K8gFaoSiGD/OLlLRaGiAdejKBsBx2hK73M58YQsgqIpdIQ==-----END RSA 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.
$ curl -i -X POST 'https://api.sandbox.transferwise.tech/v3/profiles/{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.
HTTP/1.1 200 OKDate: Fri, 03 Jan 2020 12:34:56 GMTContent-Type: application/json;charset=UTF-8x-2fa-approval-result: APPROVED{"type": "BALANCE","status": "COMPLETED","errorCode": null}