Home > Software Architecture > Mixed Mode with MVC & Identity

Mixed Mode with MVC & Identity

This post is a brief description of how to go about setting up a mixed mode authentication scenario. In our scenario we use Identity Server 3 and windows Authentication. For Identity Server, we utilize OWIN’s UseOpenIdConnectAuthentication. However, certain customers who require us to setup our applications on their internal networks ask that their users not have to authenticate to a separate application.

Ultimately what we ended up doing is creating a single WinAuth.aspx page. In IIS we set authentication to this page only to Windows.

For us we then use an app.settings value so we can disable Mixed Mode if we want to. If enabled, in our RegisterRoutes, we set the default route to a WindowsLogin action which allows Anonymous. In the action we check for user authenticated and if not, they are redirected to the WinAuth.aspx page.

In the WinAuth.aspx we use the following code:


if(Request.LogonUserIdentity.IsAuthenticated)

{


string _windowsUserName = Request.LogonUserIdentity.Name.Split(‘\\’)[1];

 


SLAppUserManager _userManager = new
SLAppUserManager(new
UserStore<SLApplicationUser>(new
CredentialService<SLApplicationUser>()));

 


// Figure out what to user here, our UserManager or what


// Make call to GetWindowsUserByName to get username and password.

 


// See if a user exists which matches the windows user.


var _slUser = await _userManager.FindByNameAsync(_windowsUserName);

 


if(_slUser != null)

{


// Create an IdentityUser


// var _userIdentity = await _slUser.GenerateUserIdentityAsync(_userManager);

 


// Next authenticate the user

 

 


var _tokenClient = new
OAuth2Client(


new
Uri(ConfigurationManager.AppSettings[“IdentityTokenEndpoint”]),


ConfigurationManager.AppSettings[“ClientId”],


ConfigurationManager.AppSettings[“ClientSecret”]

);

 


var _response = await _tokenClient.RequestResourceOwnerPasswordAsync(_slUser.UserName, _slUser.PasswordLegacy, “openid email profile SLWindowsAuthentication offline_access”);

 

 


if(!string.IsNullOrEmpty(_response.AccessToken))

{


BearerTokenMessageInspector _inspector = new
BearerTokenMessageInspector();

 


TokenValidationParameters _validationParameters = _inspector.CreateTokenValidationParameters();

 

 


SecurityToken _securityToken = null;

 


JwtSecurityTokenHandler _tokenHandler = new
JwtSecurityTokenHandler();

 


// Next call will throw the exception if the token or configuration is not valid


// var identities = FederatedAuthentication.FederationConfiguration.IdentityConfiguration.SecurityTokenHandlers.ValidateToken(tokenString, _validationParameters, out _securityToken);


var _claimsPrincipal = _tokenHandler.ValidateToken(_response.AccessToken, _validationParameters, out _securityToken);

 

 


var _ctx = HttpContext.Current.GetOwinContext();

 


var _authenticationManager = _ctx.Authentication;

 


// Not sure why this is needed on a new login but I see it in examples and


// adding this did seem to fix the problem

_authenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);

 


//var _identity = await _userManager.CreateIdentityAsync(_slUser, DefaultAuthenticationTypes.ApplicationCookie);


var _identity = await _userManager.CreateIdentityAsync(_slUser, DefaultAuthenticationTypes.ApplicationCookie);

 


var _claims = new
List<Claim>(from c in _claimsPrincipal.Claims


where c.Type != “scope” &&

c.Type != “client_id” &&

c.Type != “iss” &&

c.Type != “aud” &&

c.Type != “nbf” &&

c.Type != “exp” &&

c.Type != “iat” &&

c.Type != “nonce” &&

c.Type != “c_hash” &&

c.Type != “at_hash”


select c);

 


//var _identity = new ClaimsIdentity(_claimsPrincipal.Claims, DefaultAuthenticationTypes.ApplicationCookie);

 

_identity.AddClaims(_claims);

 

_identity.AddClaim(new
Claim(“access_token”, _response.AccessToken));

_identity.AddClaim(new
Claim(“expires_at”, DateTime.Now.AddSeconds(_response.ExpiresIn).ToLocalTime().ToString()));

_identity.AddClaim(new
Claim(“refresh_token”, _response.RefreshToken));

 


ClaimsPrincipal _cp = new
ClaimsPrincipal(_identity);

 

 


// Session[“SLWindowsOpenIdClaimsPrincipal”] = _cp;

 


// await _authenticationManager.AuthenticateAsync(“SLWindowsOpenIdAuthentication”);

 


// 5/1/15 – After a lot of hours, all this works if we call the signout above in code first.

_authenticationManager.SignIn(new
AuthenticationProperties() { IsPersistent = false }, _identity);

 

First we make sure the user was authenticated to the domain the IIS server is a part of. If so, we use our own custom FindByName method to check that a user associated with the AD account is found.

If so, then we get an access token from our Identity Server. We then create Token Validation parameters and then we use the Microsoft JwtSecurityTokenHandler to Validate the token.

Next we get the OwinContext so we can use it’s AuthenticationManager. The SignOut call, I’m not exactly sure what that’s doing when a user was not logged in but it must be called or the SignIn will not work.

Then we manually create an Identity, we strip all irrelevant claims out. Then finally a call is made to the AuthenticationManager.SignIn.

Once all of the above have completed, then the OwinContext.Authentication.User is properly populated to be utilized in the rest of the application.

 

 

 

Advertisements
Categories: Software Architecture
  1. Aron
    May 22, 2015 at 12:26 am

    Hi Kris,

    I have been trying to do what you’re doing (Windows Authentication for people on our domain, bypassing the regular login screen. And the usual login page if they are external, not on our domain).

    Would you be able to provide a github link to your code where you’ve implemented this?

    I have spent a week trying all different suggestions, “solutions” etc, but have had no luck.

    Hope to hear back from you soon.

    Thanks,

    Aron

    • May 22, 2015 at 10:52 am

      Aron, I don’t have a sample of this I can share right now. Plus even if I did, the way we went about this requires that you use IIS and set individual permissions on the WinAuth.aspx page. (Note you can name the page anything you like.)

      Also, we use a custom back end with our own users. Our user Manager and AuthenticationManager are custom but that should be agnostic to this discussion.

      As described in the blog, if you create a route that points to a controller with an action set to allow anonymous, In the action get the OwinContext. See if the Identity Is Authenticated and if not redirect to your WinAuth.Aspx page.

      In this page, we run the code listed in the blog and once it’s finished, then we redirect the user to our Home/Index page. If the WinAuthentication was successful, then Identity was created and populated with whatever was necessary. If successful your page will show, if not they’ll get redirected to the login page.

      Note, you’ll also have to figure out how you want to associate your Window user account with an account in your user repository, build claims etc. That code most likely will be unique to your situation as well.

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: