Junaid
Junaid

Reputation: 1030

Implement OAuth 2.0 based authentication on the Blazor Server Side

I have a .NET 8.0 Blazor web app:

- BlazorApp.Client
- Blazor.Server
- BlazorApp.Shared

The app uses JWT-based auth between client and server. I need to use an external third-party API to fetch some data which needs OAuth 2.0-based authentication. The code to fetch this data is in the Blazor.Server app.

0Auth 2.0 authorization_code flow of third-party API:

Then, add this access token to the Bearer <access_token> to every API call towards the third-party's endpoints in the Blazor.Server app. I am using an auth handler:

public class ThirdPartyApiAuthHandler : DelegatingHandler
{    
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        try
        {
            var accessToken = @"// harcoded access token //";

            if (string.IsNullOrEmpty(accessToken))
            {
                throw new UnauthorizedAccessException("User not authenticated with eBay.");
            }

            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
    
            return await base.SendAsync(request, cancellationToken);
        }
        catch (Exception ex)
        {
            throw new InvalidOperationException($"Error in Authentication Handler while sending request to {request.RequestUri}", ex);
        }
    }
}

I am a bit confused about implementing the OAuth flow in the above handler. How can I redirect the user to the external API's authentication URL in the handler? Is it right to handle this in the auth handler?

Upvotes: 0

Views: 41

Answers (1)

Hades
Hades

Reputation: 1985

Using a package called Microsoft.Extensions.ServiceDiscovery.Yarp you can configure a “Catch All” route in the Blazor Server application and point it to the external API. It will act as a reverse proxy for the API and you can have it automatically attach additional headers to the requests without the client needing to see them. For me, it looked like this:

app.MapForwarder("/api/{**catch-all}", apiBaseUrl, builder =>
{
    builder.AddXForwardedFor();
    builder.AddRequestTransform(async ctx =>
    {
        var originalPath = ctx.HttpContext.Request.Path.Value;
        
        if (originalPath!.StartsWith("/api"))
        {
            var newPath = originalPath["/api/".Length..]; // Removes "/api"
            ctx.ProxyRequest.RequestUri = new Uri($"{apiBaseUrl}{newPath}");
        }
        
        var accessToken = await ctx.HttpContext.GetTokenAsync("access_token");
        if (!string.IsNullOrEmpty(accessToken))
            ctx.ProxyRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
    });
});

This will add the X-Forwarded-For header to the request to ensure the API sees the end user’s real IP and attaches the Authorization header and access token to the request automatically if the user is logged in.

In this scenario, you handle the authentication only on the server.

Once configured you would have your client call the route on your server and let your server proxy it to the external API.

Upvotes: 0

Related Questions