MVC5 OWIN ad and database - asp.net

I'm creating an MVC5 app. It is an intranet app. All users are already authenticated to the local Active Directory Domain.
We have an existing database that is currently used for a Windows app.
I want take the User's domain login name and use it to look up the roles and claims that are already configured in that database.
I will assume the the base project of ASP.NET / MVC5 / Authentication "Individual User Accounts" would be the starting point.
Please point me in the right direction.
Thanks

You do not want entire ASP.Net Identity. Instead, you can just use OWIN cookie authentication middleware.
Validate Credential via Active Directory
public bool ValidateCredentials(string userName, string password)
{
using (var context = new PrincipalContext(ContextType.Domain))
{
return context.ValidateCredentials(userName, password);
}
}
Authorize via Old Database
Once authenticated, you want to retrieve authorized role from old Database, and create claims.
private readonly HttpContextBase _context;
private const string AuthenticationType = "ApplicationCookie";
public OwinAuthenticationService(HttpContextBase context)
{
_context = context;
}
public void SignIn(User user)
{
IList<Claim> claims = new List<Claim>
{
new Claim(ClaimTypes.Sid, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.GivenName, user.FirstName),
new Claim(ClaimTypes.Surname, user.LastName),
};
// Get authorized roles from old database
foreach (Role role in user.Roles)
{
claims.Add(new Claim(ClaimTypes.Role, role.Name));
}
ClaimsIdentity identity = new ClaimsIdentity(claims, AuthenticationType);
IOwinContext context = _context.Request.GetOwinContext();
IAuthenticationManager authenticationManager = context.Authentication;
authenticationManager.SignIn(identity);
}
public void SignOut()
{
IOwinContext context = _context.Request.GetOwinContext();
IAuthenticationManager authenticationManager = context.Authentication;
authenticationManager.SignOut(AuthenticationType);
}
Startup.cs
You also need to configure Startup for all those to happen.
[assembly: OwinStartup(typeof(YourApplication.Startup))]
namespace YourApplication
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "ApplicationCookie",
LoginPath = new PathString("/Account/Login")
});
}
}
}
I hope you get the starting point.

The .NET OWIN Identity classes require that you authenticate through the CheckPasswordAsync() method of the ApplicationUserManager class. This can be done by overriding the CheckPasswordAsync() method of class ApplicationUserManager. In your override you will need to call the ValidateCredentials() method of class System.DirectoryServices.AccountManagement to authenticate via Active Directory. This will require the user to login to the application with their Windows username and password. There are a few steps to get that to work though.
As you said, you start with a base project with "Individual User Accounts" authentication.
Step 1 - Update the ConfigureAuth() method in file App_Start\Startup.Auth.cs by adding the code below to the ConfigureAuth() method.
using System.DirectoryServices.AccountManagement;
//Add an Owin context for Active Directory principals
app.CreatePerOwinContext(() => new PrincipalContext(ContextType.Domain));
The rest of the updates are done in file App_Start\IdentityConfig.cs
Step 2 - Update the constructor for class ApplicationUserManager.
using System.DirectoryServices.AccountManagement;
//Add a PrincipalContext parameter to the constructor
public ApplicationUserManager(IUserStore<ApplicationUser> store, PrincipalContext principal) : base(store)
{
this.principal = principal;
}
Step 3 - In the Create() method update the call to the constructor for class ApplicationUserManager.
//Add the PrincipalContext parameter
var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<PortalIdentityDbContext>()), context.Get<PrincipalContext>());
Step 4 - Override the CheckPasswordAsync() method of class ApplicationUserManager.
//Override CheckPasswordAsync to login via Active Directory.
public override async Task<bool> CheckPasswordAsync(ApplicationUser user, string password)
{
return await Task.FromResult(this.principal.ValidateCredentials(user.UserName, password, ContextOptions.Negotiate));
}
As for using your existing database, you will have to incorporate the OWIN Identity tables in it or vice-versa. The Identity functionality requires those tables and you can't change that. I would create a test project and get familiar with those tables. Then figure out how you want to incorporate them into your existing database or vice-versa. I heavily modify those tables for my custom functionality. But the core tables and columns have to exist.

Related

Two Factor Authentication using Asp.net identity framework in ASP.NET MVC 5 application

I am implementing two factor authentication in my ASP.NET MVC 5 application for all users. I have already implemented single factor (username and password) authentication using ASP.NET Identity.
Here is how I implemented single factor authentication:
Step 1: I created the startup.cs file to know application should use cookie authentication
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
LogoutPath = new PathString("/Account/LogOut")
});
}
}
Step 2: User makes post request to login to application by sending username and password. Once username and password are verified, I used sign method from Owin Authentication Manager to sign in the user. This sign in method creates cookie and helps the authentication.
public ActionResult Login(LoginViewModel model)
{
Session.Clear();
if (!ModelState.IsValid)
{
return View(model);
}
var userManager = new ApplicationUserManager(new ApplicationUserStore(new ApplicationDbContext()));
var user = userManager.Find(model.Username,model.Password);
if (user != null)
{
var userIdentity = userManager.CreateIdentity(user, DefaultAuthenticationTypes.ApplicationCookie);
var authenticationManager = HttpContext.GetOwinContext().Authentication;
authenticationManager.SignIn(new AuthenticationProperties(), userIdentity);
}
}
But now I want to introduce OTP authentication after username and password login.I know how to generate OPT and I have SMS provider to send OTP to the user. I can easily put authenticationManager.SignIn() method after OTP verification. But my question is, is there any separate configuration we need provide during signIn to know application that we are doing two factor authentication.
If yes can somebody provide the code where to make such configuration?

