How to set up mTLS

Introduction

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 dev.leantech.me 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 dev.leantech.me 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.

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:

https://mtls.sandbox.leantech.me

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 https://sandbox.leantech.me base URL do not require mTLS and can be access just using the lean-app-token as authentication method.


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',
      },
      httpsAgent,
      // You can change the end point per your need. This endpoint is good for
      // testing mTLS
      url: 'https://api.leantech.me/customers/v1',
      withCredentials: true,
      jar: true,
    })
    console.log(request)
  } catch (error) {
    console.log(error)
  }
}

start()

Ruby example

require 'openssl'
require 'net/http'

options = {
  use_ssl: true,
  verify_mode: OpenSSL::SSL::VERIFY_PEER,
  cert: OpenSSL::X509::Certificate.new(File.read('cert.crt')),
  key: OpenSSL::PKey::RSA.new(File.read('key.pem')),
  ca_file: 'ca.pem'
}

uri = URI("https://api.leantech.me/banks/v1")
req = Net::HTTP::Get.new(uri)
req["lean-app-token"] = "<LEAN_APP_TOKEN>"

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

puts res.body

Python example

import requests

getBanksUrl = 'https://api.leantech.me/banks/v1/'

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

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

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

Go example

package main

import (
    "crypto/tls"
    "crypto/x509"
    "flag"
    "io/ioutil"
    "log"
    "net/http"
)

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() {
    flag.Parse()

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

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

    // Setup HTTPS client
    tlsConfig := &tls.Config{
        Certificates: []tls.Certificate{cert},
        RootCAs:      caCertPool,
    }
    tlsConfig.BuildNameToCertificate()
    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", "https://api.leantech.me/banks/v1", nil)
    req.Header.Set("lean-app-token", "<LEAN_APP_TOKEN>")
    if err != nil {
        log.Fatal(err)
    }

    res, err := client.Do(req)
    if err != nil {
        log.Fatal(err)
    }
    contents, err := ioutil.ReadAll(res.Body)
    log.Print(string(contents))

}

Dotnet / C# example

/*
  Please make sure the system running this code has lean root certificate installed in system or user store for more info please read this
*/

using System;
using System.Threading.Tasks;
using System.Net.Http;
using System.Security.Cryptography.X509Certificates;

namespace HTTP_Test
{
  class program
  {
    static void Main()
    {
      Task t = new Task(HTTP_GET);
      t.Start();
      Console.ReadLine();
    }


    static async void HTTP_GET()
    {
      // path of pfx file generate by 
      // $ openssl pkcs12 -export -out certificate.pfx -inkey privateKey.key -in certificate.crt -certfile CAcert 
      var certificateLocation = "";
      // password of pfx file
      var certificatePassword = "";
      // lean app token
      var leanAppToken = "";
      // create a new HttpClientHandler
      var handler = new HttpClientHandler();
      // create a new certficite using location and password
      var certificate = new X509Certificate2(certificateLocation, certificatePassword);
      // add certificate to client certificates
      handler.ClientCertificates.Add(certificate);
      // ignore server checks (Caution: You should implement a check to confirm server certificate)
      handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => { return true; };
      // create a new HttpClient using the handler and setting the default BaseAddress as https://api.leantech.me/
      var client = new HttpClient(handler)
      {
        BaseAddress = new Uri("https://api.leantech.me/")
      };
      // add lea app token default request header
      client.DefaultRequestHeaders.Add("lean-app-token", leanAppToken);

      // request the banks endpoint
      HttpResponseMessage response = await client.GetAsync("/banks/v1");

      // output to console status and JSON
      Console.WriteLine("Response StatusCode: " + (int)response.StatusCode);
      HttpContent content = response.Content;
      string result = await content.ReadAsStringAsync();
      Console.WriteLine(result);
    }
  }
}

Java example

Please find the example for Java on our Github: https://github.com/leantechnologies/integration-snippets/tree/master/mtls-examples/java