Sist endret: 20. des. 2024

Bruke meldingstjenesten med Maskinporten-autorisasjon

forrige side gikk vi gjennom hvordan man setter opp en ressurs og versjonskravene for meldingsklienten.

Vi kan nå gå videre til oppsett av Maskinporten og applikasjonskode.

Maskinporten

For å bruke meldingstjenesten behøver man en Maskinporten-klient med tilgang til følgende scopes:

  • altinn:serviceowner
  • altinn:correspondence.read
  • altinn:correspondence.write

For å sette opp dette kan du følge de generelle stegene i veiledningen for Maskinporten-integrasjons med noen modifikasjoner beskrevet nedenfor.

  • Meldingsklienten bruker en ny, intern klient for å kommunisere med Maskinporten. Derfor blir konfigurasjonsobjektet seende slik ut:

    App/appsettings.json
    "MaskinportenSettings": {
        "authority": "https://[test.]maskinporten.no/",
        "clientId": "the client id",
        "jwkBase64": "base64 encoded jwk"
    }
    
  • Meldingsklienten finner og bruker automatisk Maskinporten-klienten, og forsøker å binde seg til standard konfigurasjonssti MaskinportenSettings.

  • Hvis du trenger en annen konfigurasjonssti, kan du konfigurere den med hjelp av ConfigureMaskinportenClient:

    App/Program.cs
    void RegisterCustomAppServices(
       IServiceCollection services,
       IConfiguration config,
       IWebHostEnvironment env
    )
    {
        services.ConfigureMaskinportenClient(
            "DinUnikeMaskinportenSettingsSti"
        );
    }
  • Hvis du trenger et tilpasset konfigurasjonsoppsett, kan du bruke en delegatmetode:

    App/Program.cs
    void RegisterCustomAppServices(
       IServiceCollection services,
       IConfiguration config,
       IWebHostEnvironment env
    )
    {
        services.ConfigureMaskinportenClient(config =>
        {
            config.Authority = "https://[test.]maskinporten.no/";
            config.ClientId = "klient-id";
            config.JwkBase64 = "base64-kodet jwk";
        });
    }

Applikasjonskode

Ved å bruke avhengighetsinjeksjon i .NET, kan du registrere at tjenesten din trenger en ICorrespondenceClient. Denne klienten kan deretter brukes til å sende meldinger og vil automatisk håndtere Maskinporten-autorisering.

Når du skal sende en melding, finnes det veldig mange tilgjengelige parametere. Selv om bare et fåtall er nødvendige, kan selve prosessen med å bygge forespørselen være litt overveldende. For å hjelpe med dette, er det et CorrespondenceRequestBuilder-grensesnitt tilgjengelig.

Eksempelet nedenfor bruker dette grensenittet til å bygge en meldingsforespørsel med de vanligste alternativene: selve meldingen, en varsling til mottakeren, og et vedlegg.

Du finner alle tilgjengelige alternativer og tilhørende dokumentasjon via IntelliSense i din foretrukne kodeeditor.

Tjenesteregistrering

App/Program.cs
// ...

void RegisterCustomAppServices(
    IServiceCollection services,
    IConfiguration config,
    IWebHostEnvironment env
)
{
    services.AddTransient<ITheInterfaceYouAreImplementing, CorrespondenceClientDemo>();
}

Implementering av korrespondanseklient

App/CorrespondenceClientDemo.cs
using System;
using System.Threading.Tasks;
using Altinn.App.Core.Features.Correspondence;
using Altinn.App.Core.Features.Correspondence.Builder;
using Altinn.App.Core.Features.Correspondence.Models;

namespace Altinn.App;

internal sealed class CorrespondenceClientDemo(
    ICorrespondenceClient correspondenceClient
) : ITheInterfaceYouAreImplementing
{
  public async Task<SendCorrespondenceResponse> SendMessage()
  {
    CorrespondenceAuthorisation authorisation = CorrespondenceAuthorisation.Maskinporten;
    CorrespondenceRequest request = CorrespondenceRequestBuilder
      .Create()
      .WithResourceId("A valid resource registry identifier")
      .WithSender("Sender's organisation number")
      .WithSendersReference("Sender's arbitrary reference for the correspondence")
      .WithRecipient("Recipient's organisation number")
      .WithAllowSystemDeleteAfter(DateTime.Now.AddYears(1))
      .WithContent(
        language: "en",
        title: "Hello from .NET 👋🏻",
        summary: "The message summary",
        body: "The message body with markdown support"
      )
      .WithNotification(
        CorrespondenceNotificationBuilder
          .Create()
          .WithNotificationTemplate(CorrespondenceNotificationTemplate.CustomMessage)
          .WithEmailSubject("New Altinn message")
          .WithEmailBody(
            "You have a new message in your Altinn inbox, log in to see what's new."
          )
          .WithSmsBody("Got 📨 in Altinn")
          .WithNotificationChannel(CorrespondenceNotificationChannel.EmailPreferred)
      )
      .WithAttachment(
        CorrespondenceAttachmentBuilder
          .Create()
          .WithFilename("attachment.txt")
          .WithName("The attachment 📎")
          .WithSendersReference("Sender's arbitrary reference for the attachment")
          .WithDataType("text/plain")
          .WithData("This is the attachment content"u8.ToArray())
      )
      .Build();

    return await correspondenceClient.Send(
      new SendCorrespondencePayload(request, authorisation)
    );
  }

  public async Task<GetCorrespondenceStatusResponse> GetMessageStatus(Guid correspondenceId)
  {
    return await correspondenceClient.GetStatus(
      new GetCorrespondenceStatusPayload(
        correspondenceId,
        CorrespondenceAuthorisation.Maskinporten
      )
    );
  }
}

Notater om autorisering

I eksempelet ovenfor bruker vi enum-verdien CorrespondenceAuthorisation.Maskinporten for å indikere at autorisering automatisk skal håndteres internt med Maskinporten. Dette er den enkleste og mest praktiske metoden for autorisasjon, men det er ikke den eneste tilgjengelige.

Hvis du trenger tilpasset autoriseringslogikk i forbindelse med sending av meldinger, kan du bruke din egen delegat til dette formålet.

Et eksempel på dette kan være hvis du av diverse årsaker foretrekker å bruke den eksterne Maskinporten-klienten.

Både SendCorrespondencePayload og GetCorrespondenceStatusPayload aksepterer et delegatparameter. Implementasjonen kan se slik ut:

// ...

new SendCorrespondencePayload(
  request,
  async () =>
  {
    TokenResponse maskinportenResponse = await maskinportenService.GetToken(
      "base64 encoded jwk",
      "test|prod",
      "the client id",
      "altinn:serviceowner altinn:correspondence.write",
      null
    );

    TokenResponse altinnResponse = await maskinportenService.ExchangeToAltinnToken(
      maskinportenResponse,
      "test|prod"
    );

    return JwtToken.Parse(altinnResponse.AccessToken);
  }
);