Concur V3 Api not working for Users - c#

I configured a Concur Sandbox and played around with the api. Since the User api is not supported by the .Net SDK I wrote following code.
Issue is that code for Expenses work (returns 200 with valida result), but code for users returns 401 Unauthorized.
Expenses
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("OAuth", oauthAccessToken);
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
var requestUri = "https://www.concursolutions.com/api/v3.0/expense/receiptimages";
var respone = await httpClient.GetAsync(requestUri);
if (respone.IsSuccessStatusCode)
{
var result = respone.Content.ReadAsStringAsync();
//throw new InvalidUriException(string.Format("Invalid uri: {0}", requestUri));
}
}
Ideally this should also work as the previous code works,
Users
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("OAuth", oauthAccessToken);
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
var requestUri = "http://www.concursolutions.com/api/v3.0/common/users?user=user1%40company.net";
var respone = await httpClient.GetAsync(requestUri);
if (!respone.IsSuccessStatusCode)
{
//throw new InvalidUriException(string.Format("Invalid uri: {0}", requestUri));
}
}
}
I thought it's a permission issue, but I given allowed enough permission.
Here is the Administration->Webservice app configuration,

Issue is that the User Api call needs to be https.

Related

How to diagnose a 401 error attempting to get an OAuth2 bearer token in c# .NET Core?

I have some limited skills in c++ and have recently moved in C# (asp.net) and azure Web services. As a PoC I'm trying to make REST calls into PayPal (which I'll need to be using professionally in 3 -6 months).
I've set up my personal PayPal account using the instructions here and I get a bearer token back using curl as described in the link. Awesome.
I'm now trying to do this from .NET Core C# and all I get is a 401 error. I've examined the request and it seems the same as the curl in terms of headers; the base64 encoded credentials I think I'm adding are the same as the ones in the verbose curl log (I examined the two base64 strings by eye) so it must be something I'm doing (or not doing) in the set up of the call. I'm looking for suggestions, pointers, or flat out laughter at the obvious mistake I've made.
I've set up what I believe to be a named client thus:
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("PayPal", c =>
{
c.BaseAddress = new Uri("https://api.sandbox.paypal.com/v1/");
c.DefaultRequestHeaders.Add("Accept", "application/json");
c.DefaultRequestHeaders.Add("Accept-Language", "en_US");
});
(with all the other stuff that comes free with VS under it omitted for brevity).
I attempt the call thus:
string clientCredString = CLIENTID + ":" + SECRET;
var clientCreds = System.Text.Encoding.UTF8.GetBytes(clientCredString);
var client = _clientFactory.CreateClient("PayPal");
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", System.Convert.ToBase64String(clientCreds));
var messageBody = new Dictionary<string,string > ();
messageBody.Add("grant_type", "client_credientials");
var request = new HttpRequestMessage(HttpMethod.Get, "oauth2/token")
{
Content = new FormUrlEncodedContent(messageBody)
};
string token;
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
var json = await response.Content.ReadAsStringAsync();
token = JsonConvert.DeserializeObject<string>(json);
}
else
{
throw new ApplicationException("Well that failed");
}
and get a 401 code for my trouble.
Suggestions for troubleshooting, better methods of doing this and laughter at my foolishness all welcomed.
Update:
I read the documentation, a couple of items stand out to me:
Requires a verb of post.
Uses FormUrlEncodedContent for client credentials.
Basic auth requires username and password (Client Id & Secret)
I believe the syntax should be:
var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, "...");
request.Content = new Dictionary<string, string>() { "grant_type", "client_credentials" };
request.Headers.Authorization = new AuthenticationHeaderValue("Basic", $"{Encoding.UTF8.GetBytes($"{id}:{secret}")}");
HttpResponseMEssage = response = await client.PostAsync(request);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
For the benefit of future readers:
It was, as suggested, an encoding problem. The line:
var clientCreds = System.Text.Encoding.UTF8.GetBytes(clientCredString);
needed to be
var clientCreds = System.Text.Encoding.ASCII.GetBytes(clientCredString);
It should also be noted that this particular operation requires a POST not a GET as I was using, but once I started sending properly encoded requests the errors started to make a lot more sense.

Howto access REST API methods without username and Password - but only with a token?

