Guide API documentation Community Applications
Introduction
Authentication
Overview Server application Web Auth SPA, mobile and native Using Postman with Unimicro API
Using the API
Guides
Payroll
Legal

Unimicro Web Auth

Introduction

In this article you will see how we can set up an integration against Unimicro that will work for multiple platforms.

Prerequisites

  • A Unimicro account
  • Access to a company in Unimicro
  • Client ID
  • Client Secret

Create your app first.

Creating a new integration
While we are very proud of our new developer portal, there are still some building blocks left at the old site. To be able to complete this tutorial you need to create an app, in order to get the Client ID, Client Secret and register redirect URLs. In a transition period, you need to contact our integration team that will help you set this up. Go do that now, we will wait right here!

If you already have an app, you can manage it from Applications.

Tools

In the coding examples we'll be using C# in .net core 3.1. All dependencies will be listed in Appendix A and B.

Notes

In this article <client_id> and <client_secret> represents the client id and client secret that belongs to your client.

Getting an access token

In this section we will show how to retrieve an access token.

Step 1

Send the user to:

https://test-login.unimicro.no/connect/authorize?client_id=<client-id>&redirect_uri=<redirect-uri>&response_type=code&prompt=login&scope=AppFramework profile openid offline_access&state='<optional-state>'

The client-id is the client Id you got for your app, the redirect-url is one of the uri you have registered, and the scopes is should be "AppFramework profile openid offline_access".

The user will now login, chose a platform and a company, and then are redirected back to you with a code that you will need to be able to get the access token. The redirect_uri must be registered on the client, so if you have dynamic redirect internally after authentication, you can specify this in the state parameter. This will return with the same value, after a successful authentication.

Step 2

Use the code that you got back in step 2 and create a POST request against our identity provider.

POST https://test-login.unimicro.no/connect/token

Headers:
"Content-Type":"application/x-www-form-urlencoded"

Body:
"grant_type": "authorization_code",
"code": CODE_FROM_REDIRECT,
"redirect_uri": https://integration-partner/post-login,
"client_id": <client_id>,
"client_secret": <secret>

Example in C#

var httpClient = new HttpClient();

var pairs = new List<KeyValuePair<string, string>>
{
    new KeyValuePair<string, string>("client_id", "<client_id>"),
    new KeyValuePair<string, string>("code", code),
    new KeyValuePair<string, string>("grant_type", "authorization_code"),
    new KeyValuePair<string, string>("client_secret", "<client_id>"),
    new KeyValuePair<string, string>("redirect_uri", "https://integration-partner/post-login")
};

var content = new FormUrlEncodedContent(pairs);
var result = await httpClient.PostAsync(new Uri("https://test-login.unimicro.no/connect/token"), content)

You'll then get a JSON response that looks like this:

{
	"id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6Im...",
	"access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImVhMD...",
	"expires_in": 3600,
	"token_type": "Bearer",
	"refresh_token": "8942647edb873533c35a..."
}

Step 2b - Use refresh token to get a new access token

The process is similar to the above. The difference is that we will change the grant_type to "refresh_token" and replaces code with refresh_token.

var pairs = new List<KeyValuePair<string, string>>
{
    new KeyValuePair<string, string>("client_id", "<client_id>"),
    new KeyValuePair<string, string>("refresh_token", identityResponse.refresh_token),
    new KeyValuePair<string, string>("grant_type", "refresh_token"),
    new KeyValuePair<string, string>("client_secret", "<client_id>"),
    new KeyValuePair<string, string>("redirect_uri", "https://integration-partner/post-login")
};

Step 3 - Talk with the Unimicro API

Now you have an access token and are ready to contact the API. If you have a single tenant client and the user has selected a company you do not need to specify a CompanyKey in the headers, but if you have a multi-tenant client, you must specify the CompanyKey in the headers.

The first thing to do is to obtain the URL for our API from the token. The URL we are looking for is stored on a claim in the token called AppFramework

var jwtSecurityToken = new JwtSecurityToken(accessToken);
jwtSecurityToken.Payload.TryGetValue("AppFramework", out var baseUrl);

It could be smart to wrap this in a try-catch.

Single tenant request:

httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

var apiResult = await httpClient.GetAsync(new Uri(baseUrl + "api/biz/user?action=current-session"));

Multiple tenant request:

httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
httpClient.DefaultRequestHeaders.Add("CompanyKey", "015eb513-753a-4942-9f6a-8ba930e33dc6");

