Skip to main content

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.

Client credentials based interaction

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.

Bank ID based interaction

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:

ScopeMeaning
assetView symbol and price information
account.baseView and modify the customer's KYC information and personal details including email address
account.base:readView the customer's KYC information and personal details including email address
account.bank-accountView and modify the customer's bank account(s) information
account.bank-account:readView the customer's bank account(s) information
orderView, Create and amend details of the customer's buy and sell orders
order:readView details of the customer's buy and sell orders
walletView the customer's transaction details in their wallet

Check the full API documentation for the exact applicability of each scope.