FHIDev / fhi.helseid

Fhi.HelseId component for accessing NHN HelseId
MIT License
7 stars 5 forks source link

Bytte ut IdentityModel.AspNetCore med Microsoft-komponenter #450

Open martinmine opened 2 weeks ago

martinmine commented 2 weeks ago

Fhi.HelseId.Web-prosjektet i dag har en avhengighet til IdentityModel.AspNetCore. Vi ønsker å anvende Microsoft-biblioteker istedenfor for å begrense størrelsen til Fhi.HelseId og bruke pakker som er laget og støttet av Microsoft istedenfor en tredjepart. I dag bruker vi kun dette til å refreshe access tokens i forbindelse med BFF-patternet. Se klassene TokenEndpointService og AutomaticTokenManagementCookieEvents.

For å erstatte IdentityModel.Client.TokenClient kan vi vurdere å bruke ConfidentialClientApplication

ChatGpt ga meg følgende hint til å kunne refaktorere RefreshTokenAsync i TokenEndpointService:

var confidentialClient = ConfidentialClientApplicationBuilder.Create(oidcOptions2.ClientId)
    .WithAuthority(configuration.Authority) // The authority URL for your token endpoint
    .WithClientAssertion(clientAssertion)   // Use your JWT bearer token as the client assertion
    .WithHttpClientFactory(() => httpClient) // Optionally provide your own HttpClient instance
    .Build();

// Define the scope for the token request
var scopes = new[] { $"{configuration.ApiResourceId}/.default" }; // Adjust scope as necessary

try
{
    var authResult = await confidentialClient.AcquireTokenForClient(scopes)
        .ExecuteAsync();

    var accessToken = authResult.AccessToken;
}
catch (MsalServiceException ex)
{
    // Handle exception
    Console.WriteLine($"An error occurred: {ex.Message}");
}
martinmine commented 5 days ago

Det ser ikke ut dette er så lett til å få til med MSAL som først antatt. Har undersøkt litt og sett på hvordan auth tokens hentes ut behind the scenes i Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler (se funksjonene RedeemAuthorizationCodeAsync og RunAuthorizationCodeReceivedEventAsync), har kommet frem til følgende PoC uten å ha fått testet det ut enda:

public async Task RefreshTokenAsync(string token)
{
    var oidcOptions = await GetOidcOptionsAsync();
    var configuration = await oidcOptions.ConfigurationManager.GetConfigurationAsync(default);
    var clientAssertion = secretHandler?.GenerateClientAssertion;

    var tokenEndpointRequest = new OpenIdConnectMessage()
    {
        ClientId = oidcOptions.ClientId,
        GrantType = OpenIdConnectGrantTypes.RefreshToken,
        RefreshToken = token,
        ClientAssertion = clientAssertion,
        ClientAssertionType = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", // TODO: check if there is a const class for this somewhere
    };

    var requestMessage = new HttpRequestMessage(HttpMethod.Post, configuration.TokenEndpoint);
    requestMessage.Content = new FormUrlEncodedContent(tokenEndpointRequest.Parameters);

    var responseMessage = await httpClient.SendAsync(requestMessage);
    var responseContent = await responseMessage.Content.ReadAsStringAsync();
    var resultMessage = new OpenIdConnectMessage(responseContent);

    /*
    Available properties:
    resultMessage.AccessToken;
    resultMessage.RefreshToken;
    resultMessage.ExpiresIn;
    */
}