Can someone give me pointers on how to How to consume an external REST service from a MVC 4 web application? The services rely on an initial call with credentials base 64 encoded, then returns a token which is used for further web service queries.
I cannot find an easy primer on how to do this kind of thing, could someone help please?
I have all this working in classic ASP & JQuery but need to move over to an MVC 4 web application.
You could use the HttpClient class. Here's an example of how you could send a GET request and use Basic Authentication:
var client = new HttpClient();
client.BaseAddress = new Uri("http://foo.com");
var buffer = Encoding.ASCII.GetBytes("john:secret");
var authHeader = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(buffer));
client.DefaultRequestHeaders.Authorization = authHeader;
var response = client.GetAsync("/api/authenticate").Result;
if (response.IsSuccessStatusCode)
{
string responseBody = response.Content.ReadAsStringAsync().Result;
}
Once you have retrieved the access token you could make authenticated calls:
var client = new HttpClient();
client.BaseAddress = new Uri("http://foo.com");
string accessToken = ...
var authHeader = new AuthenticationHeaderValue("Bearar", accessToken);
client.DefaultRequestHeaders.Authorization = authHeader;
var response = client.GetAsync("/api/bar").Result;
if (response.IsSuccessStatusCode)
{
string responseBody = response.Content.ReadAsStringAsync().Result;
}
Related
One of my code is perfectly working in ASP.NET MVC 5, but when I am using almost same logic to call the API from ASP.NET Core 6 MVC, then the API always returns an error.
Same parameters and same address is being used in both technologies but the old approach is working.
Please see my old and new Logic to call this API, and guide me how to fix it.
The API Url is :
https://pcls1.craftyclicks.co.uk/json/rapidaddress?key=mykey&include_geocode=true&&postcode=DD68AB&response=data_formatted
ASP.NET MVC 5 - working fine:
using (var client = new HttpClient())
{
string addressUri = WebConfigurationManager.AppSettings["addressUri"];
var uri = addressUri + postcode.Replace(" ","") + "&response=data_formatted";
var response = await client.GetAsync(uri);
string textResult = await response.Content.ReadAsStringAsync();
lObjApiRes = JsonConvert.DeserializeObject<AddressLookupViewModel>(textResult);
}
ASP.NET Core 6 MVC - results in error:
using (HttpClient client = new HttpClient())
{
string addressUri = Configuration["CustomSettings:AddressUri"];
string uri = addressUri + postcode.Replace(" ", "") + "&response=data_formatted";
string textResult = "";
HttpRequestMessage request = new HttpRequestMessage()
{
RequestUri = new Uri(uri),
Method = HttpMethod.Get,
};
HttpResponseMessage response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
textResult = await response.Content.ReadAsStringAsync();
}
}
The steps are simple to reproduce the case. Just create an ASP.NET Core 6 MVC project and try my code.
Please guide me to resolve the issue in a better way.
Why I am always getting this error:
{ "error_code":"0001",
"error_msg": "No data was found for the requested postcode." }
Our SharePoint 2010 site is now using Azure AD Application Proxy for a single sign-on solution.
We have an Azure Web Job trying to access the SharePoint 2010 List.asmx service.
We added the List.asmx service as a Web Application to Azure AD.
We are able to get a bearer token for the List.asmx service.
var apiClientId = "XXXXXXXX-8ceb-452b-abec-b85a784fbfb5";
enter code herevar apiAppKey = "XXXXXXXXlx0ZJynQ8mmICeX454VrtOkxa0qqIZuKQY8=";
var apiResourceId = "https://sharepoint2010baseurl.com/_vti_bin/lists.asmx";
var tenant = "XXXXXXXX-debb-41a6-9c78-0516c185fa0d";
var aadInstance = "https://login.windows.net/{0}";
authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
var authContext = new AuthenticationContext(authority);
if (authContext.TokenCache.ReadItems().Count() > 0)
authContext = new AuthenticationContext(authContext.TokenCache.ReadItems().First().Authority);
var clientCredential = new ClientCredential(apiClientId, apiAppKey);
var authResult = await authContext.AcquireTokenAsync(apiResourceId, clientCredential);
Assert.IsNotNull(authResult.AccessToken);
After adding the bearer token to the RequestMessage Header of the SoapClient call, we get an error.
var soapClient = new ListsSoapClient();
soapClient.ClientCredentials.UserName.UserName = "BasicUserName";
soapClient.ClientCredentials.UserName.Password = "BasicPassword";
const string listName = "Announcements";
using (new OperationContextScope(soapClient.InnerChannel))
{
HttpRequestMessageProperty requestMessage = new HttpRequestMessageProperty();
requestMessage.Headers["Content-Type"] = "text/xml";
requestMessage.Headers[System.Net.HttpRequestHeader.Authorization] = "Bearer " + authResult.AccessToken;
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = requestMessage;
var query = new XElement("Query", "");
var viewFields = new XElement("ViewFields",
new XElement("FieldRef", new XAttribute("Name", "ID")),
new XElement("FieldRef", new XAttribute("Name", "Title")),
new XElement("FieldRef", new XAttribute("Name", "Body")),
);
var queryOptions = new XElement("QueryOptions", "");
var result = soapClient.GetListItems(listName, null, null, viewFields, null, null, null);
Assert.IsNotNull(result, "Results were null.");
}
The error message we are receiving is as follows:
The HTTP request is unauthorized with client authentication scheme 'Basic'. The authentication header received from the server was 'Bearer realm="XXXXXXXX-debb-41a6-9c78-0516c185fa0d",authorization_uri="https://login.microsoftonline.com/XXXXXXXX-debb-41a6-9c78-0516c185fa0d/oauth2/authorize",trusted_issuers="00000001-0000-0000-c000-000000000000#*",client_id="XXXXXXXX-f826-46d2-a221-ba9908427c8f"'.
The underlying question here is how do you call an asmx service with an Azure AD bearer token?
The tutorial for OneDrive for Business (here: https://dev.onedrive.com/auth/aad_oauth.htm)
However, I don't want the user to have to login as I'm building a web API - I want the app to login. So, I have followed the tutorial for service to service authentication (here: https://msdn.microsoft.com/en-us/library/azure/dn645543.aspx) which gets me an access token.
However, when I try to authenticate with the service I get an error saying "unsupported app only token". The code I'm using is below (btw, I'm using RestSharp):
public string GetAccessToken()
{
var client = new RestClient("https://login.microsoftonline.com/<tenant>/oauth2");
var request = new RestRequest("token", Method.POST);
request.AddParameter("grant_type", "client_credentials");
request.AddParameter("client_id", <client_id>);
request.AddParameter("client_secret", <client_secert);
request.AddParameter("resource", "https://<tenant>-my.sharepoint.com/");
var response = client.Execute(request);
var content = response.Content;
var authModel = JsonConvert.DeserializeObject<AuthResponseModel>(content);
return authModel.AccessToken;
}
this gets me the access token
This is how I try to access my drive:
public string GetDrive()
{
var accessToken = GetAccessToken();
var client = new RestClient("https://<tenant>-my.sharepoint.com/_api/v2.0/");
var request = new RestRequest("drive", Method.GET);
request.AddHeader("Authorization: Bearer", accessToken);
var response = client.Execute(request);
var content = response.Content;
return content;
}
Does anyone have any tips? This is getting slightly maddening.
I am using Web Api Token Based Authentication using OWIN Middleware; the token is generated successfully but i can't decode it; e.g. i cannot extract user name and password from it;
Here is my configuration
my start up code
var oAuthAuthorizationServerOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/api/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider()
};
// Token Generation
app.UseOAuthAuthorizationServer(oAuthAuthorizationServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
my code that is ued to send the token is
static async Task RunAsync(JObject token)
{
using (var client = new HttpClient())
{
client.Timeout = new TimeSpan(1000000000000);
client.BaseAddress = new Uri("http://localhost/SampleApp/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(token["token_type"].ToString(),
token["access_token"].ToString());
}}
my authetication code
var authenticationSchema = httpContext.Request.Headers["Authorization"];
if (!String.IsNullOrWhiteSpace(authenticationSchema))
authentication = AuthenticationHeaderValue.Parse(authenticationSchema);
if (authentication != null)
{
var unencoded = Convert.FromBase64String(authentication.Parameter);
var userpw = Encoding.GetEncoding("iso-8859- 1").GetString(unencoded);
var creds = userpw.Split(':');
return new Tuple<string, string>(creds[0], creds[1]);
}
and the code failed when trying to decode the code from base64 string
note:- my sample token is
3K8vHKHA2ZsKfKbvzUbo4a2sat2JLzvvyxCZ0KSD6s1wUS3t3oDPXuQ89aTmGpsG4ZL8O0cr8M9EUeZGtdM6FBwR7gLFcLZkTaimFGKyyZMNce9trQavVTzs6gam6qach1rPTLv_gIYGgPmM-401PZsr89BIXw4acTpJL3KbXs8y7PQ-o-eTV2IA8euCVkqC02iEnAzmS0SwhBouISCC-HvcNpE2aNixg4JXEt8EslU
you can see the attached for the exception
As far as I can see from the code, access token is sent plain to server; but you need to encode the access token on the client side like:
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue(token["token_type"].ToString(),
Convert.ToBase64String(Encoding.GetEncoding("iso-8859-1").GetBytes(token["access_token"].ToString())));
Then you can convert access token from base64 string on the server side. The access token string value you provided is not a valid Base64 string, so as expressed in the exception message.
We are using Embedded STS locally to test our ASP.Net web application. I'm creating a console app to call some of the WebAPI methods and to do some load testing on our app. I'd like to test using a set of users with certain permissions. For our local instance, that means authenticating against EmbeddedSTS.
How do I write an HttpClient to authenticate against EmbeddedSTS to receive this token and auth against my WebAPI endpoints?
Edit: bonus points if I can get the SAML Token while running the app in HTTP mode (not HTTPS).
I figured out how to do this.
Caveat: this is just used for a one off console app that lets us auth against EmbeddedSTS and do the WebAPI calls for stress testing purposes.
Essentially, we simulate what would happen on the browser. This uses the HttpClient and HtmlAgilityPack to parse through the HTML responses, select a user, POST it back to EmbeddedSTS, then POSTs the WS Fed token result and finally receives the FedAuth cookies. After that, the HTTP Client can be used to call any WebAPI or MVC pages in the app.
public static Task<HttpClient> BuildClient(string authurl, string username)
{
var task = Task.Run<HttpClient>(async () =>
{
// setup http client an cookie handler
var handler = new HttpClientHandler();
handler.AllowAutoRedirect = true;
handler.CookieContainer = new System.Net.CookieContainer();
handler.UseCookies = true;
var client = new HttpClient(handler);
client.MaxResponseContentBufferSize = 256000;
client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)");
client.DefaultRequestHeaders.Add("Connection", "Keep-Alive");
client.DefaultRequestHeaders.ExpectContinue = false;
// this is the html of the page that has the user dropdown
var userSelectionPage = await client.GetStringAsync(authurl);
string actionPathAndQuery = GetAction(userSelectionPage);
// for the purposes of this sample, we just choose the user called admin
var postData = new List<KeyValuePair<string, string>>() {
new KeyValuePair<string, string>("username", username)
};
// now we post the user name and expect to get the ws fed response
var wsfedresponse = await client.PostAsync(authurl + actionPathAndQuery, new FormUrlEncodedContent(postData));
var wsfedcontent = await wsfedresponse.Content.ReadAsStringAsync();
var namevaluepairs = GetHiddenInputNameValues(wsfedcontent);
var finalpost = await client.PostAsync(authurl, new FormUrlEncodedContent(namevaluepairs));
// at this point, the fedauth cookie is set, we are good to go
return client;
});
return task;
}
private static string GetAction(string htmlContent)
{
var d = new HtmlDocument();
d.LoadHtml(htmlContent);
var node = d.DocumentNode.SelectSingleNode("//form[#action]");
var result = node.GetAttributeValue("action", string.Empty);
return result;
}
private static IEnumerable<KeyValuePair<string, string>> GetHiddenInputNameValues(string htmlContent)
{
var d = new HtmlDocument();
d.LoadHtml(htmlContent);
var nodes = d.DocumentNode.SelectNodes("//input[#type='hidden']");
return nodes.Select(p =>
new KeyValuePair<string, string>(
p.GetAttributeValue("name", string.Empty),
System.Web.HttpUtility.HtmlDecode(p.GetAttributeValue("value", string.Empty))
));
}
EmbeddedSts does ws-federation. This is not designed for web Apis. You rather want Oauth2.