Xamarin iOS using HttpWebRequest vs HttpClient on iOS device - xamarin.ios

I have a Xamarin iOS app which uses HttpClient to make Get/Post web requests. Most of the times HttpClient throws "A task was canceled" error. I think it because of timeout getting response back. However, if i use HttpWebRequest it works fine.
Are there any limitations using HttpClient in iOS apps.
Here is the code
try
{
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + tokenVal);
var request = new HttpRequestMessage(HttpMethod.Get, requestUri);
HttpResponseMessage response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
returnStr = response.Content.ReadAsStringAsync();
}
else
{
Console.WriteLine(response.StatusCode);
}
}
catch (Exception ex)
{
returnStr = "Error:" + ex.Message;
}

Related

C# - Get Graph access token - using Client ID, Client Secret, Scope with Client Delegated Permissions

I have got the graph delegated permissions on my AAD app Client ID.
Now, I want to request access token for the graph calls using app Client ID, app Client Secret and Graph Scope in the backend without user consent.
I have tried the below approach but getting a Bad Request, can anyone guide me in the right way of what I'm doing wrong?
string graphAccessUrl = "https://login.microsoftonline.com/tenant.onmicrosoft.com/oauth2/v2.0/token";
_httpClient.DefaultRequestHeaders.Accept.Clear();
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));
List<KeyValuePair<string, string>> values = new()
{
new KeyValuePair<string, string>("grant_type", "client_credentials"),
new KeyValuePair<string, string>("client_id", appClientId),
new KeyValuePair<string, string>("client_secret", appClientSecret),
new KeyValuePair<string, string>("scope", scope) //graph scope
};
HttpContent c = new FormUrlEncodedContent(values);
//GET Method
try
{
HttpResponseMessage response = _httpClient.PostAsync(new Uri(graphAccessUrl), c).Result;
if (response.IsSuccessStatusCode)
{
string responseString = response.Content.ReadAsStringAsync().Result;
TokenData reponseObj = JsonConvert.DeserializeObject<TokenData>(responseString);
string accessToken = reponseObj.access_token;
return accessToken;
}
else
{
throw new ArgumentException("Failed to get authtoken due response code." + response.StatusCode);
}
}
catch (Exception ex)
{
throw new ArgumentException(ex.Message);
}
Unless your scenario is a little different to mine, the usual approach is to exchange the current user's access token for a Graph access token, as in my code sample. My code is in Node.js but you'll be able to translate it to C# easily enough.
*
* Use the Azure specific 'on behalf of' flow to get a token with permissions to call the user info endpoint
*/
private async _getGraphAccessToken(accessToken: string): Promise<string> {
try {
const formData = new URLSearchParams();
formData.append('grant_type', 'urn:ietf:params:oauth:grant-type:jwt-bearer');
formData.append('client_id', this._configuration.graphClient.clientId);
formData.append('client_secret', this._configuration.graphClient.clientSecret);
formData.append('assertion', accessToken);
formData.append('scope', 'openid profile email');
formData.append('requested_token_use', 'on_behalf_of');
const options = {
url: this._configuration.tokenEndpoint,
method: 'POST',
data: formData,
headers: {
'content-type': 'application/x-www-form-urlencoded',
'accept': 'application/json',
},
};
const response = await axios.request(options as AxiosRequestConfig) as any;
return response.data.access_token!;
} catch (e) {
// Report Graph errors clearly
throw ErrorFactory.fromUserInfoTokenGrantError(e, this._configuration.tokenEndpoint);
}
}
In OAuth terms this is a user assertion, to swap an incoming access token for another access token for the same user. Some further notes on setup in this blog post.

Unauthorized response with Invalid Audience error for Azure AD + ASP.Net Core 2.1

