Custom role provider with Claims - asp.net-mvc

I have User table in my database where I keep user's role (master admin, admin, developer). I want to authorize some controllers
so only master admin can have access.
namespace TicketSystem.Controllers
{
public class UserCredentials : ClaimsPrincipal, IIdentity, IPrincipal
{
public IIdentity Identity { get; private set; }
public int UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string[] roles { get; set; }
public string email { get; set; }
override
public bool IsInRole(string role)
{
if (roles.Any(r => role.Contains(r)))
{
return true;
}
else
{
return false;
}
}
public UserCredentials() { }
public UserCredentials(ClaimsPrincipal principal)
: base(principal)
{
}
public UserCredentials(int userId, string email, string firstName, string lastName, string[] roles)
{
this.Identity = new GenericIdentity(email);
this.UserId = userId;
this.email = email;
this.FirstName = firstName;
this.LastName = lastName;
this.roles = roles;
}
override
public string ToString()
{
return UserId + "";
}
}
}
This is my login method
UserCredentials loggedUser = null;
User loginUser = db.tblUser.Where(x => x.email == model.UserName).FirstOrDefault();
loggedUser = new UserCredentials( loginUser.idUser,
loginUser.email, loginUser.firsName, loginUser.lastName, new string[] { loginUser.role });
if (loggedUser != null)
{
var identity = new ClaimsIdentity(new[] {
new Claim(ClaimTypes.Name, loggedUser.email),
new Claim("http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider", User.Identity.AuthenticationType),
new Claim(ClaimTypes.NameIdentifier, loggedUser.FirstName),
new Claim(ClaimTypes.Role, loggedUser.roles[0])
}, "ApplicationCookie");
var ctx = Request.GetOwinContext();
var authManager = ctx.Authentication;
authManager.SignIn(identity);
I try with this
public class CustomRoleProvider : RoleProvider
{
public override bool IsUserInRole(string username, string roleName)
{
using (var usersContext = new TicketSystemEntities())
{
var user = usersContext.tblUser.SingleOrDefault(u => u.email == username);
if (user == null)
return false;
return user.role != null && user.role==roleName;
}
}
}
but I don't know how to configure web.Config. Also I'm having errors such as
TicketSystem.Models.CustomRoleProvider' does not implement inherited abstract member 'System.Web.Security.RoleProvider.GetUsersInRole(string)
I was searching other examples but I didn't find any example where the author uses Claim

RoleProvider is an abstract class, you have to implement all abstract methods to compile your CustomRoleProvider.
In the Web.config you need to add section roleManager and add your custom provider. Something like this:
<roleManager enabled="true" defaultProvider="CustomRoleProvider">
<providers>
<clear/>
<add name="CustomRoleProvider"
type="TicketSystem.Models.CustomRoleProvider,
TicketSystem, Version=1.0.0.0, Culture=neutral"
connectionStringName="TicketSystemEntities"
enablePasswordRetrieval="false" enablePasswordReset="true"/>
</providers>
</roleManager>
For reference check RoleProvider docs https://msdn.microsoft.com/en-us/library/system.web.security.roleprovider(v=vs.140).aspx and roleManager docs https://msdn.microsoft.com/en-us/library/vstudio/ms164660%28v=vs.100%29.aspx

Related

how to save jwt token in databse asp .net 3.1 web api

I want to Save jwt token in the database I share the code of the controller where token generation is done but I don't know how to save the token or that code will work or nor
this is my controller where use jwt token
public class LoginController: Controller
{
private readonly JwtAuthContext _context;
private IConfiguration _config;
public LoginController(IConfiguration config, JwtAuthContext
context)
{
_config = config;
_context = context;
}
[Route("api/Register")]
[HttpPost]
public IActionResult Post([FromBody] Register register)
{
if (ModelState.IsValid)
{
_context.Add(register);
_context.SaveChanges();
}
Console.WriteLine(register);
var ttt = _context.Registers.ToList();
return Ok(new { result = ttt });
}
[HttpPost]
public IActionResult Login([FromBody] Login Login)
{
var user = Authenticate(Login);
if (user != null)
{
var token = Generate(user);
_context.SaveChanges();
return Ok(token);
}
return NotFound("User not found");
}
private string Generate(Register user)
{
var securityKey = new
SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
var credentials = new SigningCredentials(securityKey,
SecurityAlgorithms.HmacSha256);
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, user.Email),
new Claim(ClaimTypes.Email, user.FullName),
new Claim(ClaimTypes.Role, user.Role)
};
var token = new JwtSecurityToken(_config["Jwt:Issuer"],
_config["Jwt:Audience"],
claims,
expires: DateTime.Now.AddMinutes(15),
signingCredentials: credentials);
return new JwtSecurityTokenHandler().WriteToken(token);
}
private Register Authenticate(Login Login)
{
var currentUser = _context.Registers.FirstOrDefault(o =>
o.Email.ToLower() == Login.Email.ToLower() && o.Password == Login.Password);
if (currentUser != null)
{
return currentUser;
}
return null;
}
this is my login model where I create a table of login
public class login{
public int LoginId{get;set;}
public string Email{get;set;}
public string Password{get;set;}
}
this is my register model where I can create a register model
public class Register{
public int Id{get;set;}
public string FullName{get;set;}
public string Email{get;set;}
public string Password{get;set;}
}
-------------
JwtAuthContext
--------------
public class JwtAuthContext : DbContext
{
public JwtAuthContext(DbContextOptions<JwtAuthContext> options)
: base(options)
{
}
public DbSet<Login> Logins { get; set; }
public DbSet<Register> Registers { get; set; }
public DbSet<AuthenticationToken> authenticationTokens { get;
set; }
}
This is my AuthenticationToken Model
public class AuthenticationToken
{
public string Token{get;set;}
}
Try this.
if (user != null)
{
var token = Generate(user);
_context.authenticationTokens.Add(token); // just add this line
_context.SaveChanges();
return Ok(token);
}

