Obtain Authorization via Open Banking Access
Introduction
To work with permanent users, they must be created and logged in directly in finAPI OpenBanking Access. This requires 2 types of logins, which should be known.
On the one hand, a login as a client is needed to create new users and on the other hand, the login of the user itself is important.
Please note: If users are managed directly in finAPI Open Banking Access, they must also be deleted by the client if the user is no longer needed.
This also means, that before it is deleted in finAPI Open Banking Access, it must be deleted in all Data Intelligence systems where it was used.
For one-time users, we therefore recommend the use of the User Management of the Process Controller, as the administration of users is automated there.
To learn how to self-manage users, we offer the Getting Started - User Management page.
Prerequisites
The major prerequisite is to have a valid set of client credentials: client_id
and client_secret
.
TL:TR
Used Endpoints
In short, you need to call those 2 endpoints on the finAPI Open Banking Access to obtain a token.
Description | HTTP Method | Process Controller Endpoint | Link to API Doc |
---|---|---|---|
Get tokens | POST |
| https://docs.finapi.io/?product=access#post-/api/v2/oauth/token |
Process Overview
cURL Example
Translated into cURL it looks like the following:
Step 1 - Login (cURL)
Log in with client or user credentials with the Content-Type
header application/x-www-form-urlencoded
.
Log in as a client requires
grant_type=client_credentials
and nousername
andpassword
fieldsLog in as a user requires
grant_type=client_credentials
andusername
andpassword
of the user have to be provided
Login as client example request:
curl --location 'https://sandbox.finapi.io/api/v2/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=<your client id>' \
--data-urlencode 'client_secret=<your client secret>' \
--data-urlencode 'grant_type=client_credentials'
Login as user example request:
curl --location 'https://sandbox.finapi.io/api/v2/oauth/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=<your client id>' \
--data-urlencode 'client_secret=<your client secret>' \
--data-urlencode 'username=<username>' \
--data-urlencode 'password=<user password>' \
--data-urlencode 'grant_type=password'
The result looks like this:
{
"access_token": "yvMbx_TgwdYE0hgOVb8N4ZOvxOukqfjzYOGRZcJiCjQuRGkVIBfjjV3YG4zKTGiY2aPn2cQTGaQOT8uo5uo7_QOXts1s5UBSVuRHc6a8X30RrGBTyqV9h26SUHcZPNbZ",
"token_type": "bearer",
"refresh_token": "0b9KjiBVlZLz7a4HshSAIcFuscStiXT1VzT5mgNYwCQ_dWctTDsaIjedAhD1LpsOFJ7x6K8Emf8M3VOQkwNFR9FHijALYSQw2UeRwAC2MvrOKwfF1dHmOq5VEVYEaGf6",
"expires_in": 3600,
"scope": "all"
}
Implementation Guide
See a full working project here: finAPI Data Intelligence Product Platform Examples (Bitbucket)
Code from this guide can be found here: Authentication with Open Banking Access OAuth (Bitbucket)
Environment overview can be found here: Environments
The guidelines and the example project are written in Kotlin with Spring Boot. But it is easily adoptable for other languages and frameworks.
For the HTTP connection, we are using here plain Java HttpClient
, to be not restrictive for the client selection and that everything is transparent.
Only the actual functionality is discussed in this guideline. Used helper classes or the models can be looked up in the source code.
However, we always recommend using a generated client from our API, which reduces the effort of creating models and functions to access the endpoints and eliminates potential sources of error.
To learn how to generate and apply the API client using the Process Controller "Create user and exchange with access_token" as an example, please see the Getting Started - Code Generator (SDK) Integration.
Please also note that the code presented here and also the repository is only an illustration of a possible implementation. Therefore, the code is kept very simple and should not be used 1:1 in a production environment.
Step 1 - Login
To access services, an access token is required. This token can be checked by the application and is associated with a user or client.
We distinguish between a client access token and a user access token.
A client access token can only be used for administrative purposes, while a user access token is used for business access to the endpoints.
To gain access to a token, the client must first authenticate to an OAuth endpoint. finAPI uses always the Direct access grant
flow.
The credentials are transmitted in a POST request to the finAPI Open Banking Access endpoint /api/v2/oauth/token
, but the credentials are not transmitted as a body, but as a query parameter together with the Content-Type
header application/x-www-form-urlencoded
.
A client token is always requested with the parameter grant_type=client_credentials
and without username
and password
, while a user token is requested with grant_type=password
and the two parameters.
All query fields are transferred urlencoded
.
For more information have a look at our Access API Documentation
Field | Type | Mandatory |
---|---|---|
| The finAPI | yes |
| The finAPI | yes |
| Defines which grant type should be used.
| yes |
| The username ( | if |
| The password of the user. Only required, if | if |
First, we create a class that will handle the login.
The constructor of the class receives the URL of finAPI Open Banking Access, as well as the client credentials.
We also need an object mapper to easily map the JSON data into objects. The models used can be found in Bitbucket.
For the request, we first create a parameter list (requestQuery
), that links the query parameters with &
.
Please note that the Content-Type
header is set to x-www-form-urlencoded
and the parameters are not transferred in the body but as query parameters.
@Component
class OAuthAuthorization(
@Value("\${finapi.instances.openbanking.url}") private val openBankingUrl: String,
@Value("\${finapi.security.credentials.clientId}") private val clientId: String,
@Value("\${finapi.security.credentials.clientSecret}") private val clientSecret: String,
private val objectMapper: ObjectMapper
) {
/**
* Create a HttpRequest with configured URLs and required headers for the login.
*/
private fun createLoginRequest(
username: String? = null,
password: String? = null
): HttpRequest {
// create query parameters with grant_type and credentials
val requestQuery = StringBuilder()
requestQuery.append("client_id=${clientId}")
requestQuery.append("&client_secret=${clientSecret}")
// is username and password are set, we want to do a user login, else we are using
// client_credentials only for client login
if (username != null && password != null) {
// if a user is present, we use the grant_type password
requestQuery.append("&username=${username}")
requestQuery.append("&password=${password}")
requestQuery.append("&grant_type=password")
} else {
// else we use client_credentials
requestQuery.append("&grant_type=client_credentials")
}
// create request with uri, header and data
return HttpRequest.newBuilder()
// build URL for https://<openBanking>/api/v2/oauth/token
.uri(URI.create("${openBankingUrl}${URI_OAUTH_TOKEN}"))
// set Content-Type header to application/json
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE)
// add the body
.POST(HttpRequest.BodyPublishers.ofString(requestQuery.toString()))
.build()
}
companion object {
private val log = KotlinLogging.logger { }
const val URI_OAUTH_TOKEN = "/api/v2/oauth/token"
}
}
Now that we can create a request, we need to create a client for communication and send the request.
The result is checked at least for the status code.
If this is ok, we map the JSON into an object and return only the access token in the simple example.
If something went wrong, we’ll throw an exception.
Please note that throwing a simple RuntimeException
for a production system is not a good idea. Please use your own exception, which will then be handled correctly in your code.
@Component
class OAuthAuthorization(
@Value("\${finapi.instances.openbanking.url}") private val openBankingUrl: String,
@Value("\${finapi.security.credentials.clientId}") private val clientId: String,
@Value("\${finapi.security.credentials.clientSecret}") private val clientSecret: String,
private val objectMapper: ObjectMapper
) {
@Throws(RuntimeException::class)
fun obtainToken(
username: String? = null,
password: String? = null
): String {
// get an access token with Process Controller
val client = HttpClient.newBuilder().build()
// send the request and fetch the response
val response = client.send(
createLoginRequest(
username = username,
password = password
),
HttpResponse.BodyHandlers.ofString()
)
// check for status code is 2xx or log and throw an exception
StatusCodeCheckUtils.checkStatusCodeAndLogErrorMessage(
response = response,
errorMessage = "Unable to login."
)
// return the object of the mapped result
val oauthResponseModel = objectMapper.readValue(response.body(), OAuthResponseModel::class.java)
return oauthResponseModel.accessToken
}
[...]
}
The token should now be placed in the SecurityContext
, for example, so that clients to the finAPI systems can access it.
It must be ensured that the token can only be accessed by the user to which the token belongs.