Authenticating with mTLS


Calls from your backend to Lean are required to be made over Mutual Transport Layer Security (mTLS). mTLS is a certificate based authentication protocol that replaces oAuth public / private keys. Instead of sending a private key, you send your certificate with your calls to the Lean servers to authenticate.

If you'd like to learn more about what mTLS does and how it works you can learn more here.

Getting your certificates

When you create an application at you will be able to download two files from the authentication section of the developer portal. By clicking 'generate new certificate' a zip folder containing my_app_private_key.pem file and a my_app_cert.crt file will be downloaded.

Within the UI on you should also download the Lean certificate chain. To do this click the button labelled 'Certificate chain'. This should trigger a further download of a zipped folder containing lean_public_cert_chain.pem.


Protect your Lean Private Key

The private key must be managed securely during its entire lifecycle:

  • Store the private key in a secure vault or a key-management-system with role based access control, audit and authentication in place
  • Inform Lean in case of any key leakage so that we revoke your certificate
  • Do not share your credentials to anyone

With the above complete you should now have three files. You will find several code examples on how to use them further down on this page.

Testing mTLS in sandbox

In order to test mTLS in Sandbox, you will have to use a different base URL for the sandbox API:

Any call made to this base URL requires you to use mTLS, with your certificates from the sandbox section in the developer portal. Calls to the base URL do not require mTLS and can be access just using the lean-app-token as authentication method.

Example Implementations

// Node Example
const fs = require('fs')
const https = require('https')
const axios = require('axios')

const httpsAgent = new https.Agent({
  // This is your application certificate
  cert: fs.readFileSync('cert.crt'),
  // This is your private key associated with application certificate
  key: fs.readFileSync('key.pem'),
  // This is Lean's public certificate chain.
  ca: fs.readFileSync('ca.pem'),

const start = async () => {
  try {
    const request = await axios({
      method: 'post',
      headers: {
        'lean-app-token': 'LEAN_APP_TOKEN',
      // You can change the end point per your need. This endpoint is good for
      // testing mTLS
      url: '',
      withCredentials: true,
      jar: true,
  } catch (error) {

require 'openssl'
require 'net/http'

options = {
  use_ssl: true,
  verify_mode: OpenSSL::SSL::VERIFY_PEER,
  ca_file: 'ca.pem'

uri = URI("")
req =
req["lean-app-token"] = "<LEAN_APP_TOKEN>"

res = Net::HTTP.start(uri.hostname, uri.port, options) { |http|

puts res.body
import requests

getBanksUrl = ''

headers = {'lean-app-token': '<LEAN_APP_TOKEN>', 'Content-Type': 'application/json'}

result = requests.get(
    cert=('cert.crt', 'key.pem'), # Use the path to your own certificate and private key here

if (result.status_code == 200) :
    print (result.json())
else :
    print (f"Error - {result.json()['status']}: {result.json()['message']}")
package main

import (

var (
	// cert file MUST be a concatenation of certificate provided by Lean AND the certificate chain!!!
	certFile = flag.String("cert", "cert.crt", "Your application certificate provided by Lean concatenated with cert chain")
	keyFile  = flag.String("key", "key.pem", "Your private key attached to application certificate")
	caFile   = flag.String("CA", "ca.pem", "Lean's public certificate chain")

func main() {

	// Load client cert
	cert, err := tls.LoadX509KeyPair(*certFile, *keyFile)
	if err != nil {

	// Load CA cert
	caCert, err := ioutil.ReadFile(*caFile)
	if err != nil {
	caCertPool := x509.NewCertPool()

	// Setup HTTPS client
	tlsConfig := &tls.Config{
		Certificates: []tls.Certificate{cert},
		RootCAs:      caCertPool,
	transport := &http.Transport{TLSClientConfig: tlsConfig}
	client := &http.Client{Transport: transport}

	// Please change the end point and app token as required.
	req, err := http.NewRequest("GET", "", nil)
	req.Header.Set("lean-app-token", "<LEAN_APP_TOKEN>")
	if err != nil {

	res, err := client.Do(req)
	if err != nil {
	contents, err := ioutil.ReadAll(res.Body)


Java, Dotnet & C#

Please find the guide for C# and .net on our Github:

For an example Java implementation see our Github sample project here

Authenticating with OAuth


OAuth is currently in early access and is opt-in. If you would like to be considered for use of OAuth instead of mTLS please reach out to your account manager.

OAuth is a standard authentication method used to secure APIs and other infrastructure using Public and Private secrets combined with short lived access tokens. Lean's implementation of OAuth is standardised to the specification, so if you have worked with other OAuth providers such as Google, Auth0 or any other number of identity providers you may be familiar with the mechanisms and terms for obtaining and utilising and access token with our APIs.

In order to get started with OAuth authentication you will need the following:

  • An Application Dashboard account with Admin or Developer role access.
  • Your Application ID and Client Secret (found under the 'Integration' tab in the Application Dashboard).
  • An application with access to OAuth as an authentication method.

Scopes & The OAuth flow

OAuth is implemented to secure two channels of access to Lean. Access from your backend to Lean's APIs with scope=api, and access for your customers to the LinkSDK with scope=customer.<customer_id>.

In both cases the flow for creating, editing or modifying resources on the Lean platform is the same.

  1. Generate an access token for the request, this will return a JSON Web Bearer Token (JWT)
  2. Use the JWT as a Bearer token in subsequent API calls, or as an authentication for the LinkSDK method call you want to make.

Generating Access Tokens for Backend API Calls

curl -X POST '' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=<LEAN_APPLICATION_ID>' \
--data-urlencode 'client_secret=<LEAN_CLIENT_SECRET>' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'scope=api'

Generating Access Tokens for SDK Calls

curl -X POST '' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=<LEAN_APPLICATION_ID>' \
--data-urlencode 'client_secret=<LEAN_CLIENT_SECRET>' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'scope=customer.<customer_id>'


In sandbox the url is

client_idYour application ID - this can be retrieved from your Application Dashboard account.String (UUID)
client_secretYour client secret - this can be retrieved once from your Application Dashboard - subsequent retrievals will invalidate your existing client secret.String
grant_typeThe type of access you require a token for - currently this should always be set to client_credentialsclient_credentials
scopeWhat the scope of the access token should be, either api or customer.<customer_id>api or customer.<customer_id>


	"access_token": "YOUR_ENCODED_JWT",
	"token_type": "bearer",
	"expires_in": 3599,
	"scope": "api",
access_tokenThe access token value for use with the Lean APIsString
token_typeWill always be bearer, indicates the type of token returnedString
expires_inThe time in seconds until the access token expiresInteger
scopeThe scope of the access token and what resources it can accessString

Efficient Access Management

For your API scoped Access Token, you may want to store and refresh this as a regular job in your platform, rather than regenerating whenever it's required.

Making an OAuth call to a Lean API

Once you have an access token, this should be included in your API calls. We have used an example of creating a Customer in the sample below.

curl -X POST '' \
--header 'Content-Type: application/json'
--header 'Authorization: Bearer YOUR_JWT'
--data-raw '{
  "app_user_id": "001"

Using OAuth with the LinkSDK

When starting a new session in the LinkSDK a Customer JWT also needs to be generated in order to validate the session and ensure a secure connection with your customer in your application and Lean.

SDK method calls without OAuth will fail if OAuth is enabled in your application.

  app_token: "YOUR_APP_TOKEN",
  access_token: "YOUR_JWT",
  customer_id: "CUSTOMER_ID",
  permissions: ["accounts, payments"],
  bank_identifier: "ENBD_UAE"

What’s Next