ASP.NET MVC - Custom IIdentity or IPrincipal with Windows Authentication

I am working on an intranet site with Windows Authentication for logins. However, I want to extend the IPrincipal to have other properties. For instance, I'd like to get the user's FirstName in #User.FirstName or User.AuthorizedActivity("Admin/Permissions/Edit") (would retrieve from db) using activities instead of roles to hide certain links, etc. I am really having a heck of a time figuring this out over the past 2 days and find much information doing this with Windows Authentication.
My CustomPrincipal and BaseViewPage setup:
namespace Intranet_v2.Helpers
{
public interface ICustomPrincipal : IPrincipal
{
Guid UserGuid { get; set; }
string FirstName { get; set; }
string LastName { get; set; }
string FullName { get; set; }
}
public class CustomPrincipal : ICustomPrincipal
{
public IIdentity Identity { get; private set; }
public bool IsInRole(string role) { return false; }
public CustomPrincipal(string identity)
{
this.Identity = new GenericIdentity(identity);
}
public Guid UserGuid { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName { get; set; }
}
public class CustomPrincipalSerializeModel
{
public Guid UserGuid { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName { get; set; }
}
public class BaseController : Controller
{
protected virtual new CustomPrincipal User
{
get { return HttpContext.User as CustomPrincipal; }
}
}
public abstract class BaseViewPage : WebViewPage
{
public virtual new CustomPrincipal User
{
get { return base.User as CustomPrincipal; }
}
}
public abstract class BaseViewPage<TModel> : WebViewPage<TModel>
{
public virtual new CustomPrincipal User
{
get { return base.User as CustomPrincipal; }
}
}
}
Views Web.Config BaseViewPage:
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="Intranet_v2.Helpers.BaseViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Optimization"/>
<add namespace="System.Web.Routing" />
<add namespace="Intranet_v2" />
</namespaces>
</pages>
I think my main problem is I have no idea what to do in the protected void Application_PostAuthenticateRequest(object sender, EventArgs args) for my Global.asax.cs file. I have a poor attempt at setting it up here:
protected void Application_PostAuthenticateRequest(object sender, EventArgs args)
{
//var application = (HttpApplication)sender;
var context = application.Context;
if (context.User != null || !context.User.Identity.IsAuthenticated) return;
var formsIdentity = (FormsIdentity)context.User.Identity;
if (formsIdentity == null) return;
var ticket = formsIdentity.Ticket;
JavaScriptSerializer serializer = new JavaScriptSerializer();
CustomPrincipalSerializeModel serializeModel = serializer.Deserialize<CustomPrincipalSerializeModel>(ticket.UserData);
CustomPrincipal newUser = new CustomPrincipal(ticket.Name);
newUser.UserGuid = serializeModel.UserGuid;
newUser.FirstName = serializeModel.FirstName;
newUser.LastName = serializeModel.LastName;
newUser.FullName = serializeModel.FullName;
var values = ticket.UserData.Split('|');
var roles = values[1].Split(',');
context.User = new GenericPrincipal(new GenericIdentity(ticket.Name, "Forms"), roles);
}
Now I'm at the point where #User.Name is now null. I'm in way over my head on this. Any help is appreciated. My protected void Application_PostAuthenticateRequest(object sender, EventArgs args) is completely out of wack.
All I want to do is rely on Windows Authentication to do what it does normally and add a few extra properties to the HttpContext.Current.User. Any help is appreciated... I can't be the only one trying to do this.
What I normally do is just request the additional user information later. For instance, using an Extension method like:
public static class PrincipalExtensions
{
private static void Initialize(string userName)
{
var userRecord = //Get user information from DB;
var session = HttpContext.Current.Session;
if (session != null)
{
session.Add("UserID", userRecord.ID);
session.Add("UserEmail", userRecord.Email);
//And so on
}
}
public static long? GetUserID(this IPrincipal user)
{
var id = HttpContext.Current.Session["UserID"] as long?;
if (id == null)
Initialize();
return (long)HttpContext.Current.Session["UserID"];
}
}
This is roughly what I implement in some of my projects; rather than tapping into the login process and store it in the cookie, the system can lazy load the information and cache in session when the information is needed.

Modifying the default MVC app to show additional properties

I'm trying to modify the default MVC project so that instead of showing a username, I can display their full name. Eg, the default app shows
Hello <username>! Log off
I added a new property FullName to the ApplicationUser class. The code that shows the name currently is:
#Html.ActionLink("Hello " + User.Identity.GetUserName() + "!", "Manage", "Account", routeValues:=Nothing, htmlAttributes:=New With {.title = "Manage"})
So how can I get lookup that value from the ApplicationUser class and display it here? Additionally, is there a way to cache this? It seems like a waste to perform a lookup for every request.
I also might want to show their email address instead, so I definitely need to use a new property.
I generally like to serialize a user object in the FormsAuthentication cookie when they login and then create a class inheriting from IPrincipal so that my views can read the de-serialized object:
public interface IUserPrincipal : IPrincipal
{
int Id { get; set; }
string FirstName { get; set; }
string LastName { get; set; }
string Username { get; set; }
}
public class UserPrincipal : IUserPrincipal
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Username { get; set; }
public IIdentity Identity { get; private set; }
public UserPrincipal(string Username)
{
this.Identity = new GenericIdentity(Username);
}
}
public class UserPrincipalPoco
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Username { get; set; }
}
and then when authenticating:
public ActionResult Login(LoginViewModel vm, string ReturnUrl)
{
// Check for valid authentication
if (_authenticationService.Authenticate(vm.Username, vm.Password))
{
// Add forms authentication cookie
Response.Cookies.Add(GetFormsAuthenticationCookie(vm.Username));
// Redirect after authentication
}
// Failed authentication, redirect to unauthorized
}
private HttpCookie GetFormsAuthenticationCookie(string Username)
{
var user = _userService.GetUserByUsername(Username);
UserPrincipalPoco pocoModel = new UserPrincipalPoco();
pocoModel.Id = user.Id.Value;
pocoModel.FirstName = user.FirstName;
pocoModel.LastName = user.LastName;
pocoModel.Username = Username;
JavaScriptSerializer serializer = new JavaScriptSerializer();
string userData = serializer.Serialize(pocoModel);
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket(
1,
Username,
DateTime.Now,
DateTime.Now.AddMinutes(15),
false,
userData);
string encryptedTicket = FormsAuthentication.Encrypt(authTicket);
return new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket);
}
and then in global.asax.cs:
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
JavaScriptSerializer serializer = new JavaScriptSerializer();
UserPrincipalPoco serializeModel = serializer.Deserialize<UserPrincipalPoco>(authTicket.UserData);
UserPrincipal newUser = new UserPrincipal(authTicket.Name);
newUser.Id = serializeModel.Id;
newUser.FirstName = serializeModel.FirstName;
newUser.LastName = serializeModel.LastName;
newUser.Username = serializeModel.Username;
HttpContext.Current.User = newUser;
}
}
Now you need to create a BaseViewPage that inherits from WebViewPage to tell your views to use your UserPrincipal object:
public abstract class BaseViewPage : WebViewPage
{
public virtual new UserPrincipal User
{
get { return base.User as UserPrincipal; }
}
}
public abstract class BaseViewPage<TModel> : WebViewPage<TModel>
{
public virtual new UserPrincipal User
{
get { return base.User as UserPrincipal; }
}
}
and in your Web.config tell your views to always use this BaseViewPage:
<pages pageBaseType="MyNameSpace.Views.BaseViewPage">
Now in my views I can access the user like:
#User.Username
or
#User.FirstName #User.LastName
few ways you can do it.
if the application is not too big, you may cache the user models that log in for a specific period of time so you can pull the entire user info based on the username.
you may save a list if info in User.Identity, including username, firstname, last name etc, separating them with a comma or etc.
a bad way: every time you need the extra info, hit the database and get them.
my opinion: cache the recent users who have been logged in for a specific amount of time. you will be able to create slick solutions using cashing. let me know if you need info.

