.NET 5.0 vs Framework 4.8 HTTPRequest oAuth02.

megabug

Member
Joined
Feb 15, 2022
Messages
11
Programming Experience
10+
Hi everybody,

I'm working on an app written in .net Framework 4.8 with usage of an API that use OAuth02 to authenticate. And I'm not really familiar with web API in C#.
The sample code I received is based on .NET 5.0 and work perfectly. However, when I look at the compatibility of the commands used,
I saw it should be ok to use the same code. However, I received an error 400. I tried to use fiddler to see the differences in the message sent
and seem to be content length. I tried to adjust it and now I received an other message that seem to be related to async function.
I tried to reference the .net 5 code as a new DLL but it seem not possible du to the target framework of my app (Framework 4.8) So I need to understand what is the difference between .NET 5.0 and Framwork 4.8 call.
I supposed It should be something related to TLS security or something like that, but How to manage it with old framework.

Could someone help me to downgrade that code.

Thanks in advance

Here is .NET 5.0 code I received.
C#:
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
namespace TestMemento2
{
    class Program
    {
        private static readonly HttpClient client = new HttpClient();
        static async Task Main(string[] args)
        {

            string clientID = "abc";
            string clientSecret = "1234";
            string url = "url";

            var values = new List<KeyValuePair<string, string>>();
            values.Add(new KeyValuePair<string, string>("grant_type", "client_credentials"));
            var content = new FormUrlEncodedContent(values);

            string encodedAuth = Convert.ToBase64String(Encoding.Default.GetBytes(clientID + ":" + clientSecret));
            var requestMessage = new HttpRequestMessage(HttpMethod.Post, url);
            requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Basic", encodedAuth);
            requestMessage.Content = content;
        
            //make the request
            var task = client.SendAsync(requestMessage);
            var response = task.Result;
            response.EnsureSuccessStatusCode();
            string responseBody = response.Content.ReadAsStringAsync().Result;
            Console.WriteLine(responseBody);
            Console.ReadLine();
        }

    }
}
 
Last edited:
Is this issue actually resolved? If so, it is considered good form to provide your solution. That way, we can improve it if possible and others may also benefit from it.
 
I tried to adjust it and now I received an other message that seem to be related to async function.
And what is that new error message or warning?

Was the code that you posted above the original code, or your adjusted code? If that was the original code, can you please post a reply to this thread with your adjusted code (in code tags), and let us know which line number the error or warning was pointing to?

Looking at the code in post #1, it looks like you declared Main() as async, but you never actually use await anywhere. You should just remove async as well as change the return type to void instead of Task.
 
Here is my reworked code, I now received Id = 3, Status = WaitingForActivation, Method = "{null}", Result = "{Not yet computed}"
I know it's related to mixing async and synch code. I tried that code in a console app and I need to use it on a windows form.
When the line 57 is executed it automatically get out of the function and return to main.
All the code I found on Internet didn't really give me an idea how to solve the situation. I tried many exemple from

and other forum but nothing work for my needs. I'm relatively new with web API in C#. I Already used it, but didn't required OAuth2 authentication.

Reviewed code:
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
namespace TestMemento
{

    internal class Token
    {
        [JsonProperty("access_token")]
        public string AccessToken { get; set; }

        [JsonProperty("token_type")]
        public string TokenType { get; set; }

        [JsonProperty("expires_in")]
        public int ExpiresIn { get; set; }

        [JsonProperty("refresh_token")]
        public string RefreshToken { get; set; }
    }

    class Program
    {

        private static readonly HttpClient client = new HttpClient();
        static void Main(string[] args)
        {
            var token = getTokenAsync();
            
            Console.ReadLine();
        }
        private static async Task<Token> getTokenAsync()
        {
            string clientID = "abc";
            string clientSecret = "1234";
            string url = "url";

            HttpClient client = new HttpClient();
            var values = new List<KeyValuePair<string, string>>();
            values.Add(new KeyValuePair<string, string>("grant_type", "client_credentials"));
            var content = new FormUrlEncodedContent(values);

            string encodedAuth = Convert.ToBase64String(Encoding.Default.GetBytes(clientID + ":" + clientSecret));
            var requestMessage = new HttpRequestMessage(HttpMethod.Post, url);
            requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Basic", encodedAuth);
            requestMessage.Content = content;
            ASCIIEncoding encoding = new ASCIIEncoding();
            byte[] byte1 = encoding.GetBytes(content.ToString());

            requestMessage.Content.Headers.ContentLength = byte1.Length;
            //make the request
            HttpResponseMessage httpResponseMessage = await client.SendAsync(requestMessage);
            var jsonContent = await httpResponseMessage.Content.ReadAsStringAsync();
            Token tok = JsonConvert.DeserializeObject<Token>(jsonContent);
            return tok;
        }

    }
}
 
