Get access token back from Onelogin using Authorization Code Flow with PKCe in step 2 - c#

I'm Trying to get the access token form OneLogin using the Authorization Code with PKCE. I'm able to go through step1 for PKCe and getting the authorization code back from OneLogin. But when i try to get the token using the authorization code sent by one login i keep getting 400 bad request error. I'm not sure what is wrong. I followed the info provided by oneLogin website to all required parameters in the request for Step 2. below the code i'm using. I will appreciate if some one can help on this
public async Task GetAccessToken(string redirecturl, string authCode) { HttpClientHandler clientHandler = new HttpClientHandler(); clientHandler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => { return true; };
var client = new HttpClient(clientHandler);
var body = JsonConvert.SerializeObject(new
{
grant_type = "authorization_code",
code = authCode, //The code returned from OneLogin in step 1
client_id="XXXXXXXXXXXXXXXXXX386d707215718",
redirect_uri=redirecturl,//The redirect URL registered in onelogin account
code_verifier=GetCacheEntry(CodeKey)// The code verifier used in step one
});
var req = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri("https://MySubdomain.onelogin.com/oidc/2/token"),
Content = new StringContent(body)
};
req.Content.Headers.ContentType= new MediaTypeHeaderValue(#"application/x-www-form-urlencoded");
var response = await client.SendAsync(req);
if (response.StatusCode == HttpStatusCode.OK)
{
var responseBody =await response.Content.ReadAsStringAsync();
var json = JsonConvert.DeserializeObject<OAuthTokenResponse>(responseBody);
memoryCache.Remove(CodeKey);
return Ok(json);
}
return BadRequest(response);
}

Related

NetSuite OAUTH1 POST request works in Postman but fails in Restsharp

I'm using RestSharp in .NET 6 to execute a POST request to NetSuite in a c# console application.
I'm using Token Based Authentication and OAuth1
When I execute the request using the same credentials (consumer key, consumer secret, access token, access token secret and realm) in C#, for GET requests, it works. I'm able to authenticate and get a response.
When I try a POST in C#, I get a 401, 'Unauthorized' with an error message stating that the token was rejected. The same POST request, with the same auth values and URL works in Postman however.
I feel like Postman is doing something to the authentication header in a different way to Restsharp, but that still doesn't explain why GET requests are working with RestSharp
public string ExecuteRequest(string url, int httpMethod, string body = "")
{
var client = new RestClient(url);
client.Authenticator = GetOAuth1Authenticator();
Method method = (Method)httpMethod;
var request = new RestRequest(url, method);
client.AddDefaultHeader("Accept", "*/*");
client.Options.MaxTimeout = -1;
request.AddHeader("Cookie", "NS_ROUTING_VERSION=LAGGING");
request.AddHeader("ContentType", "application/json");
if (string.IsNullOrEmpty(body) == false)
{
request.AddParameter("application/json", body, ParameterType.RequestBody);
}
var response = client.Execute(request);
if (response.IsSuccessful == false)
{
throw new HttpRequestException($"ERROR: {response.ErrorMessage} - RESPONSE CONTENT: {response.Content}");
}
if (response.Content == null)
{
throw new NullReferenceException("API RESPONSE IS NULL");
}
return response.Content;
}
private OAuth1Authenticator GetOAuth1Authenticator()
{
OAuth1Authenticator authenticator = OAuth1Authenticator.ForAccessToken(consumerKey: Credential.consumer_key,
consumerSecret: Credential.consumer_secret,
token: Credential.access_token, tokenSecret: Credential.access_token_secret, signatureMethod: RestSharp.Authenticators.OAuth.OAuthSignatureMethod.HmacSha256);
authenticator.Realm = Credential.accountId;
return authenticator;
}
For anyone who knows SuiteTalk REST API for NetSuite, I'm trying to do a POST request to transform a PO into a VendorBill, using this endpoint:
[netsuite host url]/purchaseOrder/{id}/!transform/vendorBill
try
var client = new RestClient(urlString);
var request = new RestRequest(Method.POST);
btw, check your oauth method, when you are generating the signature you must specify the method you are using ("POST")

