Search code examples
authenticationgetbearer-tokenblazor-webassembly

How to authenticate user on image src request from core hosted blazor web assembly


When calling the default API in a Blazor Core Hosted Webassembly application I use the preconfigured HttpClient which includes headers for the security token. This means on the server I have access to which user is logged in.

However, when loading an image into an img tag with the src property this doesn't use the HttpClient and so the header is not attached to the get request. This is fine if you want to serve a static, public, image from the server side (e.g. logo, brand image, icon etc.), but how can you authenticate this request such that users can only get their own images?

I have made an Images controller to handle the calls, which can either be a standard MVC controller or an API controller, and put an [Authorize] attribute on the GetImage/{id} method, but I don't know how to get the browser to authenticate (I get a 401, even through the user is logged into the application)

In an MVC site this would usually be handled by an auth cookie sent with requests from the browser, but Blazor doesn't set this cookie.

I could use the HttpClient to download the image, convert it into base64 and set the content of the img tag using that base64 string, but this bypasses the browser cache and seems needlessly complicated as I would then need to handle all these transfers and caching etc. manually in the client.

What is the best way to authenticate this browser triggered get request? Can I set a cookie by hand that will do this?


Solution

  • There is an auth Cookie set by the Identity Server in Blazor Core Hosted. However, by default this is not used to authenticate MVC requests using the [Authorize] Attribute.

    You can set the [Authorize] attribute to use the Identity Server auth cookie by adding the Identity.Application scheme to the AuthenticationSchemes on the decorator:

    [Authorize(AuthenticationSchemes = "Identity.Application")]

    Having done this the request will be authenticated.

    However, this method has meant getting the UserId slightly differntly in the MVC controller when compared to the Blazor AIP calls.

    In calls made from the HttpClient I get the UserId from the HttpContextAccessor

    var userId = _httpContextAccessor.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier)
    

    This returns null when the request comes through the MVC controller and so I get the UserId via the UserManager instead:

               var userName = _httpContextAccessor?.HttpContext?.User?.Identity?.Name;
               var user = await _userManager.FindByEmailAsync(userName);
               var userId = theUser.Id;
    

    This seems to work, but if there's a better way to do this I would be glad to hear it.