I have developed an UI and Web API using ASP.net Core 2.1 with Azure AD authentication. Both are registered with Azure App registration. I am using below code in UI. But I am getting an Unauthorized error.
string AZURE_AD_INSTANE = "https://login.microsoftonline.com/";
string TENANT_ID = "<tenant GUID>";
string CLIENT_ID = "<Client GUID ofWeb API>";
string SECRET = "<Secret created for Web API under Certificates & secrets>";
string RESOURCE = "https://MyOrg.onmicrosoft.com/TestWebAPI"; //Application ID URI set in Expose an API
ClientCredential ClientCredential = new ClientCredential(CLIENT_ID, SECRET);
string authority = String.Format("{0}{1}", AZURE_AD_INSTANE, TENANT_ID);
AuthenticationContext authContext = new AuthenticationContext(authority);
string accessToken = authContext.AcquireTokenAsync(RESOURCE, ClientCredential).Result.AccessToken;
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "https://localhost:44326/api/values/Get");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
HttpResponseMessage response = client.SendAsync(request).GetAwaiter().GetResult();
string status = response.StatusCode.ToString();
StartUp.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
.AddAzureADBearer(options => Configuration.Bind("AzureAd", options));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
I am getting a access token. When I check in jwt.io, it says 'Signature Verified'. But the API call gives unauthorized response status code. When I check the response header, it has the information as "{Bearer error="invalid_token", error_description="The audience is invalid"}"
How can I resolve this?
Ensure https://MyOrg.onmicrosoft.com/TestWebAPI is registered as a valid audience in your WebAPI:
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuer = true,
ValidAudiences = new List<string>
{
"https://MyOrg.onmicrosoft.com/TestWebAPI",
"..."
}
}
};

Why am I getting a missing header error when calling the put file api for azure data lake gen2?