HTTPClient Post Request and get User Token

I have created a function which takes the username, password and the endpoint (url) and returns the token.
Here is the function:
public string GetToken(KiaaaAuthClient authClient)
{
Debug.Log("GetToken function executed");
// HttpClient object
HttpClient client = new HttpClient();
Debug.Log("62");
// Creating values for the request
var values = new Dictionary<string, string>
{
{"username", authClient.userName},
{"password", authClient.password},
};
Debug.Log("70");
// Encoding request in Http form
var data = new FormUrlEncodedContent(values);
Debug.Log("74");
// Response message
HttpResponseMessage responseMessage = client.PostAsync(authClient.endPoint, data).Result;
Debug.Log("78");
// Json string content
var jsonContent = responseMessage.Content.ReadAsStringAsync().Result;
Debug.Log("82");
// Getting a dictrionary from the JSON string
var jsonDict = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonContent);
Debug.Log("86");
// if 'error' is not present in jr (dictionary)
if (!jsonDict.ContainsKey("error"))
{
authClient.token = jsonDict["access_token"];
return authClient.token;
}
else
{
Debug.Log("Error: failed to acquire token");
return null;
}
It gave the webexception error (name resolution failure). Can anyone give me the reason for this? Moreover, I am very new to this, I am not sure whether it is a correct way to post a request and get the user authentication token. Please guide!

Using Microsoft Graph to obtain Access Token for Azure Web App Continuous Integration Deployment

