Authentication
In general authentication follows oauth2. We use two of the standard grant types, client credentials and refresh token, and a custom grant type for getting a token bound to a customer via BankID.
tip
For the benefit of automated testing against our staging authentication endpoint you may include a Test
header
value of true
to allow for a successful Bank ID flow without actually carrying out the Bank ID verification itself.
Naturally this feature is not supported in the production endpoints.
Tokens​
Tokens are strings representing one or more permissions. They must be generated by interacting with the Safello authentication API. All of our business APIs (everything except the OAuth2 endpoints) require a Bearer token for access. The mechanics of acquiring various types of token are given in the following sections.
A tokens in detail section of the documentation covers the full details of the JWT tokens we are using in our API.
Client credentials​
For security, as a minimum you will need your client credentials to access any of the Safello endpoints. Using only client credentials you can acquire an access token that will then let you retrieve data from endpoints where the resource owner isn't a customer, for example for listing currency symbols or prices.
This type of access token is obtained by calling into the
/oauth2/token
endpoint supplying
a grant_type
value of client_credentials
in the request payload.
Using only client credentials to obtain an access token example
export CLIENT_ID=...
export SECRET=...
export CREDENTIALS_BASE_64=$(echo -e -n "$CLIENT_ID:$SECRET" | base64)
curl \
--silent \
--header "Authorization: Basic $CREDENTIALS_BASE_64" \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'source-ip-address: 127.0.0.1' \
--header 'source-user-agent: curl' \
--request POST \
--location 'https://api.s4f3.io/oauth2/token' \
--data 'grant_type=client_credentials'
{
"access_token":"... redacted ...",
"scope":"full",
"token_type":"Bearer",
"expires_in":299
}
Listing symbols example
In this example the value of the BEARER_TOKEN
environment variable should populated from the value of
the access_token
property of the output of the preceding example (obtaining an access token for client credentials).
Note that the authentication type for the business endpoints then becomes Bearer
rather than Basic
authentication
and that the client credentials do not need to be presented for each call to a business endpoint - only the appropriate
token.
export BEARER_TOKEN=...
curl \
--silent \
--header "Authorization: Bearer $BEARER_TOKEN" \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'source-ip-address: 127.0.0.1' \
--header 'source-user-agent: curl' \
--request GET \
--location 'https://api.s4f3.io/v2/symbols/crypto'
Output of the listing symbols example
[
{ "currency":"BTC","minorAmountDecimals":8,"displayName":"Bitcoin","status":"active","availableActions":["buy","sell"] },
{ "currency":"DOT","minorAmountDecimals":8,"displayName":"Polkadot","status":"active","availableActions":["buy","sell"] },
{ "currency":"MATIC","minorAmountDecimals":8,"displayName":"Polygon","status":"active","availableActions":["buy","sell"] },
{ "currency":"XRP","minorAmountDecimals":8,"displayName":"Ripple","status":"deprecated","availableActions":[] },
{ "currency":"ETH","minorAmountDecimals":8,"displayName":"Ethereum","status":"active","availableActions":["buy","sell"] }
]
BankID​
In order to retrieve resources (data) that is owned by the customer you must get the customer's approval via the Bank ID process. Once complete you will hold an access and refresh token that can be used to access these resource - but only for that specific customer.
For example, you must have a token produced by the Bank ID flow to access a customer's email address - but you will not be able to access any other customer's email address using this token.
For simplicity in the "Bank ID based interaction" figure the network traffic between Safello's services and the Bank ID service is not shown.
To initiate the BankID flow make a request to /oauth2/bankid
with a Swedish
personal identity number and you'll get a request identifier in the response payload.
Initiate bank flow example
export CLIENT_ID=...
export SECRET=...
export PERSONAL_NUMBER=...
export CREDENTIALS_BASE_64=$(echo -e -n "$CLIENT_ID:$SECRET" | base64)
curl \
--silent \
--header "Authorization: Basic $CREDENTIALS_BASE_64" \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'source-ip-address: 127.0.0.1' \
--header 'source-user-agent: curl' \
--request POST \
--location 'https://api.s4f3.io/oauth2/bankid' \
--data "pnr=${PERSONAL_NUMBER}"
warning
At the present time you cannot have two BankID requests for the same personal number active at the same time - if you try to start a new flow while the original is still active an error will be returned. A new flow can be started when the existing one is completed or has expired.
The response will include the request_id
field with the appropriate identifier.
{
"request_id":"e91ec331-6106-4d4e-8358-f51ec60bde04",
"auto_start_token":"28312e1a-2b63-4474-967e-c3881ba27296",
"interval":2,
"expires_in":120
}
Using the request ID you can then use the urn:safello:params:oauth:grant-type:bankid
grant type and
poll the /oauth2/token
endpoint for completion.
warning
Once the request_id has been successfully used in a call to obtain the token it cannot be re-used.
Poll for token example
export CLIENT_ID=...
export SECRET=...
export CREDENTIALS_BASE_64=$(echo -e -n "$CLIENT_ID:$SECRET" | base64)
curl \
--silent \
--header "Authorization: Basic $CREDENTIALS_BASE_64" \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'source-ip-address: 127.0.0.1' \
--header 'source-user-agent: curl' \
--request POST \
--location 'https://api.s4f3.io/oauth2/token' \
--data 'grant_type=urn:safello:params:oauth:grant-type:bankid' \
--data 'request_id=e91ec331-6106-4d4e-8358-f51ec60bde04'
Until the Bank ID process is completed an error response payload will indicate that it is in the "pending" state:
{ "error" : "authorization_pending" }
Because the Personal Identity Number was supplied in the initiating request you do not in the current version of the API need to prompt the user with a QR code or otherwise use the autostart token. When the user opens the Bank ID app associated with their personal number they will be prompted with the message "I identify myself at Safello AB".
warning
Our staging environment (api.s4f3.io) is configured to use test BankID authentication.
On completion of the Bank ID process this endpoint will return an access token and a refresh token.
{
"access_token":"... redacted ...",
"refresh_token":"... redacted ...",
"scope":"full",
"token_type":"Bearer",
"expires_in":299
}
Token expiry​
The issued access token has a fixed expiry time. This is indicated as a number of seconds in the expires_in
field of the response, so in the example response of the previous section is lasts for 299 seconds (just under 5
minutes).
You should therefore manage the expiry of the access token and when it is expired or its expiry is imminent then make a call using the refresh token to get a new access token.
The token expiry mechanics are described further in the "Tokens in detail" section of the documentation.
Refresh token​
A refresh token is given to you when you initially complete the Bank ID flow. Every time you request a new access token you'll also be given a new refresh token. The refresh token currently (2022-05-04) has a lengthy expiry period but we plan to shorten this.
caution
If the same refresh token is reused that will be interpreted as if the token was used by some non-authorized user and then the current refresh token will be invalidated. You will then need to repeat the Bank ID approval process to obtain a new refresh token.
To refresh the access token make a request to the /oauth2/token
endpoint
including the refresh token and specifying a grant type of refresh_token
Obtaining a new access token
export CLIENT_ID=...
export SECRET=...
export CREDENTIALS_BASE_64=$(echo -e -n "$CLIENT_ID:$SECRET" | base64)
export REFRESH_TOKEN=...
curl \
--silent \
--header "Authorization: Basic $CREDENTIALS_BASE_64" \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'source-ip-address: 127.0.0.1' \
--header 'source-user-agent: curl' \
--request POST \
--location 'https://api.s4f3.io/oauth2/token' \
--data 'grant_type=refresh_token' \
--data "refresh_token=$REFRESH_TOKEN"
The result of the call is a payload equivalent to the original acquisition of a refresh and access token. The access token will have the usual short-lived expiry (currently around 5 minutes). Currently (2022-05-04) the new refresh token is issued with the same expiry timestamp as the old one - however we will likely move to a model where the expiry of the new refresh token is extended.
{
"access_token":"... redacted ...",
"refresh_token":"... redacted ...",
"scope":"full",
"token_type":"Bearer",
"expires_in":299
}
This call returns a new access_token
in the response body.
Scopes​
Access to the business endpoints is further controlled by the scopes granted with the token. The complete set of scopes is:
Scope | Meaning |
---|---|
market | View market data |
account.base | View and modify the customer's KYC information and personal details including email address |
account.base:read | View the customer's KYC information and personal details including email address |
account.bank-account | View and modify the customer's bank account(s) information |
account.bank-account:read | View the customer's bank account(s) information |
order | View, Create and amend details of the customer's buy and sell orders |
order:read | View details of the customer's buy and sell orders |
wallet | View the customer's transaction details in their wallet |
Check the full API documentation for the exact applicability of each scope.