I am trying to call the gen2 rest endpoint directly and keep getting an error that I am missing a required header (MissingRequiredHeader message An HTTP header that's mandatory for this request is not specified. I fail to see what header is missing. I'm using the following code to send the request.
var client = new HttpClient();
client.BaseAddress = new Uri($"https://{account}.dfs.core.windows.net/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("api_version", "2018-11-09");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _authResult.AccessToken);
string azPath = $"{baseRawSystemPath}/{path.Replace("\\", "/")}";
byte[] bytes = Encoding.UTF8.GetBytes(content);
HttpContent body = new StringContent(content, Encoding.UTF8, "application/json");
HttpResponseMessage response = await client.PutAsync(azPath, body);
if (response.IsSuccessStatusCode)
{
var responseListingJson = await response.Content.ReadAsStringAsync();
return;
}
else
{
var error = await response.Content.ReadAsStringAsync();
return;
}
Just to answer my own question... I was missing the resource=file query string parameter. That allowed the request to succeed and the file to be created.

Create Azure Key Vault C# error remote server returned an error: (403) Forbidden

I was tried to create azure key vault through in the specified subscription.
Followed this article,
https://learn.microsoft.com/en-us/rest/api/keyvault/keyvaultpreview/vaults/createorupdate#examples
So I write the code in a console application and
my code,
var URI = "https://management.azure.com/subscriptions/00000000000000000000000000/resourceGroups/0000000/providers/Microsoft.KeyVault/vaults/KeyValutADj?api-version=2018-02-14-preview";
Uri uri = new Uri(String.Format(URI));
var token = await AuthHelper.KeyVaultAuthenticationAsync();
// Create the request
var httpWebRequest = (HttpWebRequest)WebRequest.Create(uri);
httpWebRequest.Headers.Add(HttpRequestHeader.Authorization, "Bearer " + token);
httpWebRequest.ContentType = "application/json";
httpWebRequest.Method = "PUT";
HttpWebResponse httpResponse = null;
string body = "{\"location\": \"centralus\",\"properties\": {\"tenantId\": \"00000000.onmicrosoft.com\",\"sku\": {\"family\": \"A\",\"name\": \"standard\"},\"accessPolicies\": [{\"tenantId\": \"0000000000.onmicrosoft.com\",\"objectId\": \"0000000000000000000000000000000\",\"permissions\": {\"keys\": [\"encrypt\",\"decrypt\",\"wrapKey\",\"unwrapKey\",\"sign\",\"verify\",\"get\",\"list\",\"create\",\"update\",\"import\",\"delete\",\"backup\",\"restore\",\"recover\",\"purge\"],\"secrets\": [ \"get\",\"list\",\"set\",\"delete\",\"backup\",\"restore\",\"recover\",\"purge\"],\"certificates\": [\"get\",\"list\",\"delete\",\"create\",\"import\",\"update\",\"managecontacts\",\"getissuers\",\"listissuers\",\"setissuers\",\"deleteissuers\",\"manageissuers\",\"recover\",\"purge\"] }}],\"enabledForDeployment\": true,\"enabledForDiskEncryption\": true,\"enabledForTemplateDeployment\": true}}";
try
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Accept.ParseAdd("application/json");
client.DefaultRequestHeaders.UserAgent.ParseAdd("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
using (var stream = new MemoryStream())
using (var writer = new StreamWriter(stream))
{
writer.Write(body);
writer.Flush();
stream.Flush();
stream.Position = 0;
using (var content = new StreamContent(stream))
{
content.Headers.Add("Content-Type", "application/json");
var response = await client.PutAsJsonAsync(URI, content);
if (response.IsSuccessStatusCode)
{
}
else
{
}
}
}
}
}
But when run the console application , get the error
"The remote server returned an error: (403) Forbidden."
How to solve this issue?
"The remote server returned an error: (403) Forbidden."
The error message means you have not permission to add resource to azure.
I test and reproduce your problem in my site. After I Add permission in Subscriptions to user or the application which I has registered in Azure AD, I could create key vault correctly.
Also, you could get more details about how to registry AD App and assign role to application, please refer to document. After that we can get tenantId, appId, secretKey from the Azure Portal. Then we can use Microsoft.IdentityModel.Clients.ActiveDirectory SDK to get token for api authentication.
The way how to generate Bearer Token you could refer to the following code.
var appId = "0000000000000000000000000000000";
var secretKey = "******************************************";
var tenantId = "0000000000000000000000000000000";
var context = new AuthenticationContext("https://login.windows.net/" + tenantId);
ClientCredential clientCredential = new ClientCredential(appId, secretKey);
var tokenResponse = context.AcquireTokenAsync("https://management.azure.com/", clientCredential).Result;
var accessToken = tokenResponse.AccessToken;
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);
var baseUrl = new Uri($"https://management.azure.com/");
var requestURl = baseUrl +"subscriptions/b83c1ed3-c5b6-44fb-b5ba-2b83a074c23f/resourceGroups/joeyWebApp/providers/Microsoft.KeyVault/vaults/joeykeyvault5?api-version=2018-02-14-preview";
string body = "{\"location\": \"centralus\",\"properties\": {\"tenantId\": \"0000000000000000000000000000000\",\"sku\": {\"family\": \"A\",\"name\": \"standard\"},\"accessPolicies\": [{\"tenantId\": \"0000000000000000000000000000000\",\"objectId\": \"0000000000000000000000000000000\",\"permissions\": {\"keys\": [\"encrypt\",\"decrypt\",\"wrapKey\",\"unwrapKey\",\"sign\",\"verify\",\"get\",\"list\",\"create\",\"update\",\"import\",\"delete\",\"backup\",\"restore\",\"recover\",\"purge\"],\"secrets\": [ \"get\",\"list\",\"set\",\"delete\",\"backup\",\"restore\",\"recover\",\"purge\"],\"certificates\": [\"get\",\"list\",\"delete\",\"create\",\"import\",\"update\",\"managecontacts\",\"getissuers\",\"listissuers\",\"setissuers\",\"deleteissuers\",\"manageissuers\",\"recover\",\"purge\"] }}],\"enabledForDeployment\": true,\"enabledForDiskEncryption\": true,\"enabledForTemplateDeployment\": true}}";
var stringContent = new StringContent(body, Encoding.UTF8, "application/json");
var response = client.PutAsync(requestURl, stringContent).Result;
}

Github API is responding with a 403 when using Request's request function