var apiResult = await httpClient.GetAsync(new Uri(baseUrl + "api/biz/user?action=current-session"));

Headers for multi tenant should include:

Content-Type: application/json
Authorization: Bearer [JWT]
CompanyKey: [CompanyKey-from step 3b]

In both cases your response should look something like:

{
    "Permissions": [],
    "License": {},
    "DisplayName": "My Name",
    "Email": "dev@unimicro.no",
    "GlobalIdentity": "ee5a6e9f-9ca8-4c97-9bf0-6563633d594f",
    "PhoneNumber": "",
    "LastLogin": "2019-02-12T12:15:23.85Z",
    "UserName": "Username",
    "Protected": false,
    "BankIntegrationUserName": null,
    "IsAutobankAdmin": false,
    "StatusCode": null,
    "CustomValues": null,
    "ID": 1,
    "Deleted": false,
    "CreatedAt": null,
    "UpdatedAt": null,
    "CreatedBy": null,
    "UpdatedBy": null
}

Step 3b - Multi tenant company key

To get the companies that is available for the current session, you'll make a request against http://test.unimicro.no/api/init/companies with the Authorization header set with Bearer [JWT]. This will provide you with a return that looks something like:

{
    "Name":"Company Inc.",
    "Key":"015eb513-753a-4942-9f6a-8ba930e33dc6",
    "WebHookSubscriberId":null,
    "IsTest":false,
    "FileFlowEmail":null,
    "ID":5,
    "Deleted":false,
    "CreatedAt":"2017-02-01T15:55:43.46Z",
    "UpdatedAt":null,
    "CreatedBy":null,
    "UpdatedBy":null
}

You will then use the Key from the response as CompanyKey for the company you wish to make the request against. You can switch which company you make requests against, by switching the CompanyKey header.

Appendices

Appendix A - NuGet packages

Microsoft.AspNetCore.App Microsoft.NETCore.App Newtonsoft.Json System.IdentityModel.Tokens.Jwt

Appendix B - Usings

using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;

Appendix C - Code example

using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;

namespace MyApp.Controller
{
 Route("api/[controller]")]
    [Produces("application/json")]
    [ApiController]
    public class TokensController : ControllerBase
    {

        private readonly Client Client = new Client
        {
            clientId = "<client_id>",
            clientSecret = "<client_secret>"
        };

        [HttpGet]
        public async Task<IActionResult> GetToken([FromQuery(Name = "code")] string code = null)
        {
            if (code == null)
            {
                return Ok("No code");
            }

            var httpClient = new HttpClient();

            var pairs = new List<KeyValuePair<string, string>>
            {
                new KeyValuePair<string, string>("client_id", Client.clientId),
                new KeyValuePair<string, string>("code", code),
                new KeyValuePair<string, string>("grant_type", "authorization_code"),
                new KeyValuePair<string, string>("client_secret", Client.clientSecret),
                new KeyValuePair<string, string>("redirect_uri", "https://yourdomain.no/redirects-here"),
            };

            var content = new FormUrlEncodedContent(pairs);
            var result = await httpClient.PostAsync(new Uri("https://test-login.softrig.com/connect/token"), content);
            var returnContent = await result.Content.ReadAsStringAsync();

            if (!result.IsSuccessStatusCode) return BadRequest(returnContent);

            var identityResponse = JsonConvert.DeserializeObject<IdentityResponse>(returnContent);

            return await GetDataFromCompany(identityResponse.access_token);
        }

        private async Task<IActionResult> GetDataFromCompany(string accessToken)
        {
            var httpClient = new HttpClient();
            httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);

            try
            {

                var jwtSecurityToken = new JwtSecurityToken(accessToken);
                if (jwtSecurityToken.Payload.TryGetValue("AppFramework", out var baseUrl))
                {
                    Console.WriteLine($"Base url is {baseUrl}");
                    var apiResult = await httpClient.GetAsync(new Uri(baseUrl + "api/biz/companysettings"));

                    return Ok(await apiResult.Content.ReadAsStringAsync());
                }
                else
                {
                    return BadRequest($"Could not find claim on token");
                }
            }
            catch (Exception ex)
            {
                return BadRequest(ex.Message);
            }

            return BadRequest("this is wrong");
        }

    }

    internal class IdentityResponse
    {
        public string id_token { get; set; }
        public string access_token { get; set; }
        public int expires_in { get; set; }
        public string token_type { get; set; }
        public string refresh_token { get; set; }
    }

    internal class Client
    {
        public string clientId { get; set; }
        public string clientSecret { get; set; }
    }
}