And what is that new error message or warning?

Was the code that you posted above the original code, or your adjusted code? If that was the original code, can you please post a reply to this thread with your adjusted code (in code tags), and let us know which line number the error or warning was pointing to?

Looking at the code in post #1, it looks like you declared Main() as async, but you never actually use await anywhere. You should just remove async as well as change the return type to void instead of Task.
The code I posted in 1 is in .net 5.0 and it work fine. I need to make it work into .NET Framework 4.8 however anything I tried not work. I'm still blocked. Any help will be considered. On How to convert that code. I'm relatively new with API using Token and OAuth02.
Thanks.
 
Other than deleting line 6 (using System.Text.Json) and changing the return type to void and removing async on line 13, the code compiles just fine with .NET Framework 4.8.

Can you tell us what URL you are passing in on line 18 so that we can try to see what error you are getting?
 
Other than deleting line 6 (using System.Text.Json) and changing the return type to void and removing async on line 13, the code compiles just fine with .NET Framework 4.8.

Can you tell us what URL you are passing in on line 18 so that we can try to see what error you are getting?
Yes for sure it compiles just fine however, the service return me an error 400. I can't give you the URL because if I give you the url you will also need the username and password to see the error.
Here is the content of the error returned by the API.
C#:
{StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
  Connection: keep-alive
  Pragma: no-cache
  X-Rate-Limit-Limit: 15s
  X-Rate-Limit-Remaining: 1999
  X-Rate-Limit-Reset: 2022-02-22T21:11:18.7044197Z
  CF-Cache-Status: DYNAMIC
  Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
  Report-To: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v3?s=eFZ9rUjcOq1VMg%2FZIwTaOaRCdKuWsfK981WzUr0A16SFT4lxRB%2BeGGYt6KTg1k5cMwL9A2oGYS0SA9RdvI4bpDpLmMOnfuS7YsLSydXcIQ1yvo7aiXBBkZcFJbRcPN9yM117enHGJQ%3D%3D"}],"group":"cf-nel","max_age":604800}
  NEL: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}
  CF-RAY: 6e1b339be8807138-YUL
  Cache-Control: no-cache, max-age=0
  Date: Tue, 22 Feb 2022 21:10:31 GMT
  Server: cloudflare
  X-Powered-By: ASP.NET
  Content-Length: 23
  Content-Type: application/json; charset=utf-8
}}
 
Last edited by a moderator:
The following OAuth2 authentication works fine for me when trying to get an access token for Microsoft Graph API. It worked using both .NET Framework 4.8, .NET Core 5.0, and .NET Core 6.0 without modifying anything in the code.

C#:
using System;
using System.Collections.Generic;
using System.Net.Http;

namespace TestMemento2
{
    class Program
    {
        private static readonly HttpClient client = new HttpClient();

        static void Main(string[] args)
        {
            string clientID = "a71490ea-5ae3-414f-aa73-5b9aa1177ffc";
            string clientSecret = "*** secret ***";
            string tenantID = "ab5f051e-d4ea-429d-8d7f-93aaaa6f2193";
            string url = $"https://login.microsoftonline.com/{tenantID}/oauth2/v2.0/token";

            var values = new List<KeyValuePair<string, string>>();
            values.Add(new KeyValuePair<string, string>("grant_type", "client_credentials"));
            values.Add(new KeyValuePair<string, string>("client_id", clientID));
            values.Add(new KeyValuePair<string, string>("client_secret", clientSecret));
            values.Add(new KeyValuePair<string, string>("scope", "https://graph.microsoft.com/.default"));
            var content = new FormUrlEncodedContent(values);

            var requestMessage = new HttpRequestMessage(HttpMethod.Post, url);
            requestMessage.Content = content;

            var response = client.SendAsync(requestMessage).Result;
            response.EnsureSuccessStatusCode();
            string responseBody = response.Content.ReadAsStringAsync().Result;
            Console.WriteLine(responseBody);
            Console.ReadLine();
        }
    }
}

The only major difference between your code and my code is that credentials are sent as part of the content vs. your code that was trying to send the credentials as the Authorization header.
 
