Authentication / Authorization MVC 5 and Web API - Katana / Owin

I'm having trouble trying to choose a route to take on the project that I have.

I read about the OWIN specifications and Katana implementation in .NET. The reason I would like to go with the Katana route is owin components related to ADFS and Token / Cookie generation.

I have two projects: one for the MVC 5 website and one for the web API. In the future, they may consist of two separate servers, but so far they are on the same level.

I know that I will use IIS, so I do not need to explore the Owin pipeline.

The requirements that I have are that there will be users who will be registered in ADFS, and other users who will be registered using Token / Cookie generation, with Role / Membership providers. Based on who is authenticated, some sections of my web page will be shown. Web page promotion is carried out in a razor.

Does anyone have any material that I can read to help explain the flow of design I can take? Or did someone make a project similar to what I'm worried about, which might add any advice? There are many scattered documents that describe specific things that I need, but not a big picture; for example, only about WebAPI and ADFS or WebAPI and azure windows, etc. etc.

My theory is to implement authentication / authorization in the MVC5 website project, authorization in the web API (somehow there must be a connection between the two). Then can I create a copy of the project for ADFS and another copy for Token / cookie authentication? Or maybe I will need to do 4 different types of authentication: 2 for adfs, where I authenticate to the MVC5 website and web API, and again 2 more to generate tokens / cookies.

Any suggestions would be helpful as I am not very familiar with such technologies.

+7
asp.net-web-api owin katana adfs
source share
2 answers

I can suggest that the WsFederation parameter in OWIN is good, but requires cookies ... and they are a different kind of cookie than local auth with cookies. ADFS 2.0 / WsFederation uses AuthenticationType = "Cookies", and local authentication uses AuthenticationType = "ApplicationCookie". As far as I know, they are apparently incompatible. I think you will have to use tokens for ADFS, but I believe that ADFS 3.0 requires 2012R2. Use OWIN OAuth for this.

UPDATE: after some time, I figured out how to make these two types of authentication coexist peacefully in the same web application. Using OWIN, configure the CallCookieAuthentication TWICE call once to enable the new WsFederationAuthentication middleware and enable local cookie authentication again. This is not intuitive, but behind the scenes, by specifying different types of authentication for each, they set them up as different “engines”. Here's what it looks like in my startup:

app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, LoginPath = new PathString("/Account/Login"), Provider = new CookieAuthenticationProvider { OnResponseSignIn = ctx => { ctx.Identity = TransformClaims(ctx, app); } } }); app.UseCookieAuthentication(new CookieAuthenticationOptions { Provider = new CookieAuthenticationProvider { OnResponseSignIn = ctx => { ctx.Identity = TransformClaims(ctx, app); } } }); app.UseWsFederationAuthentication(new WsFederationAuthenticationOptions { Wtrealm = Realm, MetadataAddress = Metadata, Caption = "Active Directory", SignInAsAuthenticationType = CookieAuthenticationDefaults.AuthenticationType }); 

This allows users to authenticate both in local SQL tables and in ADFS 2.0. The TransformClaims callout allows me to normalize my claims between the two providers so that they are consistent.

EDIT: Here are very rudimentary TransformClaims. You can do many things inside this: get a user from your database, configure navigation requirements, user permissions, role collections, whatever. I just built this smaller version from a much larger implementation, so I did not run it, but I hope you get an idea of ​​how to use the OnResponseSignIn event.

 private static ClaimsIdentity TransformClaims(CookieResponseSignInContext ctx, IAppBuilder app) { var ident = ctx.Identity; var claimEmail = ident.Claims.SingleOrDefault(c => c.Type == ClaimTypes.Email); var claimName = ident.Claims.SingleOrDefault(c => c.Type == ClaimTypes.Name); //normalize my string identifier var loginString = (claimEmail != null) ? claimEmail.Value : (claimName != null) ? claimName.Value : null; var efctx = ctx.OwinContext.Get<DBEntities>(); var user = UserBL.GetActiveUserByEmailOrName(efctx, loginString); if (user == null) { //user was auth'd by ADFS but hasn't been auth'd by this app ident.AddClaim(new Claim(ClaimTypesCustom.Unauthorized, "true")); return ident; } if (ident.Claims.First().Issuer == "LOCAL AUTHORITY") { //Local //local already has claim type "Name" //local didn't have claim type "Email" - adding it ident.AddClaim(new Claim(ClaimTypes.Email, user.Email)); } else { //ADFS //ADFS already has claim type "Email" //ADFS didn't have claim type "Name" - adding it ident.SetClaim(ClaimTypes.Name, user.UserName); } //now ident has "Name" and "Email", regardless of where it came from return ident; } 
+8
source share

Cmedine, based on Brett's answer, I set up my authentication and authorization. I will show you the code when you requested some sample code.

 public partial class Startup { public void ConfigureAuth(IAppBuilder app) { app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType); app.UseCookieAuthentication(new CookieAuthenticationOptions { SlidingExpiration = false, CookieName = "identity", //short time for testing only ExpireTimeSpan = TimeSpan.FromSeconds(120), Provider = new CookieAuthenticationProvider { OnResponseSignIn = ctx => { ctx.Identity = TransformClaims(ctx); } } }); app.UseWsFederationAuthentication( new WsFederationAuthenticationOptions { MetadataAddress = "https://[[ADFS_Url]]/FederationMetadata/2007-06/federationmetadata.xml", Wtrealm = "[[realm]]", UseTokenLifetime = false } ); } private ClaimsIdentity TransformClaims(CookieResponseSignInContext ctx) { return new IdentityCreator().CreateIdentity(ctx.Identity, [[ApplicationName]]); } } 

IdentityCreator takes the name ClaimsIdentity and the name of the application and goes to the database and receives applications for this user in this application. Hope this helps!

+1
source share

All Articles