Server application
Server applications are able to authenticate with the Unimicro APIs through a certificate based client credential flow.
They may act on behalf of companies they've been given access to without human interaction.
This article shows how to authenticate as a C# .NET server client.
Create a client token
In order to create a client token we start by loading our certificate. We then use this certificate to sign a JWT access token with our Client ID and the identity server's token endpoint.
// Audience should be set to the identity server's token endpoint, e.g. https://test-login.unimicro.no/connect/token for our test environment
private static string CreateClientToken(string clientId, string audience)
{
var certificate = new X509Certificate2(@"path\to\Cert.p12", "certificate password");
var now = DateTime.UtcNow;
var securityKey = new X509SecurityKey(certificate);
var signingCredentials = new SigningCredentials(
securityKey,
SecurityAlgorithms.RsaSha256
);
var token = new JwtSecurityToken(
clientId,
audience,
new List<Claim>()
{
new Claim("jti", Guid.NewGuid().ToString()),
new Claim(JwtClaimTypes.Subject, clientId),
new Claim(JwtClaimTypes.IssuedAt, now.ToEpochTime().ToString(), ClaimValueTypes.Integer64)
},
now,
now.AddMinutes(1),
signingCredentials
);
var tokenHandler = new JwtSecurityTokenHandler();
return tokenHandler.WriteToken(token);
}
The resulting token should contain the following fields in it's payload.
{
"jti": <guid>,
"sub": <clientid>,
"iat": 1592557940,
"nbf": 1592557940,
"exp": 1592558000,
"iss": <clientid>,
"aud": "https://test-login.unimicro.no/connect/token"
}
Request access token
Once you have your client token you can use that to request an access token.
static async Task<TokenResponse> RequestTokenAsync()
{
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync("https://test-login.unimicro.no");
if (disco.IsError) throw new Exception(disco.Error);
var clientToken = CreateClientToken("client-id", disco.TokenEndpoint);
var response = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
Address = disco.TokenEndpoint,
Scope = "Scope1 scope2",
ClientAssertion =
{
Type = OidcConstants.ClientAssertionTypes.JwtBearer,
Value = clientToken
}
});
if (response.IsError) throw new Exception(response.Error);
return response;
}
The request to connect/token that was generated should look something like this.
POST https://test-login.unimicro.no/connect/token
Body (application/x-www-form-urlencoded)
{
"client_id": "<clientId>",
"scope": "AppFramework",
"grant_type": "client_credentials",
"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
"client_assertion": "<local generated jwt-token>"
}
Send a request to the API
Once you have your access token you can use it to communicate with the Unimicro APIs. Let's fetch the CompanySettings entity.
static async Task CallServiceAsync(string token)
{
var baseAddress = "https://test.unimicro.no/";
var client = new HttpClient
{
BaseAddress = new Uri(baseAddress)
};
client.SetBearerToken(token);
try
{
var response = client.GetStringAsync("https://test.unimicro.no/api/init/companies").Result;
Console.WriteLine("Result: " + JArray.Parse(response));
var companies = JsonConvert.DeserializeObject<List<Company>>(response);
client.DefaultRequestHeaders.Add("CompanyKey", companies[0].Key);
var companyResponse = client.GetStringAsync("https://test.unimicro.no/api/biz/companysettings").Result;
Console.WriteLine("CompanySettings: " + JArray.Parse(companyResponse));
}
catch (Exception ex)
{
Console.WriteLine("Exception: " + ex.Message + " \n stack " + ex.StackTrace);
}
}
References
The following references has been used in these examples
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Net.Http;
using System.Security.Claims;
using System.Security.Cryptography.X509Certificates;
using Newtonsoft.Json.Linq;
using System.Threading.Tasks;
using IdentityModel;
using IdentityModel.Client;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using NodaTime;