Last modified: Oct 22, 2024

Integrate Altinn app with Maskinporten

How to setup an integration between an Altinn App and Maskinporten.

This is a guide on how to set up an Altinn application to create a client that utilizes a Maskinporten integration for its requests. This is a use case that is relevant when the application are going to perform requests that needs to be authorized on behalf of the organization owning the application and not the end user owning the instance. By nature, these requests will have credentials from the private user who logged in to the application and created the new instance. In order to send these requests on behalf of the organization the following must be done;

  1. Ensure organization has access to Azure key vault
  2. Create the integration to Maskinporten at Samarbeidsportalen
  3. Store the keys from the integration in Azure key vault for the organisation
  4. Set up the application to use the Maskinporten client by retrieving the secrets/keys from Azure key vault.

Azure Key Vault Access

Before going forward in this guide, make sure you have access to Azure key vault for your organization, so the keys created further on in the guide can be added directly into the secrets in Azure.

If access is missing, please refer to Access to logs and secrets.

Maskinporten Integration

In this section the Maskinporten client will be set up. A part of setting up the client includes creating keys that should be stored in Azure Key vault later on in the guide. If different people in the organization have access to different resources needed in this process, please cooperate and do the following steps on the same machine. This is recommended to avoid sending secrets between machines.

When access to creating secrets in Azure key vault is confirmed, please proceed to create the integration.

Prerequisites

Register new integration through Samarbeidsportalen

  • Login to Samarbeidsportalen in Test or Production

  • Choose Selvbetjening and then Integrasjoner for the environment you want. Ver2 is test and Produksjon is production.

    “Samarbeidsportalen”
    Samarbeidsportalen

  • Choose Ny integrasjon

    “New integration”
    New integration

  • To fill out the form, provide all required properties:

    • Scopes: Choose Legge til scopes and include all the scopes necessary for the integration to generate tokens containing
    • Navn på integrasjonen: Add a descriptive name that allows you to identify the application that will be using the integration
    • Beskrivelse: Add a short description, not only for yourself but for everyone that administers integrations on behalf of your organization.

    “Add values for integration”
    Add values for integration

    The example above shows an integration used by an Altinn CLI Client which will need to generate tokens containing one or more of the three selected scopes; altinn:serviceowner, altinn:serviceowner/instances.read and altinn:serviceowner/instances.write

  • Choose Opprett in the top right corner when you have completed the configuration

The final steps of this guide cover creating a Json Web Key (JWK) for the integration to use to authenticate towards maskinporten, as well as noting down important values that can be used to configure the client that will integrate with Maskinporten.

Generate and register JWK for authentication towards Maskinporten

To avoid spreading the business certificate across many systems, we opt for creating an asymmetric key (JSON Web Key) and associate it to the newly created integration. In this example we use mkjwk.org.

  • Navigate to mkjwk.org in a browser

  • Fill in values like the example below and click Generate

    “New JWK”
    New JWK

    The output should look like this:

    “The JWK”
    The JWK

Now, the public part of the key should be added to the newly created integration in Samarbeidsportalen.

  • Navigate back to the integration in Samarbeidsportalen

  • Choose Egne public nøkler

    “Own public keys”
    Own public keys

  • Add two empty square bracets to the empty text box as shown below

    “Add array”
    Add array

  • Navigate back to the JWK generator site

    “The JWK”
    The JWK

  • Copy the public key of the JWK (marked 1 in the picture) and paste this into the array in Samarbeidsportalen.

    “Add public key”
    Add public key

  • Choose Legg til

The registration and configuration in Samarbeidsportalen is now complete, and the integration is ready to generate Maskinporten tokens on request from any client that can provide the private and public parts of the JWK.

Important values for client configuration

From samarbeidsportalen:

  • Integrasjonens identifikator

    This will be used in your client configuration. In Altinn libraries, this value is referred to as the client identifikator

From the JWK generation tool:

  • Public and private key pair (marked 2 in the picture below) This is what your client will use when calling the Maskinporten integration.

    “The JWK”
    The JWK

In Altinn libraries this key pair is referenced as EncodedJwk and must be base64 encoded before it is included in application configuration or uploaded to a Key Vault.

Base64encode.org can be used for encoding.

Azure Key Vault Configuration

When preparing the application to use the secrets from Azure Key vault, there are some steps that needs to be done:

  1. Add the secrets retrieved during the Maskinporten client configuration to Azure Key vault:

    • The base64 encoded JWT public and private key pair
    • The clientID for the integration

    It is important that the name of these secrets in Azure key vault corresponds with the name of the section in the appsettings file in the application repository. E.g. if your appsettings section for the Maskinporten integration section looks like this:

    {
      "MaskinportenSettings": {
        "Environment": "test",
        "ClientId": "",
        "Scope": "altinn:serviceowner/instances.read",
        "EncodedJwk": "",
        "ExhangeToAltinnToken": true,
        "EnableDebugLog": true
      }
    }
    

    The secrets in Azure key vault should have names like this:

    MaskinportenSettings--ClientId
    MaskinportenSettings--EncodedJwk
    
  2. For the application to be able to read the secrets from Azure key vault the application need to be configured to do so. See the secrets section to achieve this.

  3. Add the appsettings section example from above into the appsettings.{env}.json file.

NB: The secrets are read by the application on start up so if changing the secrets after the application is deployed, you will need to redeploy the application.

Setup Application to use Maskinporten Integration

When modifying the application to use the Maskinporten integration, we need to adapt the Program.cs file.

First of all we need to add the MaskinportenHttpClient service with the appropriate configuration in the function RegisterCustomAppServices:

services.AddMaskinportenHttpClient<SettingsJwkClientDefinition, AppClient>(config.GetSection("MaskinportenSettings"));

Then we need to add the Azure Key Vault configuration provider to our host. Start by adding these package references in the project file (App.csproj) - get the latest version from NuGet.org:

<PackageReference Include="Azure.Extensions.AspNetCore.Configuration.Secrets" Version="1.3.1" />
<PackageReference Include="Azure.Identity" Version="1.11.4" />

Then we can complete the configuration by adding the Azure Key Vault configuration provider:

using Azure.Identity;

// ...

void ConfigureWebHostBuilder(IWebHostBuilder builder)
{
    builder.ConfigureAppWebHost(args);
    builder.ConfigureAppConfiguration(
        (context, configuration) =>
        {
            var section = context.Configuration.GetSection("kvSetting");
            var keyVaultUri = section.GetValue<string>("SecretUri");
            var clientId = section.GetValue<string>("ClientId");
            var clientSecret = section.GetValue<string>("ClientSecret");
            var tenantId = section.GetValue<string>("TenantId");

            if (
                string.IsNullOrWhiteSpace(keyVaultUri)
                || string.IsNullOrWhiteSpace(clientId)
                || string.IsNullOrWhiteSpace(clientSecret)
                || string.IsNullOrWhiteSpace(tenantId)
            )
                return;

            configuration.AddAzureKeyVault(
                new Uri(keyVaultUri),
                new ClientSecretCredential(tenantId, clientId, clientSecret)
            );
        }
    );
}