StackOverflowException was unhandled: An unhandled exception of type 'System.StackOverflowException' occurred in mscorlib.dll

i am using asp.net mvc 4 and entity framework 5 in a project. i have a base Entity that all entities derived from it:
public abstract class BaseEntity
{
[Required]
public virtual int Id { get; set; }
[Required]
public virtual DateTime CreatedOn { set; get; }
public virtual string CreatedBy { set; get; }
[Required]
public virtual DateTime ModifiedOn { set; get; }
public virtual string ModifiedBy { set; get; }
}
First the Account Entity is a class for application user:
public class Account : BaseEntity
{
public string UserName { get; set; }
public string Password { get; set; }
public byte[] AvatarBinary { get; set; }
public string AvatarMimeType { get; set; }
public virtual IList<AccountInRole> AccountRoles { get; set; }
}
Role of the User :
public class Role : BaseEntity
{
public string RoleName { get; set; }
public virtual IList<AccountInRole> AccountRoles { get; set; }
}
each User can have multiple Role and vice versa:
public class AccountInRole : BaseEntity
{
public int AccountId { get; set; }
public int RoleId { get; set; }
public virtual Account Account { get; set; }
public virtual Role Role { get; set; }
}
when i want to give roles for an specific user, call GetRoles method in Accountrepository. this is implemented in this way:
public class AccountRepository : IAccountRepository
{
#region Properties
private CharityContext DataContext { get; set; }
public IQueryable<Account> Accounts
{
get { return DataContext.Accounts; }
}
#endregion
#region Ctors
public AccountRepository() : this(new CharityContext())
{
}
public AccountRepository(CharityContext db)
{
DataContext = db;
}
#endregion
#region Methods
public List<Role> GetRoles(string userName)
{
var acc = DataContext.Accounts;
var query = from u in DataContext.Accounts
from r in DataContext.Roles
from ur in DataContext.AccountInRoles
where ur.AccountId == u.Id && ur.RoleId == r.Id && u.UserName == userName
select r;
return query.ToList();
}
#endregion
}
in this method, an exception has thrown when the compiler want to run above LINQ query. this exception is:
StackOverflowException was unhandled
An unhandled exception of type 'System.StackOverflowException' occurred in mscorlib.dll
{Cannot evaluate expression because the current thread is in a stack overflow state.}
the GetRoles method are call two time :
one time from the Custom Authorize Attribute:
public class CustomAuthorize : AuthorizeAttribute
{
//private readonly IAccountRepository _accountRepository;
private string[] roles;
//public CustomAuthorize(params string[] roles)
//{
// this.roles = roles;
//}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext == null)
throw new ArgumentNullException("httpContext");
if (!httpContext.User.Identity.IsAuthenticated)
return false;
if (Roles == string.Empty)
return true;
var lstRoles = Roles.Split(',');
AccountRepository _accountRepository = new AccountRepository();
var userRoles = _accountRepository.GetRoles(httpContext.User.Identity.Name);
foreach (var role in lstRoles)
{
bool isFound = false;
foreach (var userRole in userRoles)
{
if (userRole.RoleName == role)
isFound = true;
}
if (!isFound) return false;
}
return true;
}
}
and second time from the Application_AuthenticateRequest method in the Global.asax.cs :
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
string cookie = FormsAuthentication.FormsCookieName;
HttpCookie httpCookie = Request.Cookies[cookie];
if (httpCookie == null) return;
FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(httpCookie.Value);
if(ticket == null || ticket.Expired) return;
FormsIdentity identity = new FormsIdentity(ticket);
var _accountRepository = new AccountRepository();
var roles = _accountRepository.GetRoles(identity.Name);
var principal = new CharityAccount(identity.Name, roles.Select(x => x.RoleName).ToArray());
Context.User = Thread.CurrentPrincipal = principal;
}
CharityAccount that ou can see it in above method is implemented in this way:
public class CharityAccount : IPrincipal
{
private string[] roles;
private IIdentity identity;
public IIdentity Identity
{
get { return identity; }
}
public bool IsInRole(string role)
{
return Array.IndexOf(roles, role) >= 0;
}
public CharityAccount(String name, String[] roles)
{
identity = new GenericIdentity(name, "Custom authentication");
this.roles = roles;
}
}
According to your idea, what is the problem?
regards
You have done few things which can lead you to troubles. The one I can see is the circular reference of Accounts, roles in AccountinRoles and vice versa.
I have simplified your code though it's not the best design(But I believe in keeping things simple and stupid). You can keep your virtual properties if you really mean what the virtual properties are for in entities.
This working and running fine.
public abstract class BaseEntity
{
public int Id { get; set; }
public DateTime CreatedOn { set; get; }
}
public class Account : BaseEntity
{
public string UserName { get; set; }
public string Password { get; set; }
}
public class Role : BaseEntity
{
public string RoleName { get; set; }
}
public class AccountInRole
{
public int AccountId { get; set; }
public int RoleId { get; set; }
}
public class Operation
{
public List<Role> GetRoles()
{
List<Account> lstAccount = new List<Account>();
List<Role> lstRole = new List<Role>();
List<AccountInRole> lstAccountInRoles = new List<AccountInRole>();
Account ac1 = new Account
{
Id = 1,
UserName = "Jack",
Password = "somePassword2",
CreatedOn = DateTime.Now
};
Account ac2 = new Account
{
Id = 2,
UserName = "Sam",
Password = "somePassword1",
CreatedOn = DateTime.Now
};
lstAccount.Add(ac1);
lstAccount.Add(ac2);
Role r1 = new Role
{
Id = 1,
RoleName = "TestRole1",
CreatedOn = DateTime.Now
};
Role r2 = new Role
{
Id = 2,
RoleName = "TestRole2",
CreatedOn = DateTime.Now
};
lstRole.Add(r1);
lstRole.Add(r2);
AccountInRole acRole1 = new AccountInRole
{
AccountId = ac1.Id,
RoleId = r1.Id
};
AccountInRole acRole2 = new AccountInRole
{
AccountId = ac2.Id,
RoleId = r2.Id
};
lstAccountInRoles.Add(acRole1);
lstAccountInRoles.Add(acRole2);
string userName = "Sam";
// Query the data
var roles = from u in lstAccount
where u.UserName == userName
from acc in lstAccountInRoles
from r in lstRole
where acc.AccountId == u.Id
&& r.Id == acc.RoleId
select r;
return roles.ToList();
}
}