ASP.NET Core 3.1 + Angular 9 role based authentication

I have created a web application with ASP.NET Core 3.1 and angular with the Visual Studio Template and enabled Authentication.
Then I scaffolded the Identity and added this to the Startup.cs:
services.AddDefaultIdentity<ApplicationUser>(options =>
options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>().AddEntityFrameworkStores<ApplicationDbContext>();
services.AddIdentityServer().AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
services.AddAuthentication().AddIdentityServerJwt();
After that I have created 3 users and assigned them different roles. When I add the Attribute [Authorize] to any controller action it works correctly. But how can I make it role based? I have tried [Authorize(Roles = "Administrator")] But it denied the access. On an unprotected method:
var loggedinUser = await _userManager.FindByNameAsync(userid);
var roles = await _userManager.GetRolesAsync(loggedinUser);
the roles list has the Administrator on it, so I do not know what I am missing to make it work. Also inside an Angular component, how can I get the role of the current logged in user?
You can extend the implementation of default ProfileService of IdentityServer to include role claim like following way.
public class ExtendedProfileService : ProfileService<ApplicationUser>
{
public ExtendedProfileService(UserManager<ApplicationUser> userManager, IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory) : base(userManager, claimsFactory)
{
}
public override async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
await base.GetProfileDataAsync(context);
var user = await UserManager.GetUserAsync(context.Subject);
var roles = await UserManager.GetRolesAsync(user);
var claims = new List<Claim>();
foreach (var role in roles)
{
claims.Add(new Claim("role", role));
}
context.IssuedClaims.AddRange(claims);
}
}
And don't forget to register your extended profile service to dependency container
services.AddTransient<IProfileService, ExtendedProfileService>();

User.Identity.Name is empty in Asp.NET Core 2.0 API Controller

I am new to ASP.NET core itself. However, I am creating WebAPIs in ASP.NET Core 2.0. I have configured JWT Bearer Token based authentication. Below is my Controller which return token.
[AllowAnonymous]
[Route("api/[controller]")]
public class TokenController : Controller
{
private readonly UserManager<UserEntity> userManager;
private readonly SignInManager<UserEntity> signInManager;
public TokenController(UserManager<UserEntity> userManager, SignInManager<UserEntity> signInManager)
{
this.userManager = userManager;
this.signInManager = signInManager;
}
// GET: api/values
[HttpGet]
public async Task<IActionResult> Get(string username, string password, string grant_type)
{
{
var user = await userManager.FindByEmailAsync(username);
if (user != null)
{
var result =await signInManager.CheckPasswordSignInAsync(user, password, false);
if (result.Succeeded)
{
var claims = new[]
{
new Claim( JwtRegisteredClaimNames.Sub, username),
new Claim( JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim( JwtRegisteredClaimNames.GivenName, "SomeUserID")
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("secretesecretesecretesecretesecretesecrete"));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken( issuer: "test",
audience: "test",
claims: claims,
expires: DateTime.Now.AddDays(15),
signingCredentials: creds);
return Ok(new { access_token = new JwtSecurityTokenHandler().WriteToken(token), expires_on=DateTime.Now.AddDays(15) });
}
}
}
return BadRequest("Could not create token");
}
}
But when calling ValuesController API which is decorated with [Authorize] attributes. I am getting User.Identity.Name is empty. I am not getting any information about user. I am not sure, My token controller is correctly written. As long as it is protecting my ValuesController, I assume, it is correct. However, I might be missing something. Please help.
Note: I am developing using Visual Studio 2017 with Mac Community
addition
Yes, you need to specify the claim for the unique name which is translated into the user.identity.name:
new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName)
I've also been having this problem with ASP.Net Core 2, and I'm really surprised no one's discovered the other cause of this problem.
When my webapp is deployed to IIS, "User.Identity.Name" always returns null. The IIS site has anonymous access disabled, and windows authentication is enabled.
BUT.
I didn't realise that my ASP.Net Core 2 has a "launchSettings.json" file, quietly hidden under the Properties folder, and in there, there's also some iisSettings, and in here "windowsAuthentication" was, strangely, set as false by default.
Changing "windowsAuthentication" to true, and "anonymousAuthentication" to false solved the problem for me.
After doing this, "User.Identity.Name" did finally contain the correct username.
But what the heck is this setting ? Why would this get priority over the actual settings we've setup in IIS Manager ?!
Had this problem too (Core 3.1) using the "DefaultIdentity" (Individual User Accounts).
User.Identity.Name is null, User.Identity.IsAuthenticated = true.
By using httpContextAccessor you can get the userId an with that id you can find the user and the UserName.
In your controller add
using System.Security.Claims;
...
private readonly IHttpContextAccessor _httpContextAccessor;
public MyController(MyContext context, IHttpContextAccessor httpContextAccessor)
{
_context = context;
_httpContextAccessor = httpContextAccessor;
}
// Any method username needed
[HttpGet("{id}")]
public async Task<ActionResult<MyInfo>> GetMyInfo(int id)
{
var userId = _httpContextAccessor.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value;
var user = _context.AspNetUsers.Find(userId);
var userName = user.UserName;
...
}
In the Startup.cs add the following line:
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
For Azure OAuth v2, use preferred_username instead of unique_name (see this and this).
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Extensions.DependencyInjection;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
serviceCollection.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
options.TokenValidationParameters.RoleClaimType = "roles";
options.TokenValidationParameters.NameClaimType = "preferred_username";
//options.TokenValidationParameters.NameClaimType = "email"; // or if you want to use user's email for User.Identity.Name
//below lines of code can be removed. just there if you want some code to be executed right after user is validated.
options.Events.OnTokenValidated = async context =>
{
var personFirstName = context.Principal.FindFirstValue("given_name") ?? string.Empty;
var personLastName = context.Principal.FindFirstValue("family_name") ?? string.Empty;
var personEmail = context.Principal.FindFirstValue("email")?.ToLower();
var personName = context.Principal.Identity.Name;
};
});
Then in your controllers, you will get username from User.Identity.Name

