Skip to main content
Skip table of contents

Obtain Authorization via Process Controller

Introduction

To simplify use cases that provide a one-time result - like the GiroIdent KYC check - finAPI introduced the Process Controller, which provides and manages an identity transparently for the customer with a simple two-step procedure. The Process Controller is a wrapper service on top of finAPI Access.

Process Controller allows to get an access_token via a process token.
For this purpose, the create new process endpoint creates a user whose credentials are managed by it.

These users and all data are automatically deleted after some days, so that no user management is necessary.

Prerequisites

The major prerequisite is to have a valid set of client credentials: client_id and client_secret.

Security

A process token is basically a token for which a user is created in the background, which is only available for a limited time and is then completely deleted from the systems.

To ensure that the process token cannot be used by third parties to obtain an access_token, it has 2 security mechanisms.
First, the exchange of the process token for an access_token is time-limited, which can be configured in days per mandator, and it can only be exchanged a maximum of 10 times.
In addition, the query parameter invalidate=true can be specified when fetching the access_token. This means that no further token exchange is possible.

A process token should therefore always be exchanged with an access_token using the query parameter invalidate=true.

This parameter should only be set to false if a customer can resume the process at a later point in time.

TL:TR

Used Endpoints

In short, you need to call those 2 endpoints on the Process Controller to obtain a token.

Description

HTTP Method

Process Controller Endpoint

Link to API Doc

Create a process token

POST

/api/v1/processes

https://docs.finapi.io/?product=processctl#post-/processes

Get access_token from process token

GET

/api/v1/processes/{processToken}

https://docs.finapi.io/?product=processctl#get-/processes/-processToken-

Process Overview

cURL Example

Translated into cURL it looks like the following:

Step 1 - Create a Process Token (cURL)

Create a Process Token that automatically contains a user that will be deleted after some time.

BASH
curl --location 'https://di-processctl-finapi-general-sandbox.finapi.io/api/v1/processes' \
--header 'Content-Type: application/json' \
--data '{
    "clientId": "<your clientId>",
    "clientSecret": "<your clientSecret>",
    "clientReferences": [
        {
            "clientReference": "<customer-reference>",
            "clientReferenceKey": "<customer key>"
        }
    ],
    "processId": "USER_ONLY",
    "processTargetUrl": "https://finapi.io?token="
}'

The result looks like this:

JSON
{
  "processes": [
    {
      "clientReference": "<customer-reference>",
      "processToken": "8f92ec58-b9b9-11ec-8422-0242ac120002"
    }
  ]
}

Step 2 - Exchange the Process Token into an access_token (cURL)

Call the “Get access token for a process” endpoint with the process token from the previous request.

BASH
curl --location 'https://di-processctl-finapi-general-sandbox.finapi.io/api/v1/processes/<processToken>?invalidate=true'

The result looks like this:

