SSO with AD FS and OWIN, how to create an account and handle permissions

I am setting up a web application using AD FS, for this I am using OWIN.

To enter the system, everything is in order. If I am a domain user and go to the site, it automatically connects.

But I want to deal with users and roles myself after logging in.

So, I want to check if a user exists in my database with this AD account (this process will be completed before entering another application)

I want to use Microsoft Identity for claim processing (roles and resolutions). But I don’t understand how to put my code to handle a successful connection from AD FS (with Ws-Federation) and add verification and fill in the correct roles.

My code in ConfigureAuth:

public partial class Startup { private static string realm = ConfigurationManager.AppSettings["ida:Wtrealm"]; private static string adfsMetadata = ConfigurationManager.AppSettings["ida:ADFSMetadata"]; private NLogLoggingService _loggingService; public void ConfigureAuth(IAppBuilder app) { _loggingService = new NLogLoggingService("Startup"); _loggingService.Debug("ConfigureAuth"); app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType); app.UseCookieAuthentication(new CookieAuthenticationOptions()); app.UseWsFederationAuthentication( new WsFederationAuthenticationOptions { Wtrealm = realm, MetadataAddress = adfsMetadata, //CallbackPath = PathString.FromUriComponent("/Account/TestCallback"), // https://msdn.microsoft.com/en-us/library/microsoft.owin.security.authenticationmode(v=vs.113).aspx AuthenticationMode = AuthenticationMode.Passive, //Notifications = new WsFederationAuthenticationNotifications //{ //} }); } 

In Web.config, the area is a link to my web application ( https: //ssoadfs.test ), and adfsMetadata is the link to metadata.xml from AD FS.

What is the way to set my role and login logic after connecting AD FS?

The scheme I was thinking about:

enter image description here

EDIT: After some attempts, I cannot handle any successful callback. I do not want to handle roles in the HomeController ...

My last configuration file:

  _loggingService = new NLogLoggingService("Startup"); _loggingService.Debug("ConfigureAuth"); // Configure the db context, user manager and signin manager to use a single instance per request app.CreatePerOwinContext(ApplicationUser.ApplicationDbContext.Create); app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create); app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create); app.SetDefaultSignInAsAuthenticationType(DefaultAuthenticationTypes.ApplicationCookie); app.UseCookieAuthentication(new CookieAuthenticationOptions { Provider = new CookieAuthenticationProvider { OnResponseSignIn = ctx => { _loggingService.Debug("OnResponseSignIn"); ctx.Identity = TransformClaims(ctx, app); }, // Enables the application to validate the security stamp when the user logs in. // This is a security feature which is used when you change a password or add an external login to your account. OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>( validateInterval: TimeSpan.FromMinutes(30), regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager)) } }); app.UseWsFederationAuthentication( new WsFederationAuthenticationOptions { Wtrealm = realm, MetadataAddress = adfsMetadata, Caption = "Active Directory", CallbackPath = PathString.FromUriComponent("/Account/TestCallback"), Notifications = new WsFederationAuthenticationNotifications { SecurityTokenValidated = n => { new NLogLoggingService("Startup").Debug("SecurityTokenValidated"); var incomingClaimsFromAdfs = n.AuthenticationTicket.Identity.Claims.ToList(); var incomingClaimsHasNameIdentifier = incomingClaimsFromAdfs.Any( c => c.Type == System.Security.Claims.ClaimTypes.NameIdentifier); _loggingService.Debug("SecurityTokenValidated - incomingClaimsHasNameIdentifier: " + incomingClaimsHasNameIdentifier); if (!incomingClaimsHasNameIdentifier) { var emailClaim = incomingClaimsFromAdfs.First(c => c.Type == System.Security.Claims.ClaimTypes.Name); _loggingService.Debug(emailClaim.Value); } //if (!incomingClaimsHasNameIdentifier) //{ // var emailClaim = incomingClaimsFromAdfs.First(c => c.Type == System.Security.Claims.ClaimTypes.Name); // incomingClaimsFromAdfs.Add(); // IUser user = await this.UserStore.FindByNameOrEmailAsync(userNameOrEmailAddress); // if ((Entity<long>)user == (Entity<long>)null) // LoginResult = new ApplicationUserManager.LoginResult(LoginResultType.InvalidUserNameOrEmailAddress, default(IUser)); // //else if (!loggedInFromExternalSource && new PasswordHasher().VerifyHashedPassword(user.Password, plainPassword) != PasswordVerificationResult.Success) // // LoginResult = new UserManager<TTenant, TRole, TUser>.LoginResult(LoginResultType.InvalidPassword, user); // else // LoginResult = await this.CreateLoginResultAsync(user, tenant); //} //else //{ // throw new ApplicationException("Get ADFS to provide the NameIdentifier claim!"); //} //var normalizedClaims = incomingClaimsFromAdfs.Distinct(new ClaimComparer()); //var claimsIdentity = new ClaimsIdentity(normalizedClaims, n.AuthenticationTicket.Identity.AuthenticationType); //n.AuthenticationTicket = new AuthenticationTicket(claimsIdentity, n.AuthenticationTicket.Properties); return Task.FromResult(0); } } }); 

In this code I tried CallbackPath (nothing appeared in my log), WsFederationAuthenticationNotifications.SecurityTokenValidated (nothing appeared in my log), CookieAuthenticationProvider.OnResponseSignIn (the same did not happen)

In HomeController, I can have Identity.Name:

 public ActionResult Index() { if (HttpContext.GetOwinContext().Authentication.User.Identity.IsAuthenticated) { new NLogLoggingService("Home").Debug("User is authenticated"); } return View(); } 

Have I really missed something to receive notifications or work with providers in CookieAuthenticationOptions ???

+5
source share
1 answer

If you are using ASP.NET Identity 2.0 or later, you can use an approach similar to the one below. Note that this approach assigns the user GroupRoles instead of assigning each of the roles one by one. You can change the necessary details according to your needs.

 [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task<ActionResult> Login(LoginViewModel model, string returnUrl) { if (!ModelState.IsValid) { return View(model); } ApplicationGroupManager groupManager = new ApplicationGroupManager(); if (Membership.ValidateUser(model.UserName, model.Password)) { FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe); //Assign Roles to the current User ApplicationUser user = UserManager.FindByName(model.UserName); //If the user is registered in the system (ASP.NET Identity) add record to AspNetUsers table if (user != null) { //Returns Group Id and Role Id by using User Id parameter var userGroupRoles = groupManager.GetUserGroupRoles("bfd9730e-2093-4fa0-89a2-226e301d831b"); foreach (var role in userGroupRoles) { string roleName = RoleManager.FindById(role.ApplicationRoleId).Name; UserManager.AddToRole(user.Id, roleName); } } else { //crate new user //first retrieve user info from LDAP: // Create an array of properties that we would like and add them to the search object string[] requiredProperties = new string[] { "samaccountname", "givenname", "sn", "mail", "physicalDeliveryOfficeName", "title" }; var userInfo = CreateDirectoryEntry(model.UserName, requiredProperties); var newUser = new ApplicationUser(); newUser.UserName = userInfo.GetDirectoryEntry().Properties["samaccountname"].Value.ToString(); newUser.Name = userInfo.GetDirectoryEntry().Properties["givenname"].Value.ToString(); newUser.Surname = userInfo.GetDirectoryEntry().Properties["sn"].Value.ToString(); newUser.Email = userInfo.GetDirectoryEntry().Properties["mail"].Value.ToString(); newUser.EmailConfirmed = true; newUser.PasswordHash = null; var result = await UserManager.CreateAsync(newUser); if (result.Succeeded) { //If the user is created ... } //Assign user group (and roles) var defaultGroup = "751b30d7-80be-4b3e-bfdb-3ff8c13be05e"; groupManager.SetUserGroups(newUser.Id, new string[] { defaultGroup }); } return this.RedirectToAction("Index", "Issue"); } this.ModelState.AddModelError(string.Empty, "Wrong username or password!"); return this.View(model); } static SearchResult CreateDirectoryEntry(string sAMAccountName, string[] requiredProperties) { DirectoryEntry ldapConnection = null; try { ldapConnection = new DirectoryEntry("LDAP://OU=******, DC=******, DC=******", " acb@xyz.com ", "YourPassword"); ldapConnection.AuthenticationType = AuthenticationTypes.Secure; DirectorySearcher search = new DirectorySearcher(ldapConnection); search.Filter = String.Format("(sAMAccountName={0})", sAMAccountName); foreach (String property in requiredProperties) search.PropertiesToLoad.Add(property); SearchResult result = search.FindOne(); //SearchResultCollection searchResultCollection = search.FindAll(); //You can also retrieve all information if (result != null) { return result; } else { return null; //Console.WriteLine("User not found!"); } } catch (Exception e) { Console.WriteLine("Exception caught:\n\n" + e.ToString()); } return null; } 


Hope this helps ...

+1
source

Source: https://habr.com/ru/post/1246709/


All Articles