Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mixed mode authentication with OWIN

Tags:

owin

I am building an MVC 5 application. I need to authenticate people against an AD and a sql data base or a web service.

Requirement is if a person is logged into corporate network or conneted over VPN i have to log them without asking for credentials. if the users are accessing the web site over internet or a person do not have AD account i have to use forms authentication.

I am looking at this article but will this work with ASP.Net MVC and OWIN? Any other alternative?

Thanks in advance.

like image 994
user1868744 Avatar asked Feb 25 '14 22:02

user1868744


2 Answers

I'm doing something very similar at the moment too. I'm providing a single sign-on portal for internal and external users where they can log in with their AD account or a specified user/password combination.

How I'm currently achieving this (note this is still a work in progress) is in the following way. I'm also using the ASP.NET Identity 2.1 alpha that includes the SignInManager (very cool).

  1. Set up user accounts with an optional password (must be specified for non-AD users)
  2. Associate a UserLogin to the account for AD users with ProviderKey equal to their AD account Sid
  3. On the login I detect if Request.LogonUserIdentity has an known account. Then check if they are a valid use by using the UserManager.FindAsync method. From here you can challenge them again, provide them with an option to log directly in as the known user or log them straight in (your choice here).
  4. I then also allow them to log in via the standard user login form by detecting a username entered in the domain\username format. This allows for a domain user to log in when they are entering your site externally or from another users computer.

Some code snippets from this process (these are just some samples to get you going as the complete solution is spread over my solution).

Log in a using the Request.LoginUserIdentity. This could be a method in your account controller.

public async Task<ActionResult> WindowsLogin(string returnUrl)
{
    var loginInfo = GetWindowsLoginInfo();
    var user = await _userManager.FindAsync(loginInfo);
    if (user != null)
    {
        await SignInAsync(user, false);
        return RedirectTo(returnUrl, "Manage");
    }

    return RedirectToAction("Login");
}

private UserLoginInfo GetWindowsLoginInfo()
{
    if (Request.LogonUserIdentity == null || Request.LogonUserIdentity.User == null)
    {
        return null;
    }
    return new UserLoginInfo("Windows", Request.LogonUserIdentity.User.ToString());
}

I have also added a method to my ApplicationSignInManager (inherits from SignInManager) to allow a user to log in with their AD details using the standard login form.

public async Task<SignInStatus> WindowsLoginAsync(string userName, string password, bool isPersistent)
{
    var signInStatus = SignInStatus.Failure;

    using (var context = new PrincipalContext(ContextType.Domain, "YourDomain"))
    {
        // validate the credentials
        bool credentialsValid = context.ValidateCredentials(userName, password);

        if (credentialsValid)
        {
            UserPrincipal userPrincipal = UserPrincipal.FindByIdentity(context, userName);
            if (userPrincipal != null)
            {
                var loginInfo = new ExternalLoginInfo
                {
                    Login = new UserLoginInfo(AuthenticationTypes.Windows, userPrincipal.Sid.ToString())
                };
                signInStatus = await ExternalSignInAsync(loginInfo, isPersistent);
            }
        }
    }
    return signInStatus;
}

Then this can be used in your Login method like this.

Regex domainRegex = new Regex("(domain\\.+)|(.+@domain)");
if (domainRegex.IsMatch(model.Username))
{
    result = await _signInManager.WindowsLoginAsync(model.Username, model.Password, model.RememberMe);
    switch (result)
    {
        case SignInStatus.Success:
            return RedirectTo(returnUrl, "Manage");
    }
}

result = await _signInManager.PasswordSignInAsync(model.Username, model.Password, model.RememberMe, true);
...

I hope some of this helps you solve the problem!

like image 158
jerms55 Avatar answered Sep 28 '22 00:09

jerms55


The way owin works is that every requests drops through all the middleware modules registered in startup.

That means if you want to have multiple ways to authenticate, you would need to use/create and register all the different middleware required. Each middle ware would then authenticate against the various user stores and a ClaimsPrincipal (or many) would be created.

A simple example, (geared towards api) would look like this. The OAuthBearer is token authentication from Identity 2.0 and BasicAuthenication is just that basic user/pwd in the header.

//This will create the usermanager per request.
app.CreatePerOwinContext(ApplicationSession.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);

// Token Authentication
app.UseOAuthBearerAuthentication(OAuthBearerOptions);

// Basic Authentication.
app.UseBasicAuthentication(app.CreateLogger<BasicAuthenticationMiddleware>(), 
                    "Realm", ValidateUser);

Good luck

like image 44
William Avatar answered Sep 27 '22 23:09

William