Authenticating with ITokenProvider/MSAL and Refit in ASP.NET Core

In my Blazor app, I need to be able to send HTTP requests to my API service and I use Refit for this. That way I can use Refitter to generate my API class and save a lot of time, and I can also use the same class to make calls no matter if I am on Server or WebAssembly (very useful if you are using Auto render mode!)

First, create an AuthBearerTokenFactory:

public static class AuthBearerTokenFactory
{
    private static Func<HttpRequestMessage, CancellationToken, Task<string>>? _getBearerTokenAsyncFunc;

    /// <summary>
    /// Provide a delegate that returns a bearer token to use for authorization
    /// </summary>
    public static void SetBearerTokenGetterFunc(Func<HttpRequestMessage, CancellationToken, Task<string>> getBearerTokenAsyncFunc)
      => _getBearerTokenAsyncFunc = getBearerTokenAsyncFunc;

    public static Task<string> GetBearerTokenAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (_getBearerTokenAsyncFunc is null)
            throw new InvalidOperationException("Must set Bearer Token Func before using it!");
        return _getBearerTokenAsyncFunc!(request, cancellationToken);
    }
}

Then register the SetBearerTokenGetterFunc in your program after app = builder.Build()

AuthBearerTokenFactory.SetBearerTokenGetterFunc(async (request, cancellationToken) =>
{
    var httpContextAccessor = app.Services.GetRequiredService<IHttpContextAccessor>();
    var configuration = app.Services.GetRequiredService<IConfiguration>();
    var scopedServices = httpContextAccessor.HttpContext?.RequestServices ?? throw new Exception("HttpContext not set!");
    var tokenAcquisition = scopedServices.GetRequiredService<ITokenAcquisition>();
    var scopes = configuration.GetSection("SCOPES_HERE").Get<IEnumerable<string>>() ?? throw new Exception("Scopes are not set in app config!");
    var httpContext = httpContextAccessor.HttpContext ?? throw new Exception("No HttpContext available!");
    return await tokenAcquisition.GetAccessTokenForUserAsync(scopes, user: httpContext.User);
});

Then when registering Refit, set the auth header getter to AuthBearerTokenFactory.GetBearerTokenAsync

And now you should be able to authenticate!

Note: I have had some issues with Hot Reload causing the HTTP context to not be set, but this only seems to occur when developing, and only sometimes, so I don’t consider it a big deal.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *