Apps

Overview

When you build an application to be used on Narmi’s platform, you’re building
a web app which will be served inside an <iframe> container from your own
domain inside of digital banking. Since Narmi only serves apps to logged in
users, this means that you are guaranteed to have a logged in user available
for authentication.

All apps must be served over TLS (a https:// url). This ensures the security
of requests.

Understanding App Launch

When your app is loaded on Narmi’s platform, a HTTP POST request is made
to your specified app URL. This POST request will contain some parameters
in the body, including the signed_request parameter which you should
decode for user information, and a signature to verify the security and
authenticity of this data.

The signed_request is base64 encoded and signed with an SHA256 HMAC of your
App Secret (sometimes this is called a JSON Web Token). You can parse this
parameter like this:

  1. Split the signed request into three parts delineated by a ‘.’ character (eg. 34k324nsfsfm.238fsdfsd.oijdoifjsidf899).
  2. Calculate the expected signature of the request, and compare it to the third part - the encoded signature.
  3. If the expected signature and signature match, decode the second part - the payload - from base64 and then decode the resultant JSON object.

This JSON object has several keys:

Name Type Description Example
exp number expiration, a Unix epoch timestamp 1291840400
iat number issued at, a Unix epoch timestamp 1516239022
aud string client id of the application “7ugpYTwyIoFkhz6bLnzQJGYUEaJGtcnrv8pfOJCb”
sub string subject, the Narmi user id “0b0b893f-9885-4789-b26d-6e879f0fc693”
user.institution_user_identifier string identifier that the institution uses for the user “555555”

If exp is greater than the current time, then the request should be considered invalid.

These steps are possible in any modern programming languages. Here are some examples:

  1. PHP
  2. ASP.NET

PHP

function parse_signed_request($signed_request) {
  list($header, $payload, $encoded_sig) = explode('.', $signed_request, 3);

  $secret = "appsecret"; // Use your app secret here

  $sig = base64_url_decode($encoded_sig);

  // confirm the signature
  $expected_sig = hash_hmac('sha256', $header.".".$payload, $secret, $raw = true);
  if ($sig !== $expected_sig) {
    error_log('Bad Signed JSON signature!');
    return null;
  }

  // decode the data
  $data = json_decode(base64_url_decode($payload), true);
  return $data;
}

function base64_url_decode($input) {
  return base64_decode(strtr($input, '-_', '+/'));
}

Then to use it:

parse_signed_request($_REQUEST['signed_request'])

For example:

parse_signed_request("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjEyOTE4NDA0MDAsInN1YiI6IjBiMGI4OTNmLTk4ODUtNDc4OS1iMjZkLTZlODc5ZjBmYzY5MyIsInVzZXIiOnsiaW5zdGl0dXRpb25fdXNlcl9pZGVudGlmaWVyIjoiOTk2MjcifSwiaWF0IjoxNTE2MjM5MDIyfQ.SUxrDJW6Q7Uylefh6aEbodxRpeeJ8bHTIT1Hs-RrYMQ")

Will produce a JSON payload of:

{
  "exp": 1291840400,
  "sub": "0b0b893f-9885-4789-b26d-6e879f0fc693",
  "user": {
    "institution_user_identifier": "99627"
  },
  "iat": 1516239022
}

ASP.NET

This example uses the .NET class HMACSHA256.

static string ParseSignedRequest(string signedRequest)
{
    string[] split = signedRequest.Split('.');

    string header = split[0];
    string data = split[1];

    string dataRaw = FixBase64String(data);
    string signatureRaw = FixBase64String(split[2]);

    // the decoded signature
    byte[] signature = Convert.FromBase64String(signatureRaw);

    byte[] dataBuffer = Convert.FromBase64String(dataRaw);

    // JSON object
    string dataJson = Encoding.UTF8.GetString(dataBuffer);

    byte[] appSecretBytes = Encoding.UTF8.GetBytes("appsecret"); // Use your app secret here
    HMAC hmac = new HMACSHA256(appSecretBytes);
    byte[] expectedHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(header + "." + data));
    bool areEqual = expectedHash.SequenceEqual(signature);

    if (areEqual) {
        return dataJson;
    } else {
        return "";
    }
}


static string FixBase64String(string str)
{
    string result = str;

    while (result.Length % 4 != 0)
    {
        result = result.PadRight(result.Length + 1, '=');
    }

    result = result.Replace("-", "+").Replace("_", "/");

    return result;
}

For example:

using System;
using System.Text;
using System.Linq;
using System.Security.Cryptography;

public class Program
{
  public static void Main()
  {
    Console.WriteLine(ParseSignedRequest("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjEyOTE4NDA0MDAsInN1YiI6IjBiMGI4OTNmLTk4ODUtNDc4OS1iMjZkLTZlODc5ZjBmYzY5MyIsInVzZXIiOnsiaW5zdGl0dXRpb25fdXNlcl9pZGVudGlmaWVyIjoiOTk2MjcifSwiaWF0IjoxNTE2MjM5MDIyfQ.SUxrDJW6Q7Uylefh6aEbodxRpeeJ8bHTIT1Hs-RrYMQ"));
  }

  //...
}

Will produce a string that can be JSON parsed:

{"exp": 1291840400,"sub": "0b0b893f-9885-4789-b26d-6e879f0fc693","user": {"institution_user_identifier": "99627"},"iat": 1516239022}