The following OAuth2 authentication works fine for me when trying to get an access token for Microsoft Graph API. It worked using both .NET Framework 4.8, .NET Core 5.0, and .NET Core 6.0 without modifying anything in the code.

C#:
using System;
using System.Collections.Generic;
using System.Net.Http;

namespace TestMemento2
{
    class Program
    {
        private static readonly HttpClient client = new HttpClient();

        static void Main(string[] args)
        {
            string clientID = "a71490ea-5ae3-414f-aa73-5b9aa1177ffc";
            string clientSecret = "*** secret ***";
            string tenantID = "ab5f051e-d4ea-429d-8d7f-93aaaa6f2193";
            string url = $"https://login.microsoftonline.com/{tenantID}/oauth2/v2.0/token";

            var values = new List<KeyValuePair<string, string>>();
            values.Add(new KeyValuePair<string, string>("grant_type", "client_credentials"));
            values.Add(new KeyValuePair<string, string>("client_id", clientID));
            values.Add(new KeyValuePair<string, string>("client_secret", clientSecret));
            values.Add(new KeyValuePair<string, string>("scope", "https://graph.microsoft.com/.default"));
            var content = new FormUrlEncodedContent(values);

            var requestMessage = new HttpRequestMessage(HttpMethod.Post, url);
            requestMessage.Content = content;

            var response = client.SendAsync(requestMessage).Result;
            response.EnsureSuccessStatusCode();
            string responseBody = response.Content.ReadAsStringAsync().Result;
            Console.WriteLine(responseBody);
            Console.ReadLine();
        }
    }
}

The only major difference between your code and my code is that credentials are sent as part of the content vs. your code that was trying to send the credentials as the Authorization header.
I tried your code with my API and I still receive an error. I tried to check what I can do by comparing both message in fieddler and it seem that the content-lenght is not correctly handled.
however I'm not sure on how to handle it. I tried some code I found on the internet but with no success.
C#:
{StatusCode: 400, ReasonPhrase: 'Bad Request', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
  Transfer-Encoding: chunked
  Connection: keep-alive
  Pragma: no-cache
  X-Rate-Limit-Limit: 15s
  X-Rate-Limit-Remaining: 1999
  X-Rate-Limit-Reset: 2022-02-22T21:46:10.9539081Z
  CF-Cache-Status: DYNAMIC
  Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
  Report-To: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v3?s=YnN9kWmh%2B5MrrC%2BgyYBpw%2FdSoejAOFzBsepgjyNcbVTrUvSf8YHsIETjE42pYDl1CiCQ21iD22rIziS8UksKGSzSiv7OfyA1MVnN3t339Ix29OKY92EZrWpSTBEnWiHOkPnS7LBOgQ%3D%3D"}],"group":"cf-nel","max_age":604800}
  NEL: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}
  CF-RAY: 6e1b66affb1cca57-YUL
  Cache-Control: no-cache, max-age=0
  Date: Tue, 22 Feb 2022 21:45:23 GMT
  Server: cloudflare
  X-Powered-By: ASP.NET
  Content-Type: application/json; charset=utf-8
}}
 