Custom Storage Provider with asp.net core Identity

I am working with an existing authentication API and repository that currently only exposes two methods.
Task<User> Login(string username, string password);
Task Logout(User user);
I would like to use this with asp.net core Identity. All the application needs the ability to do is Log In, Log Out and show the current user who is logged in.
This is my current idea on how to do this.
Extend the SignInManager class as so
public class MySignInManager : SignInManager<User>
{
public Override async Task<SignInResult> PasswordSignInAsync(string userName, string password,
bool isPersistent, bool lockoutOnFailure)
{
var user = await Login(userName, password);
if (user != null)
{
return SignInResult.Success;
}
else
{
return SignInResult.Failure;
}
}
}
Then in my startup would look like this this:
services.AddIdentity<User, IdentityRole>()
.AddDefaultTokenProviders();
services.AddTransient<MySignInManager>();
I read over this article :
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/identity-custom-storage-providers
and they suggested to implement the IUserStore and deal with the existing API from there, but after reading through the default implmentation of the SignInManager and UserManager, it seems to check the Username and password in seperate methods.
Sorry for my lack of knowledge on the subject, I am quite new to the Identity server. Any Help would be greatly appreciated!

SignalR ISAuthenticated using Headers

My goal is:
To use custom headers with my own token to authenticate a user or machine against my signalr service.
We've been using this methodology succesfully under ASP.net WEB API to perform our own custom claims based authentication and authorization.
Our Web Api was as follows:
protected void Application_Start()
{
GlobalConfiguration.Configuration.MessageHandlers.Add(new AuthorizationHeaderHandler());
}
Then we would have a AuthorizationHandler that would overwrite the Thread.CurrentPrincipal = principal; and we would be done.
Within SignalR I have tried to implement:
1. Mark our hub using Authorize
2. Implemented custom authorize atributes
3. Tried A Custom Module. But besides returning true if the correct headers we're send I still do not get the Context.User to change to the claims based principal that we generate.
But never can we get the Context.User to show the actual user that's being used to connect to the hub.
Any suggestions are Welcome.
Main reason why we want to achieve this is because we have a couple of different user/machine types that connect to our system.
Anybody any suggestions.
Finally found the solution.
I added my own owin security middleware allowing me to handle customer header based authentication.
This could be easily expanded allowing you to combine multiple authenitication scheme's within on service.
First Create Custom Authentication Middleware:
public class AuthenticationMiddleware : OwinMiddleware
{
public AuthenticationMiddleware(OwinMiddleware next) :
base(next) { }
public override async Task Invoke(IOwinContext context)
{
var request = context.Request;
var value = request.Headers["Phocabby-MachineKey"];
var username = value;
var usernameClaim = new Claim(ClaimTypes.Name, username);
var identity = new ClaimsIdentity(new[] { usernameClaim }, "ApiKey");
var principal = new ClaimsPrincipal(identity);
principal.Identities.First().AddClaim(new Claim("CanGetApiKey", "False"));
principal.Identities.First().AddClaim(new Claim("Cabinet", "True"));
request.User = principal;
await Next.Invoke(context);
}
}
Then register it in the startup class
public void Configuration(IAppBuilder app)
{
app.Use(typeof(AuthenticationMiddleware));
app.MapSignalR();
}

Resources