Get AngularJS to talk to .NET Web API secured with Azure AD - asp.net

I have two different web projects on Microsoft Azure. One project is a .NET MVC web application and the other project is a .NET Web API.
Both projects are configured to use Azure AD. The MVC web application is able to get a token and use it to make requests against the Web API. Here's sample code from the MVC web app.
string userObjectID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
AuthenticationContext authContext = new AuthenticationContext(Startup.Authority, new NaiveSessionCache(userObjectID));
ClientCredential credential = new ClientCredential(clientId, appKey);
result = authContext.AcquireTokenSilent(todoListResourceId, credential, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
// Make a call against the Web Api
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, webApiBaseAddress + "/api/list");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
HttpResponseMessage response = await client.SendAsync(request);
So this code works just fine. However, what I need to do now is call the Web API directly from an AngularJS application. When I try to do that, I get a 401 unauthorized error.
The way I am doing this is by adding a header to the HTTP GET request sent by AngularJS. I'm setting "Bearer" to the result.AccessToken value that I am passing to the page from my MVC application (code above).
Obviously this doesn't work. I suppose now my question is what are my options? Is there an official or better way to do this? Let's say I wanted to make calls to the Web API from standard JavaScript (lets forget the complexities of AngularJS). Is there a way to authenticate with Azure AD?

the canonical way of obtaining a token for an in-browser JS application would be to use the OAuth2 implicit flow. Azure AD does not currently expose that flow, but stay tuned: we are working on enabling the scenario. No dates to share yet.
HTH!
V.

The work I mentioned in the older answer finally hit the preview stage. Please take a look at http://www.cloudidentity.com/blog/2014/10/28/adal-javascript-and-angularjs-deep-dive/ - that should solve precisely the scenario you described. If you have feedback on the library please do let us know!
Thanks
V.

Related

Is it possible to restrict access to REST API built using Web API 2?

I built a REST API using ASP.NET Web API 2, so I could deliver data from a backend database to my applications running on any platform (mobile, web, desktop etc) However up until now, I simply call the website with the controller I need data from and that's it, it sends back the JSON string in the response.
But, the data is kind of special, and there is nothing to prevent another developer from simply calling the controllers and getting back the exact same data and building their own application around it.
My question is - is there anyway to restrict access to the API so that only my applications can get valid response from the server. (i.e. prevent other developers from using my REST API)
I already read these documentation Security, Authentication, and Authorization in ASP.NET Web API I'm just not sure which of these scenarios apply to me, or if any will do what I am asking.
EDIT - Another piece of info, my web service is running on Azure in case it is relevant.
Did you happen to check token based authentication?Please go through https://stackoverflow.com/a/38670221/4868839 and https://www.youtube.com/watch?v=rMA69bVv0U8 must be a good to start with.
there are different way to validate your web api.
Authentication Filters in ASP.NET Web API 2
using you can customise your authentication filter
you can refer sample Reference link
Token Based Authentication using ASP.NET Web API 2, Owin, and Identity
//App_Start/Startup class
public void ConfigureAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/v1/accesstoken"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(AppConfiguration.AccessTokenExpireDuration),
Provider = new SampleOAuthProvider() // class that override your method
};
// Token Generation
app.UseOAuthBearerTokens(OAuthServerOptions);
}
You can find reference from inherits Default implementation of IOAuthAuthorizationServerProvider used by Authorization
i hope it sholud helps you thanks.

Client application login using httprequest

I am a bit new to ASP.NET and web development, and I am still confused about the following :
On the one hand I have a very complete ASP.NET MVC website based on NopCommerce that includes login, registration, e-commerce features, forums, etc.
On the other hand, I have a Windows Forms client application that needs to read and write data from and to my website database.
The first thing I would need to do is to allow users to login in the client application by sending a request to the server. I've been looking around the web for days and I can't manage to find a precise and secure way to do so.
I'm pretty much sure that I have to use System.Net.Http to make a request from the client. Will this request then got to be handled by a MVC controller action ? Maybe an already existing one ?
Here is the method I have so far, based on a tutorial found online (it is not complete at all) :
private static async void PostRequest(string addressPost)
{
IEnumerable<KeyValuePair<string, string>> queries = new List<KeyValuePair<string, string>>()
{
new KeyValuePair<string, string>("Query1", "Email"),
new KeyValuePair<string, string>("Query2", "Password")
};
HttpContent formContent = new FormUrlEncodedContent(queries);
using (HttpClient client = new HttpClient())
{
using (HttpResponseMessage response = await client.PostAsync(addressPost, formContent))
{
using (HttpContent content = response.Content)
{
string myContent = await content.ReadAsStringAsync();
System.Diagnostics.Debug.WriteLine(myContent);
}
}
}
}
Any existing example or help would be greatly appreciated.
first we need to focus on the architecture of the application, we have here two applications, 1) web application, 2) WinForm application. and you want to share the same db for both, here are the drawbacks of doing so, you might found yourself one day your winform will lock a table because of updating etc, and your web application will lose access, thats not a good idea,
here is how i would do it.
create a web api plugin for your web application, and use api tokens for security, there is some available web api plugin for nopcommerce but its limited in functionality, so i guess you will have to add some methods based on your needs, next thing you will do is have your winform application communicate with your webapi, in that case your winform works independently and secure,
as a side note, you can have in your web api multiple tokens for each user if you want, you can manage that in your web api plugin, just make a table where you will store that info with user info and tokens for everyone and you can manage that from the web admin.

Implement Office 365 styled Basic Authentication (Active Profile)

I'm working on a SaaS application built around ASP.net MVC & WebAPI and want to make it easy for enterprises to use my service. Example would be Office 365 Basic Authentication (Active Profile) where the user enters his username/password on microsoft's site (or desktop app) and he is authenticated against his employer's Active Directory. My understanding so far is that I would need to create a RP-STS which will accept credentials and then forward those to AD FS Proxy running on the client company's AD server. Is this correct?
If yes, then how do I implement this? Setting up AD server adding a Relying Party and AD FS Proxy Role is easy, so that's really not an issue. I just need to figure out how to create/setup RP-STS service and any other steps involved in this process. There just isn't an example/tutorial of this in .net
I believe this msdn blog post describes exactly what you're asking for. It has a complete walkthrough of the entire process, including creating an RP by creating a normal WCF service, and then use the provided utility to configure the service to trust your ADFS.
http://blogs.msdn.com/b/mcsuksoldev/archive/2011/08/17/federated-security-how-to-setup-and-call-a-wcf-service-secured-by-adfs-2-0.aspx
Edit:
This code, taken from the linked article (comments are mine), is a demonstration of active federation. The client application is manually retrieving a security token from the ADFS. Passive Federation would involve forwarding the user to a secure web page in which they could send their credentials directly to the ADFS. The major benefit of Passive Federation is that the end user's secret credentials are provided directly to the ADFS, and the RP's client side code never has access to it.
var requestTokenResponse = new RequestSecurityTokenResponse();
//The line below is the 'Active' federation
var token = Token.GetToken(#"mydomain\testuser", "p#ssw0rd", "http://services.testdomain.dev/wcfservice/Service.svc", out requestTokenResponse);
var wcfClient = new FederatedWCFClient<MyTestService.IService>(token, "WS2007FederationHttpBinding_IService"); // This must match the app.config
var client = wcfClient.Client as MyTestService.IService;
var result = client.GetData();
Console.WriteLine(result);
wcfClient.Close();
Take a look at these links:
https://github.com/OfficeDev/O365-WebApp-SingleTenant
https://github.com/OfficeDev/O365-WebApp-MultiTenant
It shows how to make an application using the office 365 api to authenticate and authorize the users.
Be aware about Single Tenant and Mult Tentant application, and choose the right one.
It's really easy to do that, I've done it couple months ago.
I found the answer on the blog: http://leandrob.com/2012/04/requesting-a-token-from-adfs-2-0-using-ws-trust-with-username-and-password/
What this code essentially does is that it directly authenticates with the tenant's ADFS endpoint and gets a token as well. That's what I was looking for.
var stsEndpoint = "https://[server]/adfs/services/trust/13/UsernameMixed";
var relayPartyUri = "https://localhost:8080/WebApp";
var factory = new WSTrustChannelFactory(
new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential),
new EndpointAddress(stsEndpoint));
factory.TrustVersion = TrustVersion.WSTrust13;
// Username and Password here...
factory.Credentials.UserName.UserName = user;
factory.Credentials.UserName.Password = password;
var rst = new RequestSecurityToken
{
RequestType = RequestTypes.Issue,
AppliesTo = new EndpointAddress(relayPartyUri),
KeyType = KeyTypes.Bearer,
};
var channel = factory.CreateChannel();
SecurityToken token = channel.Issue(rst);
Another good article on that blog is: http://leandrob.com/2012/02/request-a-token-from-adfs-using-ws-trust-from-ios-objective-c-iphone-ipad-android-java-node-js-or-any-platform-or-language/ - which covers other similar scenarios.

Do I need to do something special in the ASP.NET MVC app to read a Json response from a Web API 2 application?

Is there something special I need to define in an ASP.NET MVC application to read an incoming response from a ASP.NET Web API?
From my MVC app, I make a request to an ASP.NET Web API using System.Net.HttpClient. The API receives the request and processes it fine and returns a valid response. However, the MVC application, it appears, never gets the response. I have a break point on the line that makes the request. The flow of control never comes back after executing that line. The MVC app just keeps waiting and times-out after a very long time.
However, I can confirm that the API returns a valid Json response. I have tried composing this request in Chrome Postman and see that the API returns a valid response.
Here's the code from my MVC app that makes the request to the Web API:
public async Task<R> PostAsJsonAsync<T, R>(string uri, T value)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(_baseUri);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.PostAsJsonAsync(uri, value);
if (response.IsSuccessStatusCode) return await response.Content.ReadAsAsync<R>();
else return default(R);
}
}
In the past, i.e. before Web API 2, I've had MVC apps talk to the Web API without any problem. I don't know if I am missing something that has been introduced in Web API 2.
I have a feeling you are getting a deadlock. Are you using .Result anywhere? You should be using async all the way. I mean your MVC action method should also be async method and they should await and not use .Result. Read this log post by Stephen Cleary for more info. http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

ASP.NET MVC with Forms Auth and WebApi with Basic Auth

I have a WebApi using Basic Auth nicely. And I have an MVC site using Forms Auth nicely. But here's the catch:
Client X has a dedicated database with any number of Contacts and Products. The MVC site is a dedicated site for them (via {clientId} routing), which allows their Contacts to log in (via Forms Auth) and place orders for their products. The Contact must be Form-ly logged in to place an order.
The product orders (need to) hit the WebApi to be recorded in the Client's database.
But since the WebApi uses Basic Auth to validate the Client, not the Contacts who placed the orders, every request comes back is 401 - Unauthorized.
I've checked out ThinkTecture as suggested by a number of posts here on SO, however it doesn't get me what I need because I'm not looking to allow Forms Auth in the WebApi. I don't want to authenticate the Contact from the Client's database in the WebApi, I want to authenticate the Client in the WebApi.
Has anyone come across a similar scenario and am I missing something glaringly obvious? Perhaps I need to implement both Forms and Basic on the site?
The very standard Api call I'm making from the site (where the UserName and Password are the Client's, not the Contact's):
var clientId = new Guid(RouteData.Values["clientId"].ToString());
var baseUrl = ConfigurationManager.AppSettings["ApiBaseAddress"];
var authHeader = Convert.ToBase64String(Encoding.ASCII.GetBytes(String.Format("{0}:{1}", _shoppingCartSettings.UserName, _shoppingCartSettings.Password)));
var requestUrl = String.Format("api/{0}/inventory", clientId.ToString());
var httpWebRequest = WebRequest.Create(baseUrl + requestUrl);
httpWebRequest.Headers.Add(HttpRequestHeader.Authorization, "Basic " + authHeader);
httpWebRequest.Method = "GET";
httpWebRequest.Accept = "application/json";
httpWebRequest.ContentType = "application/json";
try
{
using (var httpWebResponse = httpWebRequest.GetResponse())
{
// we never get here because of a 401
}
}
catch (WebException ex)
{
using (var httpWebResponse = ex.Response)
{
// we always get here
}
}
If I set up a separate test client and make the same call, it works great :/
Is your Web API under the same virtual directory and configuration as the MVC site? It looks like the Forms Auth HTTP module kicks in for your API, which you don't want. As long as you don't plan to call the API directly from the browser, move it to a separate virtual directory that is set up exclusively for basic auth, no forms auth module in the web.config for the API.
Why not have one login for your MVC site that has the ability to submit orders for every Client? It makes sense for your WebAPI to only allow Clients to submit orders for themselves. But I don't think it makes sense to have your MVC site authenticate as different Clients based on the Contact. Your MVC site would have to store the passwords for each Client.
Instead, create one login for the MVC site and give it the ability to submit an order for any Client.
After much banging of head against the not-so-proverbial wall, and a much needed shove by #0leg, I've discovered the cause.
In the Properties of my WebApi project file under Web > Servers, the Visual Studio Development Server was being used with a Virtual Path of "/", whereas my MVC project file was set up to use the Local IIS Web Server. The MVC project also had the Apply server settings to all users (store in project file) option checked.
Setting both to use the local IIS server resolved it.
Upon further contemplation, this now seems logical since they were essentially running on different servers.
Posting this for posterity's sake.

Resources