HTTP Bad Request

Hi All,

This is probably a simple issue to solve, I just can’t seem to figure it out myself…

I have a Web API based on .NET Core 3.1 without MVC, so I am following the Okta API guide at:
https://developer.okta.com/blog/2018/02/01/secure-aspnetcore-webapi-token-auth

When I call the function GetNewAccessToken() in the guide to get a token from my Okta API, I get a Bad Request response var response = await client.SendAsync(request);, therefore if (response.IsSuccessStatusCode) returns false and I can’t continue to get a token.

My appsettings.Development.json contains:
“Okta”: {
“TokenUrl”: “https://dev-340400.okta.com/oauth2/default/v1/token”, // from Okta API
“ClientId”: “xxx”, // from Okta Application
“ClientSecret”: “xxx” // from Okta Application
}

My API is running on https on localhost:5001:
{
“$schema”: “json.schemastore.org/launchsettings.json”,
“iisSettings”: {
“windowsAuthentication”: false,
“anonymousAuthentication”: true,
“iisExpress”: {
“applicationUrl”: “http://localhost:5001”,
“sslPort”: 44357
}
},
“profiles”: {
“IIS Express”: {
“commandName”: “IISExpress”,
“launchBrowser”: true,
“launchUrl”: “Api”,
“environmentVariables”: {
“ASPNETCORE_ENVIRONMENT”: “Development”
}
},
“Api”: {
“commandName”: “Project”,
“launchBrowser”: true,
“launchUrl”: “Api”,
“applicationUrl”: “https://localhost:5001”,
“environmentVariables”: {
“ASPNETCORE_ENVIRONMENT”: “Development”
}
}
}
}

Both HTTP and HTTPS are added under Trusted Origins in Okta, just to be sure.

The function GetNewAccessToken is a copy from the guide and contains:

private async Task<OktaToken> GetNewAccessToken()
        {
            var token = new OktaToken();
            var client = new HttpClient();
            var client_id = this.oktaSettings.Value.ClientId;
            var client_secret = this.oktaSettings.Value.ClientSecret;
            var clientCreds = System.Text.Encoding.UTF8.GetBytes($"{client_id}:{client_secret}");
            client.DefaultRequestHeaders.Authorization =
                new AuthenticationHeaderValue("Basic", System.Convert.ToBase64String(clientCreds));

            var postMessage = new Dictionary<string, string>();
            postMessage.Add("grant_type", "client_credentials");
            postMessage.Add("scope", "access_token");
            var request = new HttpRequestMessage(HttpMethod.Post, this.oktaSettings.Value.TokenUrl)
            {
                Content = new FormUrlEncodedContent(postMessage)
            };

            var response = await client.SendAsync(request); // Bad Request - fails here
            if (response.IsSuccessStatusCode)
            {
                var json = await response.Content.ReadAsStringAsync();
                this.token = JsonConvert.DeserializeObject<OktaToken>(json);
                this.token.ExpiresAt = DateTime.UtcNow.AddSeconds(this.token.ExpiresIn);
            }
            else
            {
                throw new ApplicationException("Unable to retrieve access token from Okta");
            }
            return token;
        }

My questions are then:

  • What am I doing wrong?
  • What can I do to solve this issue?

Anyone able to help?

This is the stacktrace:
System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
—> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.
at System.Net.Security.SslStream.StartSendAuthResetSignal(ProtocolToken message, AsyncProtocolRequest asyncRequest, ExceptionDispatchInfo exception)
at System.Net.Security.SslStream.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartReceiveBlob(Byte[] buffer, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.CheckCompletionBeforeNextReceive(ProtocolToken message, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartSendBlob(Byte[] incoming, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.ProcessReceivedBlob(Byte[] buffer, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.PartialFrameCallback(AsyncProtocolRequest asyncRequest)
— End of stack trace from previous location where exception was thrown —
at System.Net.Security.SslStream.ThrowIfExceptional()
at System.Net.Security.SslStream.InternalEndProcessAuthentication(LazyAsyncResult lazyResult)
at System.Net.Security.SslStream.EndProcessAuthentication(IAsyncResult result)
at System.Net.Security.SslStream.EndAuthenticateAsClient(IAsyncResult asyncResult)
at System.Net.Security.SslStream.<>c.b__65_1(IAsyncResult iar)
at System.Threading.Tasks.TaskFactory1.FromAsyncCoreLogic(IAsyncResult iar, Func2 endFunction, Action1 endAction, Task1 promise, Boolean requiresSynchronization)
— End of stack trace from previous location where exception was thrown —
at System.Net.Http.ConnectHelper.EstablishSslConnectionAsyncCore(Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken)
— End of inner exception stack trace —
at System.Net.Http.ConnectHelper.EstablishSslConnectionAsyncCore(Stream stream, SslClientAuthenticationOptions sslOptions, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.ConnectAsync(HttpRequestMessage request, Boolean allowHttp2, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.CreateHttp11ConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.GetHttpConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.DiagnosticsHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task1 sendTask, HttpRequestMessage request, CancellationTokenSource cts, Boolean disposeCts) at Api.Services.UserService.GetValues() in C:\...\Services\UserService.cs:line 144 at Api.Controllers.UsersController.Values() in C:\...\Controllers\UsersController.cs:line 125 at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask1 actionResultValueTask)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Logged|17_1(ResourceInvoker invoker)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

This is the request:
Method: POST, RequestUri: 'https://dev-340400.okta.com/oauth2/default/v1/token', Version: 1.1, Content: System.Net.Http.FormUrlEncodedContent, Headers: { Content-Type: application/x-www-form-urlencoded }

And this is the contents of the response:
StatusCode: 400, ReasonPhrase: ‘Bad Request’, Version: 1.1, Content: System.Net.Http.HttpConnectionResponseContent, Headers:
{
Date: Mon, 27 Apr 2020 16:53:24 GMT
Transfer-Encoding: chunked
Connection: keep-alive
Server: nginx
Public-Key-Pins-Report-Only: pin-sha256="…="; pin-sha256="…="; pin-sha256="…="; pin-sha256="…="; max-age=60; report-uri=“https://okta.report-uri.com/r/default/hpkp/reportOnly
X-Okta-Request-Id: XqcOBJ2ULdFg0V@0w@ufbgAAAQ4
X-XSS-Protection: 1; mode=block; report=https://okta.report-uri.com/r/d/xss/enforce
P3P: CP=“HONK”
X-Rate-Limit-Limit: 2000
X-Rate-Limit-Remaining: 1999
X-Rate-Limit-Reset: 1588006464
Cache-Control: no-store, no-cache
Pragma: no-cache
Content-Security-Policy-Report-Only: default-src ‘self’ *.oktacdn.com dev-340400.okta.com; …
Report-To: {“group”:“csp-report”,“max_age”:31536000,“endpoints”:[{“url”:“https://okta.report-uri.com/r/d/csp/reportOnly"}],"include_subdomains”:true}
X-Content-Type-Options: nosniff
Set-Cookie: sid=""; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/
Set-Cookie: JSESSIONID=46D472DCA9B25C340555515AC96D2058; Path=/; Secure; HttpOnly
Content-Type: application/json; charset=UTF-8
Expires: 0
}

Hi kr0w,
I also have the same issue but unable to figure it out since long, did you find any solution for it.