How do I get an OAuth 2.0 authentication token in C#
In Postman, click Generate Code and then in Generate Code Snippets dialog you can select a different coding language, including C# (RestSharp).
Also, you should only need the access token URL. The form parameters are then:
grant_type=client_credentials
client_id=abc
client_secret=123
Code Snippet:
/* using RestSharp; // https://www.nuget.org/packages/RestSharp/ */
var client = new RestClient("https://service.endpoint.com/api/oauth2/token");
var request = new RestRequest(Method.POST);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddParameter("application/x-www-form-urlencoded", "grant_type=client_credentials&client_id=abc&client_secret=123", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
From the response body you can then obtain your access token. For instance for a Bearer token type you can then add the following header to subsequent authenticated requests:
request.AddHeader("authorization", "Bearer <access_token>");
The Rest Client answer is perfect! (I upvoted it)
But, just in case you want to go "raw"
..........
I got this to work with HttpClient.
"abstractly" what you are doing is
- creating a POST request.
- with a body of payload "type" of 'x-www-form-urlencoded'. ( see FormUrlEncodedContent https://docs.microsoft.com/en-us/dotnet/api/system.net.http.formurlencodedcontent?view=net-5.0 and note the constructor : https://docs.microsoft.com/en-us/dotnet/api/system.net.http.formurlencodedcontent.-ctor?view=net-5.0)
- and in the payload of 'type' : x-www-form-urlencoded, you are putting in certain values like the grant_type, client_id, client_secret etc.
Side note, try to get it working in PostMan, and then it is easier to "code it up" using the code below.
But here we go, code using HttpClient.
.......
/*
.nuget\packages\newtonsoft.json\12.0.1
.nuget\packages\system.net.http\4.3.4
*/
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Web;
private static async Task<Token> GetElibilityToken(HttpClient client)
{
string baseAddress = @"https://blah.blah.blah.com/oauth2/token";
string grant_type = "client_credentials";
string client_id = "myId";
string client_secret = "shhhhhhhhhhhhhhItsSecret";
var form = new Dictionary<string, string>
{
{"grant_type", grant_type},
{"client_id", client_id},
{"client_secret", client_secret},
};
HttpResponseMessage tokenResponse = await client.PostAsync(baseAddress, new FormUrlEncodedContent(form));
var jsonContent = await tokenResponse.Content.ReadAsStringAsync();
Token tok = JsonConvert.DeserializeObject<Token>(jsonContent);
return tok;
}
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; }
}
Here is another working example (based off the answer above)......with a few more tweaks. Sometimes the token-service is finicky:
private static async Task<Token> GetATokenToTestMyRestApiUsingHttpClient(HttpClient client)
{
/* this code has lots of commented out stuff with different permutations of tweaking the request */
/* this is a version of asking for token using HttpClient. aka, an alternate to using default libraries instead of RestClient */
OAuthValues oav = GetOAuthValues(); /* object has has simple string properties for TokenUrl, GrantType, ClientId and ClientSecret */
var form = new Dictionary<string, string>
{
{ "grant_type", oav.GrantType },
{ "client_id", oav.ClientId },
{ "client_secret", oav.ClientSecret }
};
/* now tweak the http client */
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("cache-control", "no-cache");
/* try 1 */
////client.DefaultRequestHeaders.Add("content-type", "application/x-www-form-urlencoded");
/* try 2 */
////client.DefaultRequestHeaders .Accept .Add(new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));//ACCEPT header
/* try 3 */
////does not compile */client.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
////application/x-www-form-urlencoded
HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Post, oav.TokenUrl);
/////req.RequestUri = new Uri(baseAddress);
req.Content = new FormUrlEncodedContent(form);
////string jsonPayload = "{\"grant_type\":\"" + oav.GrantType + "\",\"client_id\":\"" + oav.ClientId + "\",\"client_secret\":\"" + oav.ClientSecret + "\"}";
////req.Content = new StringContent(jsonPayload, Encoding.UTF8, "application/json");//CONTENT-TYPE header
req.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
/* now make the request */
////HttpResponseMessage tokenResponse = await client.PostAsync(baseAddress, new FormUrlEncodedContent(form));
HttpResponseMessage tokenResponse = await client.SendAsync(req);
Console.WriteLine(string.Format("HttpResponseMessage.ReasonPhrase='{0}'", tokenResponse.ReasonPhrase));
if (!tokenResponse.IsSuccessStatusCode)
{
throw new HttpRequestException("Call to get Token with HttpClient failed.");
}
var jsonContent = await tokenResponse.Content.ReadAsStringAsync();
Token tok = JsonConvert.DeserializeObject<Token>(jsonContent);
return tok;
}
APPEND
Bonus Material!
If you ever get a
"The remote certificate is invalid according to the validation procedure."
exception......you can wire in a handler to see what is going on (and massage if necessary)
using System;
using System.Collections.Generic;
using System.Text;
using Newtonsoft.Json;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.Web;
using System.Net;
namespace MyNamespace
{
public class MyTokenRetrieverWithExtraStuff
{
public static async Task<Token> GetElibilityToken()
{
using (HttpClientHandler httpClientHandler = new HttpClientHandler())
{
httpClientHandler.ServerCertificateCustomValidationCallback = CertificateValidationCallBack;
using (HttpClient client = new HttpClient(httpClientHandler))
{
return await GetElibilityToken(client);
}
}
}
private static async Task<Token> GetElibilityToken(HttpClient client)
{
// throws certificate error if your cert is wired to localhost //
//string baseAddress = @"https://127.0.0.1/someapp/oauth2/token";
//string baseAddress = @"https://localhost/someapp/oauth2/token";
string baseAddress = @"https://blah.blah.blah.com/oauth2/token";
string grant_type = "client_credentials";
string client_id = "myId";
string client_secret = "shhhhhhhhhhhhhhItsSecret";
var form = new Dictionary<string, string>
{
{"grant_type", grant_type},
{"client_id", client_id},
{"client_secret", client_secret},
};
HttpResponseMessage tokenResponse = await client.PostAsync(baseAddress, new FormUrlEncodedContent(form));
var jsonContent = await tokenResponse.Content.ReadAsStringAsync();
Token tok = JsonConvert.DeserializeObject<Token>(jsonContent);
return tok;
}
private static bool CertificateValidationCallBack(
object sender,
System.Security.Cryptography.X509Certificates.X509Certificate certificate,
System.Security.Cryptography.X509Certificates.X509Chain chain,
System.Net.Security.SslPolicyErrors sslPolicyErrors)
{
// If the certificate is a valid, signed certificate, return true.
if (sslPolicyErrors == System.Net.Security.SslPolicyErrors.None)
{
return true;
}
// If there are errors in the certificate chain, look at each error to determine the cause.
if ((sslPolicyErrors & System.Net.Security.SslPolicyErrors.RemoteCertificateChainErrors) != 0)
{
if (chain != null && chain.ChainStatus != null)
{
foreach (System.Security.Cryptography.X509Certificates.X509ChainStatus status in chain.ChainStatus)
{
if ((certificate.Subject == certificate.Issuer) &&
(status.Status == System.Security.Cryptography.X509Certificates.X509ChainStatusFlags.UntrustedRoot))
{
// Self-signed certificates with an untrusted root are valid.
continue;
}
else
{
if (status.Status != System.Security.Cryptography.X509Certificates.X509ChainStatusFlags.NoError)
{
// If there are any other errors in the certificate chain, the certificate is invalid,
// so the method returns false.
return false;
}
}
}
}
// When processing reaches this line, the only errors in the certificate chain are
// untrusted root errors for self-signed certificates. These certificates are valid
// for default Exchange server installations, so return true.
return true;
}
/* overcome localhost and 127.0.0.1 issue */
if ((sslPolicyErrors & System.Net.Security.SslPolicyErrors.RemoteCertificateNameMismatch) != 0)
{
if (certificate.Subject.Contains("localhost"))
{
HttpRequestMessage castSender = sender as HttpRequestMessage;
if (null != castSender)
{
if (castSender.RequestUri.Host.Contains("127.0.0.1"))
{
return true;
}
}
}
}
return false;
}
public 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; }
}
}
}
........................
I recently found (Jan/2020) an article about all this. I'll add a link here....sometimes having 2 different people show/explain it helps someone trying to learn it.
http://luisquintanilla.me/2017/12/25/client-credentials-authentication-csharp/
Here is a complete example. Right click on the solution to manage nuget packages and get Newtonsoft and RestSharp:
using Newtonsoft.Json.Linq;
using RestSharp;
using System;
namespace TestAPI
{
class Program
{
static void Main(string[] args)
{
String id = "xxx";
String secret = "xxx";
var client = new RestClient("https://xxx.xxx.com/services/api/oauth2/token");
var request = new RestRequest(Method.POST);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddParameter("application/x-www-form-urlencoded", "grant_type=client_credentials&scope=all&client_id=" + id + "&client_secret=" + secret, ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
dynamic resp = JObject.Parse(response.Content);
String token = resp.access_token;
client = new RestClient("https://xxx.xxx.com/services/api/x/users/v1/employees");
request = new RestRequest(Method.GET);
request.AddHeader("authorization", "Bearer " + token);
request.AddHeader("cache-control", "no-cache");
response = client.Execute(request);
}
}
}