I am attempting to wire up source control with continuous integration to an Azure Web App programmatically via C#. Historically I've used Azure PowerShell to do the same.
On the C# side, I am using the Microsoft.Azure.Management.Fluent libraries. The code to wire up source control is pretty straight forward:
await webApp.Update().DefineSourceControl().WithContinuouslyIntegratedGitHubRepository(GIT_URL).WithBranch(GIT_BRANCH).WithGitHubAccessToken(GIT_TOKEN).Attach().ApplyAsync();
This code runs for about 5 minutes and then returns the error:
{"Code":"BadRequest","Message":"Parameter x-ms-client-principal-name
is null or empty."}
I initially interpreted this to mean the Fluent libraries weren't passing a necessary value to the API, so I attempted to hit the API directly using Fluent libraries to abstract the authorization piece:
var credentials = SdkContext.AzureCredentialsFactory.FromServicePrincipal( CLIENT_ID, CLIENT_SECRET, TENANT_ID, AzureEnvironment.AzureGlobalCloud);
var client = RestClient.Configure().WithEnvironment(AzureEnvironment.AzureGlobalCloud).WithCredentials(credentials).Build();
CancellationToken cancellationToken = new CancellationToken();
var request = new HttpRequestMessage(HttpMethod.Get, $"https://management.azure.com/subscriptions/{SUBSCRIPTION_ID}/resourcegroups/{RESOURCE_GROUP}?api-version=2019-10-01");
request.Headers.Add("x-ms-client-principal-name", USERNAME);
client.Credentials.ProcessHttpRequestAsync(request, cancellationToken).GetAwaiter().GetResult();
var httpClient = new HttpClient();
var response = httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).GetAwaiter().GetResult();
var result = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
Several variations of this resulted in the same error referencing x-ms-client-principal-name. This lead me to believe the problem was with the token being used and the associated permissions. To test this, I ran the PowerShell script mentioned above, watched it run through Fiddler, and grabbed the token it used to complete. Using that token with basically the same code, it worked fine:
var token = "<THE TOKEN I COPIED FROM FIDDLER>";
CancellationToken cancellationToken = new CancellationToken();
var request = new HttpRequestMessage(requestType, $"https://management.azure.com/subscriptions/{SUBSCRIPTION_ID}/resourceGroups/{RESOURCE_GROUP}/providers/Microsoft.Web/sites/{WEB_APP}/sourcecontrols/web?api-version=2015-08-01");
request.Headers.Add("Authorization", $"Bearer {token}");
if ((requestType == HttpMethod.Put || requestType == HttpMethod.Post) && !string.IsNullOrEmpty(postData))
{
request.Content = new StringContent(postData, Encoding.UTF8, "application/json");
}
var httpClient = new HttpClient();
var response = httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).GetAwaiter().GetResult();
var result = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
So now, it seems to just be a matter of getting the access token myself. This is where things have become difficult. If I obtain the token as the service principal, I end up with the same x-ms-client-principal-name that I started with:
public static string GetAuthToken(string tenantId, string clientId, string clientSecret)
{
var request = new HttpRequestMessage(HttpMethod.Post, $"https://login.microsoftonline.com/{tenantId}/oauth2/token");
request.Content = new StringContent($"grant_type=client_credentials&client_id={clientId}&client_secret={clientSecret}&resource=https://management.azure.com", Encoding.UTF8, "application/x-www-form-urlencoded");
CancellationToken cancellationToken = new CancellationToken();
var httpClient = new HttpClient();
var response = httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).GetAwaiter().GetResult();
var result = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
var auth = JsonConvert.DeserializeObject<AuthResponse>(result);
return auth.AccessToken;
}
When I attempt to obtain token using my username and password, I get an error back telling me:
AADSTS90002: Tenant '' not found. This may happen if there are no
active subscriptions for the tenant. Check to make sure you have the
correct tenant ID. Check with your subscription administrator.
Here's the code I use for that:
public static string GetAuthToken(string tenantId, string username, string password, string clientId, string clientSecret)
{
var request = new HttpRequestMessage(HttpMethod.Post, $"https://login.microsoftonline.com/{tenantId}/oauth2/token");
request.Content = new StringContent($"grant_type=password&username={username}&password={password}&client_id={clientId}&client_secret={clientSecret}&resource=https://management.azure.com", Encoding.UTF8, "application/x-www-form-urlencoded");
CancellationToken cancellationToken = new CancellationToken();
var httpClient = new HttpClient();
var response = httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).GetAwaiter().GetResult();
var result = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
var auth = JsonConvert.DeserializeObject<AuthResponse>(result);
return auth.AccessToken;
}
I cannot have the C# code invoke the PS script as a work-around as it requires Azure PowerShell which is not guaranteed to be on the machine running the code (Azure Web App) and cannot be installed due to Admin restrictions.
I need to be able to obtain an access token that also has permissions for azure dev ops (formally VSTS) so I can bind/wire-up source control for continuous integration. Any guidance that can help me get past this is much appreciated.
I was finally able to get it working. First, I had to create a new Azure AD User and grant it the necessary permissions; this would not work with my regular Azure Login. With the new user and permissions, I am able to successfully obtain token like so:
public static async Task<string> GetAccessToken(string clientId, string userName, string password)
{
AuthenticationContext authenticationContext = new AuthenticationContext("https://login.microsoftonline.com/<TENANT_ID>");
var resourceId = "https://management.azure.com";
var result = await authenticationContext.AcquireTokenAsync(resourceId, clientId, new UserPasswordCredential(userName, password));
return result.AccessToken;
}
This token then works for wiring up source control and continuous integration.

How to send request to the HTTP Header using the json webservice in windows store app

I'm working on a windows store app where I am using a web service which has parameters for downloading videos that are given below.
[request addValue:Token Id forHTTPHeaderField:#"Authorization"];
Through log_in web services I get the access token which I have to pass as value in the Authorization a request to the HTTP Header.
Token token="hgmgmhmhgm6dfgffdbfetgjhgkj4mhh8dghmge"
I have to send both these parameters with the web service given to me but I am unable to send them as I'm getting the error status code 404 unauthorized.
Here is my code:
System.Net.Http.HttpClient httpClient1 = new System.Net.Http.HttpClient();
httpClient1.DefaultRequestHeaders.Date = DateTime.Now;
httpClient1.DefaultRequestHeaders.Add("Authorization",acesstoken);
var httpResponse1 = await httpClient.GetAsync("http://gbdfbbnbb#gfdh.co/appi/fdbfdses/3/videos");
string vale = await httpResponse1.Content.ReadAsStringAsync();
string responses = vale;
I know my code is wrong and I need to correct it. Help me with your valuable suggestions.
Try this code
using (var client = new HttpClient())
{
try
{
String url = "https://www.example-http-request.com/json";
var httpClient = new HttpClient(new HttpClientHandler());
var values = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("Authorization", acesstoken)
};
HttpResponseMessage response = await httpClient.PostAsync(new Uri(url), new FormUrlEncodedContent(values));
response.EnsureSuccessStatusCode();
var responsesStr = await response.Content.ReadAsStringAsync();
if (!responsesStr.Equals(""))
{
//http request data now capture in responseToken string.
//you can change name as per your requirement.
String responseOutput = responsesStr;
//now convert/parse your json using Newtonsoft JSON library
// Newtonsoft JSON Librar link : { http://james.newtonking.com/json }
//LoggingModel is a class which is the model of deserializing your json output.
LoggingModel array = JsonConvert.DeserializeObject<LoggingModel>(responseOutput);
bool isSuccess = array.IsSuccessful;
if (isSuccess == true)
{
//if success
}else{
//if login failed
}
}else{
//no response in http request failed.
}
}
catch (Exception ex)
{
//catch exceptio here
}
}

Getting signature_invalid calling oauth/request_token for Etsy's API using RestSharp

I'm trying to use RestSharp to access Etsy's API. Here's the code I'm using attempting to get an OAuth access token:
var authenticator = OAuth1Authenticator.ForRequestToken(
ConfigurationManager.AppSettings["ApiKey"],
ConfigurationManager.AppSettings["ApiSecret"]);
// same result with or without this next line:
// authenticator.ParameterHandling = OAuthParameterHandling.UrlOrPostParameters;
this.Client.Authenticator = authenticator;
var request = new RestRequest("oauth/request_token")
.AddParameter("scope", "listings_r");
var response = this.Client.Execute(request);
Etsy tells me that the signature is invalid. Interestingly enough, when I enter the timestamp and nonce values generated by the request into this OAuth signature validation tool, the signatures don't match. Moreover, the URL generated by the tool works with Etsy where the one generated by RestSharp doesn't. Is there something I'm doing wrong or something else I need to configure with RestSharp?
Note: I'm using the version of RestSharp provided by their Nuget package, which at the time of this posting is 102.5.
I finally was able to connect to the Etsy API with RestSharp using OAuth. Here is my code -- I hope it works for you...
RestClient mRestClient = new RestClient();
//mRestClient.BaseUrl = API_PRODUCTION_URL;
mRestClient.BaseUrl = API_SANDBOX_URL;
mRestClient.Authenticator = OAuth1Authenticator.ForRequestToken(API_KEY,
API_SHAREDSECRET,
"oob");
RestRequest request = new RestRequest("oauth/request_token", Method.POST);
request.AddParameter("scope",
"shops_rw transactions_r transactions_w listings_r listings_w listings_d");
RestResponse response = mRestClient.Execute(request);
if (response.StatusCode != System.Net.HttpStatusCode.OK)
return false;
NameValueCollection queryString = System.Web.HttpUtility.ParseQueryString(response.Content);
string oauth_token_secret = queryString["oauth_token_secret"];
string oauth_token = queryString["oauth_token"];
string url = queryString["login_url"];
System.Diagnostics.Process.Start(url);
// BREAKPOINT HERE
string oauth_token_verifier = String.Empty; // get from URL
request = new RestRequest("oauth/access_token");
mRestClient.Authenticator = OAuth1Authenticator.ForAccessToken(API_KEY,
API_SHAREDSECRET,
oauth_token,
oauth_token_secret,
oauth_token_verifier);
response = mRestClient.Execute(request);
if (response.StatusCode != System.Net.HttpStatusCode.OK)
return false;
queryString = System.Web.HttpUtility.ParseQueryString(response.Content);
string user_oauth_token = queryString["oauth_token"];
string user_oauth_token_secret = queryString["oauth_token_secret"];
The user_oauth_token and user_oauth_token_secret are the user's access token and access token secret -- these are valid for the user until the user revokes access.
I hope this code helps!

Categories