.NET WebApi Authentication - asp.net-mvc

Currently, I have an MVC web application that sells widgets. A user logs into our system using forms authentication, and can then do various functions based on the group they belong to(ie Place an order, View an Order, Cancel an Order, etc).
We've been tasked with writing an Api that will give third parties the ability to create and view orders in our system. Each third party will have it's own username and will be limited to certain api methods based upon the group they belong to.
We are looking at using Web Api as a mechanism to provide the api. We would also like to be able to consume this api from our MVC web application. Unfortunately, we are running into issues with Authentication for the Web Api. Using a DelegatingHandler, we have implemented Basic Authentication over SSL for our WebApi. This works great for our third parties. However, when trying to consume the Api from our MVC application we are getting 401 access denied errors because the user was authenticated in the MVC app using Forms authentication, but we have no way of passing those credentials on to the Web Api. Is there a way to pass the Forms Auth credentials from our MVC app to our Web api app?
IIS Setup
WebSite named WidgetStore with two web applications
WidgetStore\UI -uses forms authentication
WidgetStore\Api - uses basic authentication

Is there a way to pass the Forms Auth credentials from our MVC app to our Web api app?
Sure, let's take for example the following MVC controller action calling the Web API:
[Authorize]
public ActionResult CallWebApi()
{
var baseAddress = new Uri("https://example.com");
var cookieContainer = new CookieContainer();
using (var handler = new HttpClientHandler() { CookieContainer = cookieContainer })
using (var client = new HttpClient(handler) { BaseAddress = baseAddress })
{
var authCookie = Request.Cookies[FormsAuthentication.FormsCookieName].Value;
cookieContainer.Add(baseAddress, new Cookie(FormsAuthentication.FormsCookieName, authCookie));
var result = client.GetAsync("/api/values").Result;
result.EnsureSuccessStatusCode();
// now you can read the result.Content ...
}
}
This assumes that you have also enabled forms authentication in the web.config of your Web API project and that the cookie name is the same as the one used in your MVC project.

Related

ASP.NET MVC with OpenIDConnect authentication fails with multiple instances of the service running

We have MVC Web Application with AnjularJS integrated (legacy product) and deployed as a Web Application. We expose ApiControllers for Ajax calls from the AngularJS script .We have multiple instances of this application running behind a load balancer. We recently integrated Azure AD for authentication and use OpenIDConnect flow. User login succeeds but subsequent requests (Ajax calls) to the ApiControllers fail randomly with 'Unauthorized' error.
OpenIDCOnnect Authentication setup
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = "XXXXX",
Authority = "https://login.microsoftonline.com/xxxx.onmicrosoft.com",
PostLogoutRedirectUri = #"https://localhost:44360/",
});
}
Then each MVC and API controller is annotated with Authorize attribute
[Authorize]
public class HomeController : Controller
Our Analysis
We suspect this issue might be because of the application running multiple instances and during login the session would be established with that particular instance. Subsequent requests are going to other instances randomly and the instance does not have the session information. When we deploy only one instance of the application, this error is not seen.
Questions
When I read online about Azure AD, the recommended way for Ajax calls is to use ADAL.js implicit flow. Since ours is not a purely single page application, I am not sure if this would be a valid choice. We want to see if we can solve this by sticking to OpenIDConnect.
Is there a way to store this session information in a distributed store so that multiple instances of the application can share it? I see interface IAuthenticationSessionStore in CookieAuthenticationOptions which can be used but not finding enough information on its purpose.
If this is a JS app using the MVC app to service API/AJAX requests then the MVC app should not use cookies at all. It should just validate the id_token passed from the JS client in the Authorization header. Your app is failing probably because you are trying to use cookies, encrypting them on one server and then trying to use them on another. Have a look here: https://learn.microsoft.com/en-us/azure/active-directory/develop/sample-v1-code#single-page-applications, particularly the source code for the Startup.Auth.cs class: https://github.com/Azure-Samples/active-directory-angularjs-singlepageapp/blob/master/TodoSPA/App_Start/Startup.Auth.cs.
If you are mixing SPA (JS) paradigm with serving regular HTML then you have will have to figure out how to use both cookies and tokens. Tricky and insecure.

Accessing WCF service from mobile client with user idenity

I'm working on an Web API that have a web (ASP.NET MVC), WCF service and mobile interfaces (Android/ iPhone) and i have token authentication for API.
The MVC and API have user identity to verify the users, but WCF service won't have this user identity.
Here, i need to have access the WCF service from the mobile client with security.
Application structure,
So, How can verify or share the user identity with WCF service?
Can i use the same OAuth token in WCF service to identify the user? or Is there any other standard way to do it ?
With given high level detail, ADFS should be a right fitment for your requirement, with that Identity validation can be achieved for WCF based services as well.
Refer below link to MSDN guide for a detailed approach.
A Guide to Claims-Based Identity and Access Control
However, I presume this solution for new requirements, if so, why still WCF service is required when Web API can be manage external http requests? and given that there is no DB interaction through WCF services as per diagram.
I have a similar application. I added the token (previously received after authentication) using this:
Common ajax call with jQuery:
$.ajax({
beforeSend: function (request)
{
request.setRequestHeader("Authority", authorizationToken);
},
// Below you set type, url, data, ...
});
Here, using Cordova + Ionic + Angularjs:
$http.defaults.headers.common.Authorization = authorizationToken;
if ($http.defaults.headers.common.Authorization.Parameter != undefined) {
$http.defaults.headers.common.Authorization.Parameter = authorizationToken;
}
Hope it helps.

How to integrate Azure AD Authentication with a Web API service whose service reference has been added automatically?

