DEV Community

Esperanza Najera
Esperanza Najera

Posted on

🔐 How to Implement OAuth 2.0 Authentication in ASP.NET Core with External APIs

Introducción
In today’s interconnected digital world, integrating with external APIs securely is more important than ever. One of the most widely used methods for securing API communication is OAuth 2.0. In this article, we'll walk through the process of implementing OAuth 2.0 authentication in an ASP.NET Core application, specifically for accessing an external API.

We'll cover how to:

Obtain an access token using Client Credentials.
Use that token to authenticate requests to a protected external API.
Handle token expiration and errors effectively.
By the end of this tutorial, you'll have a clear understanding of how to use OAuth 2.0 in your ASP.NET Core projects.

Diagram of the Authentication Flow

Image description

Explanation:

  • Client: Initiates the flow by sending a POST request to the controller with the necessary data.
  • Controller: Checks if a valid token is available. If not, it calls the service to obtain one.
  • Service: Sends a request to the OAuth server to get a token using the client credentials.
  • OAuth Server: Generates and returns an access token.
  • Controller: Uses the token to send the data to the external API.
  • External API: Processes the request and returns a response.
  • Client: Receives the final response.

Prerequisites
Before we start, make sure you have the following tools installed:

  • .NET 7 SDK or later
  • Visual Studio or Visual Studio Code
  • Postman for testing API requests

Step 1: Setting Up Your ASP.NET Core Project
Let's create a new ASP.NET Core Web API project.

dotnet new webapi -n OAuthExample
cd OAuthExample

Step 2: Configuring appsettings.json

{
  "OAuthSettings": {
    "ClientId": "your_client_id",
    "ClientSecret": "your_client_secret",
    "TokenEndpoint": "https://oauth2.example.com/token",
    "GrantType": "client_credentials"
  },
  "ApiSettings": {
    "BaseUrl": "https://api.example.com/",
    "ProtectedEndpoint": "api/resource"
  }
}

Enter fullscreen mode Exit fullscreen mode

Step 3: Creating the Configuration Classes
Create two classes, OAuthSettings.cs and ApiSettings.cs, to map the configurations.

OAuthSettings.cs

public class OAuthSettings
{
    public string ClientId { get; set; }
    public string ClientSecret { get; set; }
    public string TokenEndpoint { get; set; }
    public string GrantType { get; set; }
}

Enter fullscreen mode Exit fullscreen mode

ApiSettings.cs

public class ApiSettings
{
    public string BaseUrl { get; set; }
    public string ProtectedEndpoint { get; set; }
}

Enter fullscreen mode Exit fullscreen mode

Step 4: Registering Services in Program.cs
Make sure you configure the services in Program.cs:

var builder = WebApplication.CreateBuilder(args);

builder.Services.Configure<OAuthSettings>(builder.Configuration.GetSection("OAuthSettings"));
builder.Services.Configure<ApiSettings>(builder.Configuration.GetSection("ApiSettings"));

builder.Services.AddHttpClient("ApiClient", client =>
{
    var apiSettings = builder.Configuration.GetSection("ApiSettings").Get<ApiSettings>();
    client.BaseAddress = new Uri(apiSettings.BaseUrl);
})
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
{
    ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true
});

builder.Services.AddScoped<ApiService>();
builder.Services.AddControllers();

var app = builder.Build();
app.MapControllers();
app.Run();

Enter fullscreen mode Exit fullscreen mode

Step 5: Creating the ApiService
This service handles the process of obtaining the access token and making authenticated requests.

public class ApiService
{
    private readonly IHttpClientFactory _httpClientFactory;
    private readonly OAuthSettings _oauthSettings;
    private readonly ApiSettings _apiSettings;

    public ApiService(IHttpClientFactory httpClientFactory, IOptions<OAuthSettings> oauthOptions, IOptions<ApiSettings> apiOptions)
    {
        _httpClientFactory = httpClientFactory;
        _oauthSettings = oauthOptions.Value;
        _apiSettings = apiOptions.Value;
    }

    public async Task<string?> GetAccessTokenAsync()
    {
        var client = _httpClientFactory.CreateClient("ApiClient");

        var authHeaderValue = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{_oauthSettings.ClientId}:{_oauthSettings.ClientSecret}"));
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authHeaderValue);

        var formData = new Dictionary<string, string>
        {
            {"grant_type", _oauthSettings.GrantType}
        };

        var tokenResponse = await client.PostAsync(_oauthSettings.TokenEndpoint, new FormUrlEncodedContent(formData));
        var rawResponse = await tokenResponse.Content.ReadAsStringAsync();
        var tokenData = JsonSerializer.Deserialize<OAuthTokenResponse>(rawResponse);

        return tokenData?.AccessToken;
    }

    public async Task<string?> MakeAuthenticatedRequest(object data)
    {
        var token = await GetAccessTokenAsync();
        if (string.IsNullOrEmpty(token)) return null;

        var client = _httpClientFactory.CreateClient("ApiClient");
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

        var content = new StringContent(JsonSerializer.Serialize(data), Encoding.UTF8, "application/json");
        var response = await client.PostAsync(_apiSettings.ProtectedEndpoint, content);
        return await response.Content.ReadAsStringAsync();
    }
}

Enter fullscreen mode Exit fullscreen mode

Step 6: Creating the Controller

[ApiController]
[Route("[controller]")]
public class AuthController : ControllerBase
{
    private readonly ApiService _apiService;

    public AuthController(ApiService apiService)
    {
        _apiService = apiService;
    }

    [HttpPost("send")]
    public async Task<IActionResult> SendData([FromBody] object data)
    {
        var result = await _apiService.MakeAuthenticatedRequest(data);
        if (result == null)
            return StatusCode(500, "Error in sending data");

        return Ok(result);
    }
}

Enter fullscreen mode Exit fullscreen mode

Step 7: Testing with Postman

  1. Open Postman and create a POST request
  2. Set the URL to: https://localhost:5001/Auth/send
  3. Add a JSON body with the required data.
  4. Send the request and check the response.

Conclusion
In this guide, we've walked through how to implement OAuth 2.0 authentication using the Client Credentials flow in ASP.NET Core. This approach is ideal for securely accessing protected resources and APIs.

If you found this guide helpful, please leave a comment or share it! 🚀

Top comments (2)

Collapse
 
amandadavid profile image
Amanda

Implementing OAuth 2.0 Authentication in ASP.NET Core with external APIs is like setting up a secure checkpoint in a racing game. You connect your application with services like Google or Facebook, allowing users to log in seamlessly while the framework handles the complex security layers, so you don’t have to. This approach ensures that your app remains fast, secure, and user-friendly, letting users navigate the digital landscape as smoothly as they would steer through a well-coded game track .

Collapse
 
josemosqueira profile image
José Carlos

Very good and complete contribution, thank you very much for the information