I am trying to make a request to github's API. Here is my request:
var url = 'https://api.github.com/' + requestUrl + '/' + repo + '/';
request(url, function(err, res, body) {
if (!err && res.statusCode == 200) {
var link = "https://github.com/" + repo;
opener(link);
process.exit();
} else {
console.log(res.body);
console.log(err);
console.log('This ' + person + ' does not exist');
process.exit();
}
});
This is what I get for my response:
Request forbidden by administrative rules. Please make sure your request has a User-Agent header (http://developer.github.com/v3/#user-agent-required). Check https://developer.github.com for other possible causes.
I have used the exact same code in the past, and it has worked. No errors are being thrown by request. Now I am confused with why I am getting a 403 (Forbidden)? Any solutions?
As explained in the URL given in the response, requests to GitHub's API now require a User-Agent header:
All API requests MUST include a valid User-Agent header. Requests with no User-Agent header will be rejected. We request that you use your GitHub username, or the name of your application, for the User-Agent header value. This allows us to contact you if there are problems.
The request documentation shows specifically how to add a User-Agent header to your request:
var request = require('request');
var options = {
url: 'https://api.github.com/repos/request/request',
headers: {
'User-Agent': 'request'
}
};
function callback(error, response, body) {
if (!error && response.statusCode == 200) {
var info = JSON.parse(body);
console.log(info.stargazers_count + " Stars");
console.log(info.forks_count + " Forks");
}
}
request(options, callback);
From C# and using HttpClient you can do this (tested on latest .Net Core 2.1):
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add( new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.UserAgent.TryParseAdd("request");//Set the User Agent to "request"
using (HttpResponseMessage response = client.GetAsync(endPoint).Result)
{
response.EnsureSuccessStatusCode();
responseBody = await response.Content.ReadAsByteArrayAsync();
}
}
Thanks
If Github API is responding with status Code : 403, It simply mean
that your API fetching limit has been exceeded please wait for 1 min and again fetch then you will get your data,
ANGULAR
I had the same issue where I got 403 rate limit exceeded. I tried setting up the Authorization header using new HttpHeaders from angular/common but the header still failed.
The code that failed is:
getUser(username: string): Observable<any> {
this.headers.set('Authorization', `token ${this.ACCESS_TOKEN}`);
const endpoint = `https://api.github.com/users/${username}`;
return this.http.get(endpoint, {
headers: this.headers
}).pipe(response => {
return response
});
}
The code that worked:
getUser(username: string): Observable<any> {
const endpoint = `https://api.github.com/users/${username}`;
return this.http.get(endpoint, {
headers: {
Authorization: `token ${this.ACCESS_TOKEN}`,
}
}).pipe(response => {
return response
});
}
In this case, I directly set the header in the request headers segment.
This answers the question and as a bonus shows you how to check if there is a new release
You need to add a User-agent to the Request
request.UserAgent = "YourRepoName";
internal class Tag
{
[JsonProperty("tag_name")]
public string TagName { get; set; }
}
/// <summary>
/// Use the Github API to Check for Updates
/// </summary>
/// <returns></returns>
public static Task<string> CheckUpTodate()
{
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://api.github.com/repos/HypsyNZ/BinanceTrader.NET/releases");
request.UserAgent = new Random(new Random().Next()).ToString();
var response = request.GetResponse();
if (response != null)
{
StreamReader reader = new StreamReader(response.GetResponseStream());
string readerOutput = reader.ReadToEnd();
List<Tag>? deserializedString = JsonConvert.DeserializeObject<List<Tag>>(readerOutput);
if (deserializedString != null)
{
if (deserializedString.Count > 0)
{
Tag tagOnFirstRelease = deserializedString.FirstOrDefault();
if ("v" + ObservableObject.Version == tagOnFirstRelease.TagName)
{
return Task.FromResult("Up To Date");
}
else
{
return Task.FromResult("Update Available: " + tagOnFirstRelease.TagName);
}
}
}
}
}
catch (Exception ex)
{
// Error
}
return Task.FromResult("Unknown");
}

Resources