I have an ASP.NET MVC website in which I have added a "Web API 2 OData Controller with Actions, using Entity Framework".
This is the 1st set of code that is auto-generated.
I am calling this web API from a native client. I have added a reference to the Web API service through, Right Click, Add References.
This is the 2nd bit that is auto-generated.
I've configured Azure AD authentication at the client side. This is working.
What I want to do now is: setup authentication for each Web API call based on the user who logged in from the client. So the client's access token needs to be passed from the client to the Web API, and this token should be used to authenticate further.
Note that a lot of the code is auto-generated. So the additions to code should have minimal effect on regeneration of the code, if possible.
Later on the Web API will use the user information to filter data based on his identity, and use role based identity as well.
Any pointers on how to start with this? I feel that all the various pieces are available, but how to gather them into a single solution is just out of grasp.
The container that is part of the auto-generated solution is where we need to pass the token.
Here's the code:
autogenContainer.BuildingRequest += (sender, args) =>
{
args.Headers.Add("Authorization", "Bearer " + access token retrieved from Azure);
};

Single Page app using Controller - how to secure with ASP.NET Identity?

I have a single page app that uses a standard Controller (not ApiController) for retrieving all HTML views, which is done via ajax. However, WebApi is utilized using breezejs for the client to talk to the backend database. I am implementing ASP.NET identity security - should I use MVC cookie authentication or bearer token? I need the solution to illustrate a separate login page, and need a clean server side redirect.
Disclaimer
This is a relatively trivial question because it is very specific and by understanding the difference in authentication between Web API and MVC Controllers this should be fairly straight forward.
Assumptions
Your Web API Project has it's own authentication and does not talk to the MVC project to get a session user or anything
Your ASP.NET MVC Controllers are in a project using forms authentication and storing the user in a session cookie.
When I reference MVC below you undertand these are referencing ASP.NET MVC
Recommendation
What I would do is have your MVC project use OAuth for authentication and store the user in a cookie in the session that you can set and get. Then your controller actions that serve views can be decorated with the Authorize attribute. This will redirect users to the login page when they try to access a view they are not allowed to (as long as that is set up in your web.config
For the Web API Project you can't rely on Session because it sounds like you are decoupling the two projects. This is my recommendation -
When your user is successfully authenticated in your MVC Project make a request to the Web API to an open log in method. This would do some logical test and then either store the user in the DB with a session token of some sort or automatically write the user to the DB.
Now your user that is stored in session in your MVC project you can pass that down to the client and append it to the Breeze calls to your Web API and use that for authentication. You will need to explicitly set up how long that token is for and such but it is pretty easy to append this to the Breeze.js call like such -
var query = breeze.EntityQuery.from('myService').withParameters({'tokenId': thisTokenId});
Now your queries will hit the API with a tokenId parameter that it can use for authentication.
Edit
If you want to set up your ASP.NET MVC Project to use OAuth you can following along with this link -
http://www.asp.net/mvc/tutorials/security/using-oauth-providers-with-mvc
Remember that forms based authentication just means (in a nutshell) that you will provide the user some way of logging in with a form of some sort.

Active and Passive Federation in WIF

I am trying to understand the difference between Active and Passive federation in WIF. It appears that one would use an Active Federation if the Relying Party (RP) is a WCF Service instead of an ASP.NET application and a Passive Federation if the RP is an ASP.NET application. Is this accurate?
So, in a scenario in which an ASP.NET application uses a WCF in the backend, the MS articles suggest using a 'bootstrap' security token that is obtained by the ASP.NET app using an ActAs STS and this token is used to authenticate with the WCF. In this scenario, it appears that we are doing a combination of Active (user -> STS -> ASP.NET RP) and Passive (ASP.NET -> ActAs STS -> WCF) Federation?
Active Federation is about authenticating user using WSTrust protocols and your Relying Party is who owns login window and asks for security token to STS.
Passive Federation is when Relying Party has no login logic and you are redirected to the login page located on STS. Active Federation is more complex to configure, in my opinion (I'm working with silverlight, so it needs some tricks). I'm planing to post about this subject on my blog, because there is little information about it on internet.
In short, Passive Federation is just a phrase used to represent the scenario that your browser is redirected to a login page hosted by the STS. After login the STS redirects you back to the referring URL with some cookie, or something, and you are authenticated at the site that trusts the STS (using thumbprints, certs, encryption,etc).
You don't have to do it that way either. I for example like my ASP.NET sites to actively contact the STS using credentials supplied by the user, but it means the ASP.NET app pool has to authenticate at the STS using Windows Auth in order to send the credentials supplied by the user to get a token, and then I explicitly add the token to the session. In other words I don't used Passive Federation, but that's just a choice.
You can read more about passive claims here:
http://garymcallisteronline.blogspot.co.uk/2012/11/claims-explained.html
An Active call is a direct call to a WSActive endpoint (these support many authentication types).. The following code shows an active call using the username active endpoint.
private static GenericXmlSecurityToken GetToken(string username, string password, string url, string audienceUrl)
{
var factory = new WSTrustChannelFactory(new Microsoft.IdentityModel.Protocols.WSTrust.Bindings.UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential), new EndpointAddress(url));
factory.Credentials.UserName.UserName = username;
factory.Credentials.UserName.Password = password;
factory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
factory.TrustVersion = TrustVersion.WSTrust13;
WSTrustChannel channel = null;
var rst = new RequestSecurityToken
{
RequestType = WSTrust13Constants.RequestTypes.Issue,
AppliesTo = new EndpointAddress(audienceUrl),
KeyType = WSTrust13Constants.KeyTypes.Bearer,
};
channel = (WSTrustChannel)factory.CreateChannel();
return channel.Issue(rst) as GenericXmlSecurityToken;
}
Even i had same problem initially but the this blog helped me a lot.
i would suggest you to go through samples first and then analyse the documentation.
WCF federation is tricky though.

Resources