Problems implementing IPrincipal

Trying to implement IPrincipal (ASP.NET MVC 3) and having problems:
my custom IPrincipal:
interface IDealsPrincipal: IPrincipal
{
int UserId { get; set; }
string Firstname { get; set; }
string Lastname { get; set; }
}
public class DealsPrincipal : IDealsPrincipal
{
public IIdentity Identity { get; private set; }
public bool IsInRole(string role) { return false; }
public DealsPrincipal(string email)
{
this.Identity = new GenericIdentity(email);
}
public int UserId { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
}
To serialize/deserialize i use the following class:
public class DealsPrincipalSerializeModel
{
public int UserId { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
}
The Application authenticate event is as follows (works fine!)
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
//get the forms ticket
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
//instantiate a new Deserializer
JavaScriptSerializer serializer = new JavaScriptSerializer();
//deserialize the model
DealsPrincipalSerializeModel serializeModel = serializer.Deserialize<DealsPrincipalSerializeModel>(authTicket.UserData);
//put the values of the deserialized model into the HttpContext
DealsPrincipal newUser = new DealsPrincipal(authTicket.Name); //this implements IPrincipal
newUser.UserId = serializeModel.UserId;
newUser.Firstname = serializeModel.Firstname;
newUser.Lastname = serializeModel.Lastname;
HttpContext.Current.User = newUser;
}
}
As you can see in the last statement the HttpContext gets assigned this new DealsPrincipal (which works fine).
The problem is that if want to access this User in a Controller(Action) i always get a base class object. If i cast the User as follows:
User as DealsPrincipal
to get for example the UserId (sample:
( User as DealsPrincipal).UserId
this is always null!!! Why? What am i missing?
I would need to investigate more to give you correct answer but look this part of the code and it could help you (part of the source of WindowsAuthenticationModule.cs)
void OnAuthenticate(WindowsAuthenticationEventArgs e) {
////////////////////////////////////////////////////////////
// If there are event handlers, invoke the handlers
if (_eventHandler != null)
_eventHandler(this, e);
if (e.Context.User == null)
{
if (e.User != null)
e.Context.User = e.User;
else if (e.Identity == _anonymousIdentity)
e.Context.SetPrincipalNoDemand(_anonymousPrincipal, false /*needToSetNativePrincipal*/);
else
e.Context.SetPrincipalNoDemand(new WindowsPrincipal(e.Identity), false /*needToSetNativePrincipal*/);
}
}
From this code I would suggest you to check if user is anonymous before assigning instance of your custom IPrincipal inmplementation. Also, not sure if this method is executed before or after "protected void Application_AuthenticateRequest". Will try to take more time to investigate this.
Also, please look at this article:
http://msdn.microsoft.com/en-us/library/ff649210.aspx

Resources