Search code examples
asp.net-corehttp-headersdotnet-httpclientwebapibearer-token

ASP.NET Core application http client request to a web api


I have a running Web API and I try to get a bearer token back from it. Starting the request from Postman is working and I get the token back. Once doing from my application I always get an http 400 Bad Request error.

What am I missing here?

public async Task<string> GetToken(string userName, string passWord)
{
    var request = new HttpRequestMessage(HttpMethod.Post, "api/auth/login");

    request.Headers.Authorization = new AuthenticationHeaderValue(
                "Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"{ userName}:{ passWord}")));
    request.Headers.Host = "api.my-host.com";
    request.Headers.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/json"));
            
    var response = await _httpClient.SendAsync(request);

    response.EnsureSuccessStatusCode();

    using var responseStream = await response.Content.ReadAsStreamAsync();

    var authResult = await JsonSerializer.DeserializeAsync<AuthResult>(responseStream);

    return authResult == null ? "" : authResult.Access_Token;
}

As requested here's a screenshot of the Postman result:

PostmanResult

I created a HttpGet request and added the bearer token from Postman in the code and I receive data. Just the token request seems to have a problem.

And my controller:

namespace AmsAPI.Controller
{

    [Produces("application/json")]
    [Route("api/auth")]
    [ApiController]
    [Authorize]
    public class AuthenticationController : ControllerBase
    {
        private readonly IAuthenticationManager _authenticationManager;

        public AuthenticationController(IAuthenticationManager authenticationManager)
        {
            _authenticationManager = authenticationManager;
        }

        [HttpPost("login"), AllowAnonymous]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesDefaultResponseType]
        public async Task<ActionResult> Login([FromHeader] byte[] basic)
        {
            if (!ModelState.IsValid) return BadRequest();

            string Basic = Encoding.UTF8.GetString(basic);

            var splitBasic = Basic.Split(':');

            AuthCredentials credentials = new()
            {
                UserName = splitBasic[0],
                Password = splitBasic[1]
            };

            return await _authenticationManager.SignInCheck(credentials) ?
                Ok(new
                {
                    message = string.Format("User {0} successfully logged in.", credentials.UserName),
                    access_token = await _authenticationManager.CreateToken(),
                    token_type = "bearer",
                    expires_in = "3600"
                }) :
                Unauthorized();
        }

        [HttpGet("user")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesDefaultResponseType]
        public async Task<List<User>> GetUser() => await _authenticationManager.GetUser();
    }
}

Solution

  • It appeared that Postman is sending the Basic Authorization Header as Content of the Http request and not in the Header, but my Web App was implementing the Authorization correctly in the Header. So in the WebApi it looks like

        [Authorize]
        [Route("api/auth")]
        [ApiController]
        public class AuthenticationController : ControllerBase
        {
            private readonly IAuthenticationManager _authenticationManager;
    
            public AuthenticationController(IAuthenticationManager authenticationManager)
            {
                _authenticationManager = authenticationManager;
            }
    
            [HttpPost("login"), AllowAnonymous]
            [ProducesResponseType(StatusCodes.Status200OK)]
            [ProducesDefaultResponseType]
            public async Task<ActionResult> Login()
            {
                //check if header has Authorization
                if (!Request.Headers.ContainsKey("Authorization")) return BadRequest();
    
                try
                {
                    AuthenticationHeaderValue authenticationHeaderValue = AuthenticationHeaderValue.Parse(Request.Headers["Authorization"]);
    
                    if (authenticationHeaderValue == null) throw new Exception();
    
                    var bytes = Convert.FromBase64String(authenticationHeaderValue.Parameter ?? throw new Exception());
    
                    string[] creds = Encoding.UTF8.GetString(bytes).Split(':');
    
                    AuthCredentials credentials = new()
                    {
                        UserName = creds[0],
                        Password = creds[1]
                    };
    
                    return await _authenticationManager.SignInCheck(credentials) ?
                        Ok(new
                        {
                            message = string.Format("User {0} successfully logged in.", credentials.UserName),
                            access_token = await _authenticationManager.CreateToken(),
                            token_type = "bearer",
                            expires_in = "3600"
                        }) :
                        Unauthorized();
                }
                catch (Exception)
                {
                    return BadRequest();
                }
            }
        }
    

    and my request like following:

     public async Task<string> GetToken(string userName, string passWord)
        {
            //set request
            var request = new HttpRequestMessage(HttpMethod.Post, "api/auth/login");
            //set Header
            request.Headers.Authorization = new AuthenticationHeaderValue(
                "Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes($"{ userName}:{ passWord}")));
            //get response
            var response = await _httpClient.SendAsync(request);
    
            response.EnsureSuccessStatusCode();
    
            using var responseStream = await response.Content.ReadAsStreamAsync();
    
            var authResult = await JsonSerializer.DeserializeAsync<AuthResult>(responseStream);
    
            var token = authResult.Access_Token;
    
            return authResult == null ? "Authorization failed!" : "Bearer token successfully created!";
        }
    

    and the httpClient is outsourced into a Service

    public static void ConfigureServices(this IServiceCollection services)
        {
            var url = Environment.GetEnvironmentVariable("amsApiUrl");
            var host = Environment.GetEnvironmentVariable("amsHostUrl");
            //set HttpClient
            services.AddHttpClient<IAmsAccountService, AmsAccountService>(c =>
            {
                c.BaseAddress = new Uri(url ?? "");
                c.DefaultRequestHeaders.Accept.Clear();
                c.DefaultRequestHeaders.Host = host;
                c.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/json"));
                c.Timeout = TimeSpan.FromSeconds(10);
            })
            .ConfigurePrimaryHttpMessageHandler(() =>
            {
                return new HttpClientHandler()
                {
                    CookieContainer = new CookieContainer(),
                    ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) =>
                    {
                        return true;
                    }
                };
            });
        }