Archive

Archive for May, 2015

Mixed Mode with MVC & Identity

May 1, 2015 2 comments

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