I'm trying to access/call methods in a REST API with a token from c#/.net- but I can't get any response back. I have googlet a lot - but without any success :-( I am new to call methods via a REST API.
I have an endpoint and a token which I need to use for communicating with a REST API. And I need to GET, POST, PUT and DELETE data on the server via those methods
The output from the API is in JSON format.
Maybe it is simple - but I don't know howto do it.
Any help is appreciated.
I have tried the following solution - but with no success :-(
private static async void DoIt()
{
using (var stringContent = new StringContent("{ \"firstName\": \"Andy\" }", System.Text.Encoding.UTF8, "application/json"))
using (var client = new HttpClient())
{
try
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", token);
// 1. Consume the POST command
var response = await client.PostAsync(endpoint, stringContent);
var result = await response.Content.ReadAsStringAsync();
//Console.WriteLine("Result from POST command: " + result);
// 2. Consume the GET command
response = await client.GetAsync(endpoint);
if (response.IsSuccessStatusCode)
{
var id = await response.Content.ReadAsStringAsync();
//Console.WriteLine("Result from GET command: " + result);
}
}
catch (Exception ex)
{
//Console.ForegroundColor = ConsoleColor.Red;
//Console.WriteLine(ex.Message);
//Console.ResetColor();
}
}
}
In your code you initialize AuthenticationHeaderValue with "Basic", which means Basic authentication based on username and password. If you have a token, you do it with:
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", ACCESS_TOKEN);
replace ACCESS_TOKEN with the token you have.
This is the most probable solution, but I can only guess here, as I don't know the API you're trying to access. If it still doesn't work, try ommiting "Bearer".
Reference

C# to pause, turn on ssas server, backup cube.... how to?

I'm building some function apps in C# (via REST API) to make refreshes of tabular cube located on an azure ssas server. So far, no problem. However, I can't find a way to pause/start the ssas server (I saw some doc in powershell but I'd like to stay in C# so as not to mix languages)
Has anyone ever created anything like this?
I tried to make a POST suspend but no solution for now.
See the ResumeAzureAS() method here:
protected async Task<bool> ResumeAzureAS()
{
HttpClient client = new HttpClient();
var apiURI = new Uri(string.Format("https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.AnalysisServices/servers/{2}/resume?api-version=2016-05-16", subscriptionID, resourcegroup, server));
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
HttpResponseMessage response = await client.PostAsync(apiURI.ToString(), null);
response.EnsureSuccessStatusCode();
return true;
}
The rest of the API calls (such as suspend) are documented here.
private async Task<string> AASAcquireToken()
{
// Get auth token and add the access token to the authorization header of the request.
string authority = "https://login.windows.net/" + tenant + "/oauth/authorize";
AuthenticationContext ac = new AuthenticationContext(authority);
ClientCredential cred = new ClientCredential(clientID, keyID);
AuthenticationResult ar = await ac.AcquireTokenAsync(audience, cred);
return ar.AccessToken;
}
With audience set as "https://management.azure.com"
and for the "pause" itself :
I use as servername the complete name mention in the portal azure as "asazure://northeurope.asazure.windows...."
For the version of the api , well I don't know where to find it so I use one I found on the net.
var apiURI = new Uri(string.Format("https://management.azure.com/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.AnalysisServices/servers/{2}/suspend?api-version=2016-05-16", subscription, ressourceID, servername));
audience = "https://management.azure.com";
myClient.BaseAddress = new Uri(location);
myClient.DefaultRequestHeaders.Accept.Clear();
myClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
myClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", await AASAcquireToken());
HttpResponseMessage response = await myClient.PostAsync(apiURI.ToString(), null);
var output = await response.Content.ReadAsStringAsync();
response.EnsureSuccessStatusCode();
The right audience was :
audience = "https://management.core.windows.net/";

Using DefaultRequestHeaders sends requests twice?

I have a WebAPI that sends BASIC authorization information as following.
var client = new HttlpClient();
client.BaseAddress = new Uri(GlobalConstants.LdapUri);
var contentType = new MediaTypeWithQualityHeaderValue("application/json");
client.DefaultRequestHeaders.Accept.Add(contentType);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Format("{0}:{1}", userName, password))));
Task<HttpResponseMessage> results = client.GetAsync(GlobalConstants.FortressAPIUriDev);
var response = await results;
I've built this API using MVC Core 1.x and the receiving API is built using MVC5.
The problem is that this GetAsync sends two requests at the same time, and I have no clue how to resolve this. I've done some Googling myself to see if I can find a fix for this but so far no luck. Did anyone experience this problem and know how to resolve it?
Thank you very much in advance.
Long story short, found a solution as follows:
using (var client = new HttpClient())
{
var requestMessage = new HttpRequestMessage(HttpMethod.Get, GlobalConstants.LdapUri + GlobalConstants.FortressAPIUriDev);
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Format("{0}:{1}", userName, password))));
var response = await client.SendAsync(requestMessage);
}
After replacing with this code, it is sending one request at a time.
Found a hint at :
Adding headers when using httpClient.GetAsync

ASP.Net Web API Request URI Error

I have an ASP.Net Web API that generally works fine. I have a Winforms client application that does GET requests. The client application runs on our corporate network (the API is hosted as an Azure Website). Occasionally, and inconsistently, the HttpClient calls I make add what seem to be corporate URLs in front of my GET call.
Example: I try to call send an HttpClient request to the following URL: 'http://xyzxyz.azurewebsites.net/api/user/1'
but the actual request made is:
'http://usgaabc1iru01/B0000D0000N0001F0000S0000R0004/http://xyzxyz.azurewebsites.net/api/user/1'
This obviously causes an error.
I've asked our IT department what may be happening and they are at a loss. Hoping someone could point me in the right direction.
Edit:
Here's the code I use. First I have a static method I call everything I make a call to the API to get the HttpClient (is this awkward/bad perhaps):
public static HttpClient GetHttpClient()
{
var credentials = new NetworkCredential(GlobalVariables.CurrentUser.UserName, GlobalVariables.CurrentUser.Password);
HttpClientHandler handler = new HttpClientHandler();
handler.UseDefaultCredentials = false;
handler.Credentials = credentials;
HttpClient client = new HttpClient(handler);
client.BaseAddress = new Uri(PublicClasses.GlobalVariables.BaseUriString);
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
GlobalVariables.CredentialedHttpClient = client;
}
return GlobalVariables.CredentialedHttpClient;
}
}
Here's a simple GET call I use:
public static List<Project> GetAllProjects()
{
try
{
HttpClient client = GetHttpClient();
HttpResponseMessage response = client.GetAsync("api/project").Result; // Blocking call!
if (response.IsSuccessStatusCode)
{
var projects = response.Content.ReadAsAsync<IEnumerable<Project>>().Result;
return (List<Project>)projects;
}
else
{
Console.WriteLine("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
return null;
}
}
catch (Exception)
{
throw;
}
}
I don't have an answer for why is this happening but I came across similar issue in a web form (not a win forms client). That is solved by using base meta tag.I am not sure if that solves your problem, but you can give a try.
You can use base address with HttpClient like this (if you are not already doing this):
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://xyzxyz.azurewebsites.net/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync("api/user/1");
if (response.IsSuccessStatusCode)
{
//add your code
}
}

Categories