I having some issues with the data being returned by the WebAuthenticationBroker in my app. The api I'm connecting to utilizes OAuth 1.0a. I'm able to get the Broker to pull up the authorization page, but ResponseData from the Broker is just url to the page that contains the key value pair for the OAuth Verifier. So I tried a web request to try and get that data, but end up getting 405: Method Not allowed error from the page. Here is my code...
public async Task Authorize(string oAuthAuthorizeUrl)
{
// Start WebAuthenticationBroker
System.Uri StartUri = new Uri(oAuthAuthorizeUrl);
System.Uri EndUri = new Uri(oAuthCallBack);
WebAuthenticationResult auth = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.None, StartUri, EndUri);
if (auth.ResponseStatus == WebAuthenticationStatus.Success)
{
HttpClient httpClient = new HttpClient();
HttpResponseMessage response = await httpClient.GetAsync(auth.ResponseData);
HttpContent content = response.Content;
string responseText = await content.ReadAsStringAsync();
string oauth_verifier = null;
string[] keyValPairs = responseText.Split('&');
for (int i = 0; i < keyValPairs.Length; i++)
{
String[] splits = keyValPairs[i].Split('=');
switch (splits[0])
{
case "oauth_verifier":
oauth_verifier = splits[1];
break;
}
}
oAuthVerifier = oauth_verifier;
}
}
is this how the Broker is supposed to work? I assumed it would send back the data with the Verifier. Thanks for the help.
Related
i have a simple demo winform app and im trying to make a post request with header to web api.
i received access token and refreash token form the server and i stored that in text file.
and im trying to make a post request by sending the refreash token with the body and sending the access token with the header but i dont know how to include the header with the post request.
this my post method
public static async Task<string> sendMessage(string name, string contents)
{
using (HttpClient client = new HttpClient())
{
//reading the access token and refreash token from file
StreamReader sr = new StreamReader(#"C:\Users\noorm\Desktop\noor.txt");
string accessToken, refreashToken;
accessToken = sr.ReadLine();
refreashToken = sr.ReadLine();
//defining new instance of message opject
var newMessage = new messages()
{
name = name,
content = contents,
refreashToken = refreashToken
};
//sening the opject using post async and returning the response
var newPostJson = JsonConvert.SerializeObject(newMessage);
var payLoad = new StringContent(newPostJson, Encoding.UTF8, "application/json");
using (HttpResponseMessage res = await client.PostAsync(baseURL + "/messages", payLoad))
{
using (HttpContent content = res.Content)
{
string data = await content.ReadAsStringAsync();
if (data != null)
{
return data;
}
}
}
}
return string.Empty;
}
and this is the button
private async void btnSend_Click(object sender, EventArgs e)
{
var responce = await restHelper.sendMessage(txtName.Text.Trim(),txtContent.Text.Trim());
rtxt.Text = responce;
}
You can try something like the following:
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "Your Oauth token");
this is how i was able to send the access token with the header
client.DefaultRequestHeaders.Add("x-auth-token", accessToken);
I wrote a function using GetAsync() which works fine, but now i'd like to expand on it using SendAsync() instead [for POSTing and sofourth]; however my SendAsync() version is not working as expected, it returns a 422 unprocessible entity. (no IDE atm; sorry for minor typos)
init
var Client = new HttpClient{
BaseAddress = "https://example.com"
}
Client.DefaultRequestHeaders.UserAgent.ParseAdd("Project/1.0 (blah blah)");
...
Working GetAsync()
public async Task<string> GetResponse(string user, string pass){
var uri = $"/user/login.json?name={user}&password={pass}";
var req = await Client.GetAsync(uri);
return req.Content.Request.Content.ReasStringAsync();
}
non working SendAsync()
public async Task<string> GetResponse(string page, Dictionary<string, string> args){
//assume page = "/user/login.json" and args == {"username", "user"},{"password", "pass"}
try{
var req = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri(page),
Content = new FormUrlEncodedContent(args),
}
var response = await Client.SendAsync(req);
if(response.IsSuccessStatusCode){
return await response.Content.ReasStringAsync();
return null;
}
catch{ return null }
}
note: along with the 422, the response still contains json which states "invalid Auth Token!"
What is GetAsync() doing that SendAsync() is not?
Your Send included content in the BODY of a HTTP GET request.
HTTP GET requests should not have a BODY and there are servers that wont process such requests.
Convert the dictionary to a QueryString and include it in the URI.
public async Task<string> GetResponse(string page, Dictionary<string, string> args) {
//assume page = "/user/login.json" and args == {"username", "user"},{"password", "pass"}
try {
QueryString queryString = QueryString.Create(args);
var uri = new Uri(page + queryString.ToString());
var request = new HttpRequestMessage(HttpMethod.Get, uri);
var response = await Client.SendAsync(request);
if(response.IsSuccessStatusCode){
return await response.Content.ReadAsStringAsync();
return string.Empty;
} catch { return string.Empty; }
}
Your code snippets don't show it, but are you sure the second query's URL has
$"/user/login.json?name={user}&password={pass}"
and not
$"/user/login.json"
?
I am busy developing a Xamarin forms application and have been struggling the last 2 days to get a successful post to my webApi from the application. Posting to the WebApi from the chrome Postman app works perfectly but I cannot seem to get it going from the application.
This is the webApi method that I am trying to call:
[HttpPost]
public string Authenticate(HttpRequestMessage request)
{
string JsonObj = request.Content.ReadAsStringAsync().Result;
AuthToken _authToken = JsonConvert.DeserializeObject<AuthToken>(JsonObj);
int UserID = DomainAuth.ValidateDomainAccount(_authToken.Username, _authToken.Password);
if(UserID > 0)
{
_authToken.UserID = UserID;
_authToken.Authenticated = true;
}
else
{
switch(UserID)
{
case -99:
_authToken.AuthMessage = "The entered domain account does not exist";
break;
case -98:
_authToken.AuthMessage = "The entered domain account does not have application access";
break;
case -97:
_authToken.AuthMessage = "Incorrect username and password combination";
break;
}
}
return JsonConvert.SerializeObject(_authToken);
}
I was originally trying to post to IIS Express but read on a post that this cannot be done when using an external device so the api is hosted in IIS now.
This is the method that my viewmodel command calls:
public async void Login()
{
Tuple<bool, string> AuthCheck = await _authService.AuthenticateDomainAccount(_username, _password);
if (AuthCheck.Item1) //Item1: True / False value indicating if the login was a success
{
Helpers.Settings.Username = _username;
await _navigationService.NavigateAsync("Synchronization");
}
else
{
FeedbackMsg = AuthCheck.Item2; // Item2: Message indicating why the login has failed.
}
}
And lastly this is the method that makes the api call:
public async Task<Tuple<bool, string>> AuthenticateDomainAccount(string _Username, string _Password)
{
AuthToken _authToken = new AuthToken { Username = _Username, Password = _Password };
HttpClientHandler handler = new HttpClientHandler();
using (var client = new HttpClient(handler, false))
{
client.BaseAddress = new Uri(BaseAddress);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
using (HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Post, "User/Authenticate"))
{
req.Content = new StringContent(JsonConvert.SerializeObject(_authToken), Encoding.UTF8, "application/json");
try
{
var response = await client.SendAsync(req);
Debug.WriteLine("################# HERE I AM ################");
var result = await response.Content.ReadAsStringAsync();
}
catch (Exception ex)
{
string a = "a";
}
}
}
return null;
}
When I step through the application it hits the SendAsync line, but instead of waiting for it to return as expected, it skips the lines below it and immediately returns null as per the final line of the method.
Can anyone shed some light on this?
I think the issue over here is with the Uri you are trying to hit. SendAsync method may be throwing a 404 because of incorrect Url.
Please change the following line:
using (HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Post, "User/Authenticate"))
to
using (HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Post, "api/User/Authenticate"))
assuming your BaseAddress ends with a '/' at the end, else it will be
using (HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Post, "/api/User/Authenticate"))
I am trying to embed PowerBI dashboards into my customer MVC portal. My customers don't have AAD accounts, so they can't login to Live when they come to the website, they log into my MVC website with individual authority.
I have registered my App on PowerBI/AAD and have the ClientID and Secret. I make the call to AAD and get an Authorization Code which I then use to get an Athentication Token which the is returned successfully.
When ever I use the access token to get a dashboard it is continually rejected with a 403 Forbidden.
I have gone through all the samples from Microsoft, but they require a user login prompt. I have reviewed the ADAL2.0 code which refers to the AcquireToken Method, but this was deprecated in ADAL3 and replaced with AcquireTokenAsync which has different parameters and I am using this in my example below.
Here is the function to get the token:
protected AuthenticationResult GetAccessToken()
{
string pBiUser = Properties.Settings.Default.PowerBIUser;
string pBiPwd = Properties.Settings.Default.PowerBIPwd;
string pBiClientId = Properties.Settings.Default.PowerBIClientId;
string pBiSecret = Properties.Settings.Default.PowerBIClientSecret;
TokenCache TC = new TokenCache();
ClientCredential CC = new ClientCredential(pBiClientId,pBiSecret);
string AU = Properties.Settings.Default.PowerBIAuthority;
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext authenticationContext
= new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext(AU, TC);
AuthenticationResult result = authenticationContext.AcquireTokenAsync("https://analysis.windows.net/powerbi/api"
,CC).Result;
if (result == null)
{
throw new InvalidOperationException("Failed to obtain the PowerBI token");
}
return result;
}
I then take the result token and call. The response receives the 403:
protected PBIDashboards GetDashboards(AuthenticationResult authResult)
{
PBIDashboards pbiDashboards = new PBIDashboards();
var baseAddress = new Uri("https://api.powerbi.com");
using (var httpClient = new System.Net.Http.HttpClient {BaseAddress = baseAddress})
{
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("authorization",
"Bearer " + authResult.AccessToken);
using (**var response** = httpClient.GetAsync("v1.0/myorg/dashboards").Result)
{
string responseData = response.Content.ReadAsStringAsync().Result;
//Deserialize JSON string
pbiDashboards = JsonConvert.DeserializeObject<PBIDashboards>(responseData);
if (pbiDashboards != null)
{
var gridViewDashboards = pbiDashboards.value.Select(dashboard => new
{
Id = dashboard.id,
DisplayName = dashboard.displayName,
EmbedUrl = dashboard.embedUrl
});
}
}
}
return pbiDashboards;
}
Based on the error message(403), the issue is relative to the permission.
And AFAIK the is no such permission we can use when we acquire the access token using the client credentials flow for the Power BI REST. You can refer the permission for the figure below:
To get the token for the Power BI REST without user interaction, we can use the Resource owner password credentials flow. And you can use the 3rd party library PowerBI.Api.Client which already implement this.
After a lot of research, you can make a direct AJAX call to get the token:
private async Task<string> GetAccessToken()
{
string pBiUser = Properties.Settings.Default.PowerBIUser;
string pBiPwd = Properties.Settings.Default.PowerBIPwd;
string pBiClientId = Properties.Settings.Default.PowerBIClientId;
string pBiSecret = Properties.Settings.Default.PowerBIClientSecret;
string pBITenant = Properties.Settings.Default.PowerBITenantId;
string tokenEndpointUri = "https://login.microsoftonline.com/"+pBITenant+"/oauth2/token";
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("grant_type", "password"),
new KeyValuePair<string, string>("username", pBiUser),
new KeyValuePair<string, string>("password", pBiPwd),
new KeyValuePair<string, string>("client_id", pBiClientId),
new KeyValuePair<string, string>("client_secret", pBiSecret),
new KeyValuePair<string, string>("resource", "https://analysis.windows.net/powerbi/api")
});
using (var client = new HttpClient())
{
HttpResponseMessage res = client.PostAsync(tokenEndpointUri, content).Result;
string json = await res.Content.ReadAsStringAsync();
AzureAdTokenResponse tokenRes = JsonConvert.DeserializeObject<AzureAdTokenResponse>(json);
return tokenRes.AccessToken;
}
}
Once you have the string AccessToken, you can then call the Dashboards request.
protected PBIDashboards GetDashboards(string token)
{
PBIDashboards pbiDashboards = new PBIDashboards();
var baseAddress = new Uri("https://api.powerbi.com");
using (var httpClient = new System.Net.Http.HttpClient {BaseAddress = baseAddress})
{
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("authorization",
"Bearer " + token);
using (var response = httpClient.GetAsync("v1.0/myorg/dashboards").Result)
{
string responseData = response.Content.ReadAsStringAsync().Result;
//Deserialize JSON string
pbiDashboards = JsonConvert.DeserializeObject<PBIDashboards>(responseData);
if (pbiDashboards != null)
{
var gridViewDashboards = pbiDashboards.value.Select(dashboard => new
{
Id = dashboard.id,
DisplayName = dashboard.displayName,
EmbedUrl = dashboard.embedUrl
});
}
}
}
return pbiDashboards;
}
This will provide you the list of dashboards and the dashboard Id to call the PowerBI API to build the embeded page in Javascript. I used hidden input fields to store the access token and embed URL to pass over to the Javascript call.
// check if the embed url was selected
var embedUrl = document.getElementById('embed').value;
if (embedUrl === "")
return;
// get the access token.
accessToken = document.getElementById('token').value;
// Embed configuration used to describe the what and how to embed.
// This object is used when calling powerbi.embed.
// You can find more information at https://github.com/Microsoft/PowerBI-JavaScript/wiki/Embed-Configuration-Details.
var config = {
type: 'dashboard',
accessToken: accessToken,
embedUrl: embedUrl
};
// Grab the reference to the div HTML element that will host the dashboard.
var dashboardContainer = document.getElementById('dashboard');
// Embed the dashboard and display it within the div container.
var dashboard = powerbi.embed(dashboardContainer, config);
I am attempting to connect to Cloudant (a couch-style DB) from a .Net MVC application. I am following the guidelines for consuming a web API using the HttpClient, as illustrated here:
http://www.asp.net/web-api/overview/web-api-clients/calling-a-web-api-from-a-net-client
I have two methods so far -- one to get a document and one to create a document -- and both have errors. The Get method returns Unauthorized and the Post method returns MethodNotAllowed.
The client is created like this:
private HttpClient CreateLdstnCouchClient()
{
// TODO: Consider using WebRequestHandler to set properties
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(_couchUrl);
// Accept JSON
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
return client;
}
The Get method is:
public override string GetDocumentJson(string id)
{
string url = "/" + id;
HttpResponseMessage response = new HttpResponseMessage();
string strContent = "";
using (var client = CreateLdstnCouchClient())
{
response = client.GetAsync(url).Result;
if (response.IsSuccessStatusCode)
{
strContent = response.Content.ReadAsStringAsync().Result;
}
else
{
// DEBUG
strContent = response.StatusCode.ToString();
LslTrace.Write("Failed to get data from couch");
}
}
return strContent;
}
The Post method is:
public override string CreateDocument(object serializableObject)
{
string url = CouchApi.CREATE_DOCUMENT_POST;
HttpResponseMessage response = new HttpResponseMessage();
string strContent = "";
using (var client = CreateLdstnCouchClient())
{
response = client.PostAsJsonAsync(url, serializableObject).Result;
strContent = response.Content.ReadAsStringAsync().Result;
}
if (response.IsSuccessStatusCode)
{
return strContent;
}
else
{
LslTrace.Write("{0} ({1})", (int)response.StatusCode, response.ReasonPhrase);
return response.StatusCode.ToString();
}
}
URLs are per the API documentation: https://username:password#username.cloudant.com.
I am very confused by what is going on and having a lot of trouble finding examples. Thanks for your help!
Thomas
With the HttpClient, you need to do the following to authenticate correctly (assuming you use basic auth):
HttpClientHandler handler = new HttpClientHandler();
handler.Credentials = new NetworkCredential(_userName, _password);
HttpClient client = new HttpClient(handler) {
BaseAddress = new Uri(_couchUrl)
};
You should not specify the username/password in the _couchUrl - HttpClient doesn't support that.
I can't see your implementation of PostAsJsonAsync or the complete Url your are building, but you can try inspecting / logging response.ReasonPhrase when an error occurs to get a hint as to what went wrong.