Capture the 400 Bad Request response error in Fiddler. Usually the content of the response will have more descriptive text telling you why it considers it a bad request. (For example, when I was writing up my code above, I was getting error 400 initially as well, but the text told me it was because it didn't recognize the clientID I was originally passing in. I had to double check and sure enough, there was a typo.)

Also when you were testing .NET Framework 4.8 vs .NET Core 5.0, were you testing on the same machine, or using different machines? If different machines, what OS versions were they respectively?
 
Last edited:
Capture the 400 Bad Request response error in Fiddler. Usually the content of the response will have more descriptive text telling you why it considers it a bad request. (For example, when I was writing up my code above, I was error 400 as well, but the text was told me it was because it didn't recognize the clientID I was originally passing in. I had to double check and sure enough, there was a typo.)

Also when you were testing .NET Framework 4.8 vs .NET Core 5.0, were you testing on the same machine, or using different machines? If different machines, what OS versions were they respectively?
I just see Error: JSON=Error: invalid_client in fiddler. as a response of the API.
 
Please post the data from the Raw response inspector in Fiddler.
 
I would also recommend taking a closer look at your outbound payload for .NET 4.8 vs. .NET 5.0 vs .NET 6.0. For me the only difference between them was the .NET 4.8 had this is the headers:
C#:
Expect: 100-continue
Connection: Keep-Alive

Other than that, the Content-Length header value was exactly the same between the three versions of the code.

As I re-read your posts above you kept on mentioning that the content length was different between .NET 4.8 and .NET 5.0. Double check the content that got URL encoded in the body.

This should always encode to the same length regardless of .NET 4.8 or .NET 5.0 or .NET 6.0:
C#:
var values = new List<KeyValuePair<string, string>>();
values.Add(new KeyValuePair<string, string>("grant_type", "client_credentials"));
var content = new FormUrlEncodedContent(values);
:
requestMessage.Content = content;
 
Last edited:
I just see Error: JSON=Error: invalid_client in fiddler. as a response of the API.
Like I said, I just see Error_Invalid Client. Other information have no signification for me.


HTTP/1.1 400 Bad Request
Date: Wed, 23 Feb 2022 15:17:43 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 23
Connection: keep-alive
Cache-Control: no-cache,max-age=0
Pragma: no-cache
X-Rate-Limit-Limit: 15s
X-Rate-Limit-Remaining: 1999
X-Rate-Limit-Reset: 2022-02-23T15:18:33.4207582Z
X-Powered-By: ASP.NET
CF-Cache-Status: DYNAMIC
Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Report-To: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v3?s=gKZ1tbcglIzNW2uZRaopbsh6ua7qsmP0cvDQAnUPfuPqplY8Ms0118UfVoGxEjiOn8vYjAEi0xyN%2FRr%2FP3pn%2Fv67kEuBQH3PUas0Dbq5YpKQnR3r5Mb5r9%2FpOxQgYrrTJFLxXKFG1Q%3D%3D"}],"group":"cf-nel","max_age":604800}
NEL: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}
Server: cloudflare
CF-RAY: 6e216c312f844bcb-YUL

"Error: invalid_client"

I also Add Here Fiddler difference between header of my message here. Like I said, it's exactly the same code between .net 5 and framework 4.8. See in Orange the value of the content-lenght differ.
However If I want to recalculate the Content-Lenght I'm not sure of the moment where it's calculated and where to put the new value.
is it manage by client or by the message itself.

.NET Framewok 4.8
HTTP/1.1 400 Bad Request
Date: Wed, 23 Feb 2022 17:38:00 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 23
Connection: keep-alive
Cache-Control: no-cache,max-age=0
Pragma: no-cache
X-Rate-Limit-Limit: 15s
X-Rate-Limit-Remaining: 1999
X-Rate-Limit-Reset: 2022-02-23T17:38:50.8205598Z
X-Powered-By: ASP.NET
CF-Cache-Status: DYNAMIC
Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Report-To: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v3?s=OuUdhoqRyghnUI0K37BJqep96vJI%2F6xbOhs7lVwSSY7gREJmg1y0XD8rRpavDUhSuytbzoiSc95KSK1X3mkaNO1mj%2F9lzCzgWgk1PnILlF345ES8EX%2FKDCXAbqzxljRMO5cSdjN%2Flw%3D%3D"}],"group":"cf-nel","max_age":604800}
NEL: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}
Server: cloudflare
CF-RAY: 6e2239afe94fece2-YUL


NET 5.0
HTTP/1.1 200 OK
Date: Wed, 23 Feb 2022 17:39:27 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 718
Connection: keep-alive
Cache-Control: no-cache,max-age=0
Pragma: no-cache
X-Rate-Limit-Limit: 15s
X-Rate-Limit-Remaining: 1999
X-Rate-Limit-Reset: 2022-02-23T17:40:17.6013887Z
X-Powered-By: ASP.NET
CF-Cache-Status: DYNAMIC
Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Report-To: {"endpoints":[{"url":"https:\/\/a.nel.cloudflare.com\/report\/v3?s=EtpeNcxjWAyY%2F%2BmgG%2FdDqHcDAxFwTtzBmcy4OxTg7sibIWqDQIshYV97B%2FolbiHl%2BTCPYltxf0t%2Fb6BKlbEeS5WI2BRzjZORlZ93yFprA9M6qrCJHyCGnl4Hpb4AR1wBLQ0Fmrh2NQ%3D%3D"}],"group":"cf-nel","max_age":604800}
NEL: {"success_fraction":0,"report_to":"cf-nel","max_age":604800}
Server: cloudflare
CF-RAY: 6e223bce4f3c4bd7-YUL
 
Last edited:
Back
Top Bottom