I am in process of setting up my 1st MVC Web App. I know I need to provide a Forms Based Authentication model as well as I know I will be reusing it for multiple other internal web apps as well.
All the documentation for MVC 5 Authentication, which I believe is all based on OWIN stuff, have it "baked" into a single web app using EF Code First no less.
What I am trying is to have an another Web App that I strip everything out of except for the Account stuff and then try to "point" my web apps Authentication to that and have it return a "token", I'm guessing, of my Authenticated User and his/her "Roles".
Am I on the right track? Am I WAY over complicating this? I'm new to Web Development but this seems like a fairly reasonable and straightforward request. Dumbfounded that I can't find it anywhere.
Work with new OWIN Identity API that wraps everything that you need to work with Application and External sign in cookies like bellow:
public class IdentityAuthenticationManager
{
public IdentityAuthenticationManager();
public IdentityAuthenticationManager(IdentityStoreManager storeManager);
public string ClaimsIssuer { get; set; }
public string RoleClaimType { get; set; }
public IdentityStoreManager StoreManager { get; set; }
public string UserIdClaimType { get; set; }
public string UserNameClaimType { get; set; }
public virtual void Challenge(HttpContextBase context, string authenticationType, string redirectUrl);
public virtual Task<bool> CheckPasswordAndSignIn(HttpContextBase context, string userName, string password, bool isPersistent);
public virtual Task<bool> CreateAndSignInExternalUser(HttpContextBase context, string loginProvider, IUser user);
public virtual IEnumerable<Microsoft.Owin.Security.AuthenticationDescription> GetExternalAuthenticationTypes(HttpContextBase context);
public virtual Task<ClaimsIdentity> GetExternalIdentity(HttpContextBase context);
public virtual Task<IList<Claim>> GetUserIdentityClaims(string userId, IEnumerable<Claim> claims);
public virtual Task<bool> LinkExternalIdentity(ClaimsIdentity id, string userId, string loginProvider);
public virtual Task SignIn(HttpContextBase context, string userId, bool isPersistent);
public virtual Task SignIn(HttpContextBase context, string userId, IEnumerable<Claim> claims, bool isPersistent);
public virtual Task<bool> SignInExternalIdentity(HttpContextBase context, ClaimsIdentity id, string loginProvider);
public virtual void SignOut(HttpContextBase context);
public virtual bool VerifyExternalIdentity(ClaimsIdentity id, string loginProvider);
}
And The following shows the login code for the ASP.NET MVC template:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (ModelState.IsValid)
{
// Validate the user password
if (await AuthenticationManager.CheckPasswordAndSignIn(HttpContext, model.UserName, model.Password, model.RememberMe))
{
return RedirectToLocal(returnUrl);
}
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
For more information visit this.
Detailed explanation
I was searching for a way to use OWIN istead of FormsAuthentication, but without all that usermanager, userstore stuff. I suppose you want the same.
In your Login Action check for user credentials in DB with your own code and if user is correct then create Claims with desired info and call AuthenticationManager.SignIn with those Claims:
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString())
,new Claim(ClaimTypes.Name, user.UserName)
,new Claim(ClaimTypes.Email, user.Email)
};
var identity = new ClaimsIdentity(claims,
DefaultAuthenticationTypes.ApplicationCookie);
HttpContext.GetOwinContext().Authentication.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
Now you are logged in just like FormsAuthentication.SetAuthCookie method.
Now you can get User info through claims:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
{
var ctx = filterContext.HttpContext.Request.GetOwinContext();
var identity = (ClaimsPrincipal)filterContext.HttpContext.User;
var enumerable = identity.Claims as IList<Claim> ?? identity.Claims.ToList();
var username = enumerable.Where(x => x.Type == ClaimTypes.Name).Select(x => x.Value).FirstOrDefault();
var userId = enumerable.Where(x => x.Type == ClaimTypes.NameIdentifier).Select(x => x.Value).FirstOrDefault();
}
base.OnActionExecuting(filterContext);
}
You have it under your control, instead of all the EF Code fist stuff that comes with MVC template
Single place of authentication for multiple webapps, can be added with many different approach. To start, here are two.
- Using the OWIN framework's
OAuthAuthorizationServerProvider
: Create one application as a oAuth Server. Setup others to redirect to your oAuth Server for authentication.
- Using Identity Federation approach: Look into Windows Identity Federation. (Thinktecture IdentityServer V2 is an easy to implement Security Token Service. IdentityServerV2 has many feature apart from being STS.)
If all you are looking for is a common authentication scheme where a user is authenticated by a common database for example, implement an asp.net MVC web service. In this way each web site can consume your authentication service and would be hosted and managed by IIS. Info about web API can be found here http://www.asp.net/web-api/overview. Service is deployed to the web server with the standard publish deeply model in visual studio.