JSON
{
  "accessToken": "89e3b64c-b9ba-11ec-8422-0242ac120002"
}

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 Process Controller (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 - Create a Process Token

In the first step, we create a process token, which allows us to exchange it with an access_token without having the user management.

This can be done against the Process Controller

To create a process token, the endpoint POST /api/v1/processes must be called.
As a body, we need the clientId and the clientSecret on the one hand, as well as clientReferences and a processId.

For more information have a look at our Process Controller Create Process Token API Documentation

Field

Type

Mandatory

clientId

The finAPI clientId of your client. This is required to authenticate the mandator and the client, and as well to create the users behind the process token.

yes

clientSecret

The finAPI clientSecret of your client. This is required to authenticate the mandator and the client, and as well to create the users behind the process token.

yes

clientReferences

The clientReferences array can be used to create multiple users with one call. For a simple user, we do need only one element with a clientReference element inside to have a connection to the current user in your system. If there is no reference, you can simply generate an UUID.

Multiple elements are only required, if you want to prepare for example an e-mail campaign. In this case you could create multiple links with one call.

See also Process Controller Create Batch Processes API Documentation if you are interested in this multi-process creation.

yes

clientReferences[].clientReference

The clientReference subelement is meant for having a relation to your users. This can be a contract number, user id or anything else. If you don’t have any link to a user, you can simply create a UUID for this field.

yes

clientReferences[].clientReferenceKey

clientReferenceKey allows to differentiate the clientReference for several activities (e.g. campaign id). This is primarily required for tracking or associating process tokens with campaigns.

no

processId

This ID was created to have some sepratation between the processes.
For a simple user without any Process Controller Solutions Flow behind, this value should always be USER_ONLY.

For other use-cases please have a look at the API Documentation.

yes

First, we’ll create a component ProcessCtlAuthorization with a function to create the process.
In this example, we inject the URL of the Process Controller, the clientId, and clientSecret, so that this is not hard coded.

The first method will be the creation of the request object. The models, which are used here, can be found in the Bitbucket repository or can be generated out of the API.

KOTLIN
@Component
class ProcessCtlAuthorization(
    @Value("\${finapi.instances.processctl.url}") private val processCtlUrl: String,
    @Value("\${finapi.security.credentials.clientId}") private val clientId: String,
    @Value("\${finapi.security.credentials.clientSecret}") private val clientSecret: String,
    private val objectMapper: ObjectMapper
) {
    /**
     * This method creates the request object for the Create Process Token endpoint.
     * We are using a data object to create the structure,
     * because it is much easier to maintain than a pure string representation.
     *
     * In the end, we map it to a JSON string.
     */
    private fun createProcessTokenModel(
        clientReference: String,
        clientReferenceKey: String? = null
    ): String {
        // create clientReferences object
        val clientReferences = CreateProcessTokenClientReferences(
            clientReference = clientReference,
            clientReferenceKey = clientReferenceKey
        )

        // create request body
        val createProcessTokenModel = CreateProcessTokenRequestModel(
            clientId = clientId,
            clientSecret = clientSecret,
            clientReferences = listOf(clientReferences),
            processId = ProcessId.USER_ONLY
        )

        // map the object to a JSON String representation
        return objectMapper.writeValueAsString(createProcessTokenModel)
    }
    
    companion object {
        private val log = KotlinLogging.logger {}
        private const val URI_CREATE_PROCESS_TOKEN = "/processes"
        private const val URI_GET_ACCESS_TOKEN = "/processes/"
    }
}

Now we have the body, let’s continue to send the request to the Process Controller.
To do that, we do need a HttpClient instance and an HttpRequest object.

The request should contain now the URL of the Process Controller and the path of the create token endpoint. In our case, we have already injected the URL of the Process Controller. This URL contains also the /api/v1 basepath.
After this request was sent, we have to check, that the result was 2xx and if this was the case, we want to map it back to an object, to access the data inside the JSON.

This mapped result will be returned then to the caller.

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.

KOTLIN
@Component
class ProcessCtlAuthorization(
    @Value("\${finapi.instances.processctl.url}") private val processCtlUrl: String,
    @Value("\${finapi.security.credentials.clientId}") private val clientId: String,
    @Value("\${finapi.security.credentials.clientSecret}") private val clientSecret: String,
    private val objectMapper: ObjectMapper
) {
    [...]
    
    /**
     * Calls the Create Process Token endpoint of the Process Controller.
     * 
     * After this was done, check, if the state was 2xx or else throw an exception.
     */
    @Throws(RuntimeException::class)
    private fun createProcessToken(clientRef: String, clientRefKey: String? = null): CreateProcessTokenResponse {
        // prepare the client
        val client = HttpClient.newBuilder().build()
        val request = HttpRequest.newBuilder()
            // build URL for https://<processctl>/api/v1/processes
            .uri(URI.create("${processCtlUrl}${URI_CREATE_PROCESS_TOKEN}"))
            // set Content-Type header to application/json
            .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
            // add the body
            .POST(
                HttpRequest.BodyPublishers.ofString(
                    createProcessTokenModel(
                        clientReference = clientRef,
                        clientReferenceKey = clientRefKey
                    )
                )
            ).build()

        // send the request and fetch the response
        val response = client.send(request, HttpResponse.BodyHandlers.ofString())

        // check for status code is 2xx or log and throw an exception
        StatusCodeCheckUtils.checkStatusCodeAndLogErrorMessage(
            response = response,
            errorMessage = "Unable to create a user."
        )

        // return the object of the mapped result
        return objectMapper.readValue(response.body(), CreateProcessTokenResponse::class.java)
    }
}

After this step was done, we should have a process token.

Step 2 - Exchange the Process Token into an access_token

The process token must now be converted into a real access_token, to be able to call restricted endpoints.

To achieve this, we will now create a function, which is calling the GET /processes/{processToken} endpoint of the Process Controller.
On this endpoint, we can invalidate the process token, if we don’t want to get another access_token for this user.

The query parameter ?invalidate=true invalidates the process token after getting the access_token. In this case no new access_token can be obtained.

This ensures, that nobody else can access the data, which was maybe imported with this token and deletes all user data after a day.

This function should return only the access_token as a String if everything is ok.

Else, we do also throw an Exception here.

KOTLIN
@Component
class ProcessCtlAuthorization(
    @Value("\${finapi.instances.processctl.url}") private val processCtlUrl: String,
    @Value("\${finapi.security.credentials.clientId}") private val clientId: String,
    @Value("\${finapi.security.credentials.clientSecret}") private val clientSecret: String,
    private val objectMapper: ObjectMapper
) {
    [...]
    
    /**
     * Calls the Get Access Token from Process Token endpoint of the Process Controller.
     *
     * After this was done, check, if the state was 2xx or else throw an exception.
     */
    @Throws(RuntimeException::class)
    private fun exchangeProcessTokenToAccessToken(processToken: String): String {
        // prepare the client
        val client = HttpClient.newBuilder().build()
        val request = HttpRequest.newBuilder()
            // build URL for https://<processctl>/api/v1/processes/{processId}
            // we do use also ?invalidate=true to make the process token no longer available to others
            .uri(URI.create("${processCtlUrl}${URI_GET_ACCESS_TOKEN}${processToken}?invalidate=true"))
            // set Content-Type header to application/json
            .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
            .GET()
            .build()

        // send the request and fetch the response
        val response = client.send(request, HttpResponse.BodyHandlers.ofString())

        // check for status code is 2xx or log and throw an exception
        StatusCodeCheckUtils.checkStatusCodeAndLogErrorMessage(
            response = response,
            errorMessage = "Unable to exchange process token."
        )

        // map the result to object and return access token
        val resultObj = objectMapper.readValue(response.body(), GetAccessTokenByProcessTokenModel::class.java)
        return resultObj.accessToken
    }
}

After this is now also done, we could write the public function, which should bring both endpoints together.

In this function, we call our createProcessToken() function with a client reference as UUID, because we only want to have the user without any relation in our system.
If you wish to have this relation, you can give the clientReference also to the obtainAccessToken() function.

The result, in this case, could only be an object with one process. So we finally call our exchangeProcessTokenToAccessToken() function, to get the result.

This result is then returned and can be used by the caller function as a Bearer token in the Authorization header.

KOTLIN
@Component
class ProcessCtlAuthorization(
    @Value("\${finapi.instances.processctl.url}") private val processCtlUrl: String,
    @Value("\${finapi.security.credentials.clientId}") private val clientId: String,
    @Value("\${finapi.security.credentials.clientSecret}") private val clientSecret: String,
    private val objectMapper: ObjectMapper
) {
    [...]
    /**
     * Obtain an access_token by creating first a process token and exchange it.
     */
    @Throws(RuntimeException::class)
    fun obtainAccessToken(): String {
        val createProcessResult = createProcessToken(
            clientRef = UUID.randomUUID().toString(),
        )
        
        return exchangeProcessTokenToAccessToken(createProcessResult.processes[0].processToken)
    }
}

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.