2

Our project use asp.net core 5 web API, and authenticated with JWT token,

we want to protect our image file which is binary format saved in DB,

so serve the image with an action like the following C# code,

but the action can not be accessed in html, the console in browser throw an error:

Error in browser console:

1:1 GET http://localhost:9003/api/Files/DisplayInventItemImage/LE3367/1 401 (Unauthorized)

Html code

<img src = ' api/Files/DisplayInventItemImage/LR1059/2' />

C# code

[Route("api/[controller]")]
[ApiController]
public class FilesController : ControllerBase
{
    private readonly IDbService _dbService;
    private readonly ILoggerService _logger;
    private readonly InventTableService _inventTableService;
    private readonly IWebHostEnvironment _env;

    public FilesController(ILoggerService logger,
                                IDbService dbService,
                                InventTableService inventTableService,
                                IWebHostEnvironment env)
    {
        _dbService = dbService;
        _logger = logger;

        _inventTableService = inventTableService;
        _env = env;
    }

    [Authorize]
    [HttpGet("DisplayInventItemImage/{itemId}/{num}")]
    public async Task<IActionResult> DisplayInventItemImage(string itemId, int num)
    {
        try
        {
            var table = await _inventTableService.FindAsync(itemId);
            if (table == null)
                return NotFound();

            if (num == 1)
                return File(table?.Picture1, "image/jpeg");
            if (num == 2)
                return File(table?.Picture2, "image/jpeg");

            if (num == 3)
                return File(table?.Picture3, "image/jpeg");

            if (num == 4)
                return File(table?.Picture4, "image/jpeg");

            if (num == 5)
                return File(table?.Picture5, "image/jpeg");

            return Problem($"Incorrect num: {num}");

            // var filePath = Path.Combine(_env.ContentRootPath, Global.StaticFiles, "images", $"{itemId}.jpeg");
            // return PhysicalFile(filePath, "image/jpeg");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex);
            return Problem(ex.Message);
        }
    }
}
microax
  • 21
  • 2
  • You could store the JWT Authentication token in a cookie, then you won't need to do anything special when clicking on images and anchors as the authentication cookie would be sent with every request to the site's domain. – phuzi Jul 23 '21 at 07:52

2 Answers2

1

You cannot send extra headers when using HTML elements like <img> or <a>. So when you open up the network panel, you'll see the browser is requesting the resources with a simple GET request.

To solve this problem, you can pass the token as a query parameter, then promote it to a header inside a middleware that runs before the authentication middleware.

Append JWT token as token query parameter

<img src="api/img.jpg?token=eyJ..." />

Then register a middleware before the authentication middleware

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    // ...
    app.UseRouting();

    app.UseMiddleware<QueryParamTokenMiddleware>(); // must come before UseAuthentication
    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
}

This middleware will check for the token query parameter and set the Authorization header.

class QueryParamTokenMiddleware
{
    private readonly RequestDelegate _next;

    public QueryParamTokenMiddleware(RequestDelegate next) => _next = next;

    public Task InvokeAsync(HttpContext context)
    {
        if (context.Request.Query.TryGetValue("token", out var token))
        {
            context.Request.Headers[HeaderNames.Authorization] = $"Bearer {token}";
        }

        return _next(context);
    }
}

Then the authentication middleware will take over, and parse the token correctly, letting the request be dispatched to action with [Authorize] attribute.

One caveat of putting JWT in URL is that it will open up the possibility of getting the token stolen by a 3rd party monitoring the requests, and possibly masquerading as the user the token is issued for. If you're worried about token security, you can issue another token that contains only the claims required for downloading a protected asset, and embed that token in HTML to make sure it cannot be used for other purposes.

abdusco
  • 9,700
  • 2
  • 27
  • 44
0

As far as I know, if this api is not at the same project with your client project. When you access your client application, your browser will not pass the token with the request to the api by default.

You need to use javascript or jquery to use ajax to send the request with token to the api to get the image and then show it inside that image tag.

More details about how to send the request with the token by using ajax, you could refer to this answer.

More details about how to use ajax to get the image , you could refer to this answer.

Brando Zhang
  • 22,586
  • 6
  • 37
  • 65