Integrating with an Authentication API

This section demonstrates how to securely integrate your ASP.NET Core MVC or Razor Pages application with a remote authentication API. We'll cover common patterns for handling user credentials, token-based authentication, and protecting sensitive data.

Prerequisites

  • A running ASP.NET Core MVC or Razor Pages project.
  • An existing authentication API endpoint (e.g., for login, token generation).
  • Basic understanding of HTTP requests and responses.
When interacting with authentication APIs, always prioritize security. Use HTTPS, validate inputs, and store sensitive information securely.

1. User Authentication (Login)

The first step is typically to authenticate users against your API. This usually involves sending a username/email and password to a specific endpoint.

Making the HTTP Request

We'll use HttpClient to send the credentials. Ensure you configure HttpClient correctly for production environments, often as a singleton.

Example of sending login credentials to an API endpoint.

C# (Controller/PageModel)


using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;

// Assuming this is within a Controller or PageModel
public class AuthApiViewModel
{
    public string Username { get; set; }
    public string Password { get; set; }
    public string ErrorMessage { get; set; }
    public string AuthToken { get; set; }
}

public async Task<IActionResult> Login(AuthApiViewModel model)
{
    if (!ModelState.IsValid)
    {
        return Page(); // Or return View(model);
    }

    using (var httpClient = new HttpClient())
    {
        // Replace with your actual API endpoint
        var apiUrl = "https://your-auth-api.com/api/login";

        var loginData = new {
            username = model.Username,
            password = model.Password
        };

        try
        {
            var response = await httpClient.PostAsJsonAsync(apiUrl, loginData);

            if (response.IsSuccessStatusCode)
            {
                var result = await response.Content.ReadFromJsonAsync<ApiResponseModel>();
                model.AuthToken = result.Token; // Assuming your API returns a token

                // **Important:** Store the token securely (e.g., in a cookie, JWT, session)
                // and handle subsequent API calls using this token.
                // For demonstration, we'll pass it back.

                return Page(); // Or return View(model);
            }
            else
            {
                var errorContent = await response.Content.ReadAsStringAsync();
                model.ErrorMessage = $"Login failed: {response.StatusCode} - {errorContent}";
                return Page(); // Or return View(model);
            }
        }
        catch (HttpRequestException ex)
        {
            model.ErrorMessage = $"An error occurred during login: {ex.Message}";
            return Page(); // Or return View(model);
        }
    }
}

// Define a simple model for API response
public class ApiResponseModel
{
    public bool Success { get; set; }
    public string Message { get; set; }
    public string Token { get; set; }
}
                    

2. Token-Based Authentication

Most modern APIs use token-based authentication (e.g., JWT - JSON Web Tokens). After a successful login, the API issues a token. This token must be included in subsequent requests to protected API resources.

Including the Token in Headers

You'll typically add the token to the Authorization header in the format Bearer [YourToken].

Making a request to a protected API endpoint with an authentication token.

C# (Service or Helper Class)


using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;

public class ApiService
{
    private readonly HttpClient _httpClient;
    private readonly string _apiBaseUrl;

    public ApiService(HttpClient httpClient, IConfiguration configuration)
    {
        _httpClient = httpClient;
        _apiBaseUrl = configuration["ApiSettings:BaseUrl"]; // e.g., "https://your-protected-api.com/api"

        // Configure HttpClient for production (e.g., using IHttpClientFactory)
        // This example uses direct instantiation for simplicity.
    }

    public async Task<TResponse> GetDataAsync<TResponse>(string endpoint, string authToken)
    {
        _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authToken);

        var response = await _httpClient.GetAsync($"{_apiBaseUrl}/{endpoint}");

        response.EnsureSuccessStatusCode(); // Throws if not success

        return await response.Content.ReadFromJsonAsync<TResponse>();
    }

    // Add methods for POST, PUT, DELETE etc. as needed, always including the token.
}

// Example usage in a Controller/PageModel
/*
public async Task<IActionResult> LoadProtectedData(string authToken)
{
    var apiService = new ApiService(_httpClientFactory.CreateClient(), _configuration);
    var data = await apiService.GetDataAsync<MyDataModel>("protected-resource", authToken);
    // ... process data ...
    return View(data);
}
*/
                    

3. Securely Storing and Managing Tokens

This is a critical aspect of authentication integration.

  • Cookies: Store the token in an encrypted, HttpOnly cookie. This is a common and relatively secure method for web applications.
  • Client-Side Storage (Less Recommended for Auth Tokens): Avoid storing sensitive authentication tokens directly in localStorage or sessionStorage due to XSS vulnerabilities.
  • Server-Side Session: Store the token on the server in the user's session if you're not using a pure SPA.

Example: Using HttpOnly Cookies (Conceptual)

When the login is successful, you would set an IResponseCookieCollection on the response:


// After successful login and obtaining the token:
Response.Cookies.Append("AuthToken", model.AuthToken, new CookieOptions
{
    HttpOnly = true,
    Secure = true, // Only send over HTTPS
    SameSite = SameSiteMode.Strict,
    Expires = DateTimeOffset.UtcNow.AddDays(1) // Token expiration
});
                    

Then, in subsequent requests from your application (e.g., server-side calls to your API service), you would retrieve this token from the incoming request's cookies or re-issue it in outgoing requests if the API is external.

4. Handling Token Expiration and Refresh

Authentication tokens typically have an expiration time. Your application needs to handle this:

  • When a request to the API returns a 401 Unauthorized status, it might indicate an expired token.
  • Implement a token refresh mechanism if your API supports it (e.g., using a refresh token).
  • If a refresh token is not available or fails, prompt the user to log in again.
Never hardcode API keys or sensitive credentials directly in your client-side code or public repositories. Use environment variables and secure configuration management.

Conclusion

Integrating with authentication APIs requires careful attention to security, request handling, and token management. By using HttpClient effectively and implementing robust strategies for token storage and usage, you can build secure and reliable authenticated experiences in your ASP.NET Core applications.