Filter extended IdentityUserRole when granting credentials - c#

I'm trying to create an authentication server for multiple applications with Owin.
For that purpose i'm adding a clientId to my AspNetUserRole table like this.
public class ApplicationUserRoles : IdentityUserRole
{
public ApplicationUserRoles() : base() { }
[Key]
public string ClientId { get; set; }
[ForeignKey("ClientId")]
public Audience Client { get; set; }
}
That is working fine.
What I can't find is where to override the role list creation when i'm doing the GrantResourceOwnerCredentials so i can filter only de roles for the current cilentId.
I tried to change it in UserManager FindAsync(username,password) but Roles is readonly.
I thing maybe I should override something on UserStore but i'm not sure which method should be overrided, and how I can be sure i'm loading all the other data such as claims and logins and checking all the security concerns it already does.
Any idea will be much appreciated.

Ok
I'm sure this isn't the best answer but it works and i'm sure it doesn't change anything in the indentity flow.
I change in my ApplicationUserManager the create identity method. I take thw identity created by owin, remove the role claims it already has and add new ones from the rolesi want.
public override async Task<ClaimsIdentity> CreateIdentityAsync(ApplicationUser user, string authenticationType)
{
var identityClaims = await base.CreateIdentityAsync(user, authenticationType);
List<Claim> claimsDelete = new List<Claim>();
foreach (var claim in identityClaims.Claims)
{
if (claim.Type == ClaimTypes.Role)
claimsDelete.Add(claim);
}
foreach(var claim in claimsDelete)
identityClaims.TryRemoveClaim(claim);
var roleStore = new RoleStore();
var roles = roleStore.GetAll();
for (int i = 0; i < user.Roles.Count(); i++)
{
if (((ApplicationUserRoles)((List<IdentityUserRole>)user.Roles)[i]).ClientId == this.clientId)
{
identityClaims.AddClaim(new Claim(ClaimTypes.Role, roles[((List<IdentityUserRole>)user.Roles)[i].RoleId]));
}
}
return identityClaims;
}

Related

Extra claims in Token in web api

The purpose of Token in Web api is to ensure identity of user. Here is the way i created the token.
public AuthTokenModel GenerateToken(string UserName, string password, string appCode)
{
var tokenExpiration = TimeSpan.FromSeconds(Convert.ToDouble(GetAppSettingsValue("TokenExpiry")));
ClaimsIdentity identity = new ClaimsIdentity(OAuthDefaults.AuthenticationType);
identity.AddClaim(new Claim("loginId", UserName));
identity.AddClaim(new Claim("password", password));
identity.AddClaim(new Claim("applicationCode", appCode));
var props = new AuthenticationProperties()
{
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.Add(tokenExpiration),
};
var ticket = new AuthenticationTicket(identity, props);
var accessToken = Startup.OAuthOptions.AccessTokenFormat.Protect(ticket);
AuthTokenModel model = new AuthTokenModel();
model.AccessToken = string.Format("{0} {1}", "bearer", accessToken);
model.TokenType = "bearer";
model.ExpiresIn = tokenExpiration.TotalSeconds.ToString();
model.Expires = ticket.Properties.ExpiresUtc.ToString();
model.Issued = ticket.Properties.IssuedUtc.ToString();
return model;
}
and the Model is defined as:
public class AuthTokenModel
{
public string AccessToken { get; set; }
public string TokenType { get; set; }
public string ExpiresIn { get; set; }
public string Issued { get; set; }
public string Expires { get; set; }
}
but i want to know if i add more details of user in Token is the better approach ? and what is the performance if i get the further details from token rather than database.
Never include user's password anywhere. Especially in token.
Token, itself, is a very abstract thing. The concept of bearer token
declares only single idea: anyone in possession of this token could
use it the same way as others. This token is a kind of user's
authority confirmation.
Implementing your proprietary token-based authentication protocol
would be not a best choice , because it's a complex work.
Take a look at OAuth2.0 / OpenIDConnect protocols documentation. Consider mature & opensource .net-core (and previously classic .net framework)-based solution, implementing this protocols - IdentityServer.
I think it would be a great start for understanding tokens-based authentication in web-applications.

Impersonate a user in a standard ASP.NET MVC installation template

I have setup a standard ASP.NET MVC site with normal authentication. I have added roles, so new users get a specific role.
Now, I want to be able to impersonate a user.
Impersonating advice
Impersonating, when I search around, comes the following way:
FormsAuthentication.SetAuthCookie(user.UserName, false);
This doesn't work by default, as you have to do two things:
1:
Enable forms authentication:
<system.web>
<authentication mode="Forms" />
</system.web>
2:
Disable the module:
<system.webServer>
<modules>
<!--<remove name="FormsAuthentication" />-->
</modules>
<staticContent>
The challenge
However, doing this leaves a couple of challenges.
When you impersonate, you cannot log out. This is easily fixed by
adding the following in LogOut: FormsAuthentication.SignOut();
The User.IsInRole(Constants.Roles.Creditor); stops working, so
we cannot check if user in a role
What to do?
This COULD boil down to me - apparently - not fully understanding the membership framework despite trying. However, how do you get impersonate to work here?
I have no explicit reason to use "Forms" authentication, and the only reason I started on this path is Impersonating. So I see I have two obvious directions:
A) Implement impersonation in a different way, so I don't touch my
web.config to use forms
B) Fix the role problem in forms
Any help here? :-)
There are quite a few ways to accomplish this all you really need to do is get both the Id's to your controller and decide how you want it persisted (Cookie, Cache, Db , etc.).
An easy way to do this is to create a claim for the impersonation and add a policy for those kind of claims. Here is a link for adding claim based policies.
Here is some code to get you started :
In your controllers you will want an end point that does something like this
var claims = await UserManager.GetClaimsAsync(CurrentUserId);
var claim = claims.FirstOrDefault(c => c.Type == "Impersonate");
if (claim!=null)
{
//You may forget to remove it or the user could end there session before you are able to
var r = await UserManager.RemoveClaimAsync(CurrentUserId, claim);
}
var result = await UserManager.AddClaimAsync(CurrentUserId, new Claim("Impersonate", userId));
if (!result.Succeeded)
{
return GetErrorResult(result);
}
Now with the code above we wanted the users ID, but we could have just as easily gotten there role and saved that with the claim. From here you just need to decide how you want to use this claim. The link below will show you how you can do that.
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/claims
Just remember to remove the claim after you are done.
This one works nice when you may have to impersonate a user for sometime. One project I worked on the clients the needed to be able to impersonate users for weeks at a time to complete work for other clients.
If we need to implement the IsInRole() method in basic forms principal, we need to customize the principal.
User is a principal object, which contains list of Identities. Default identity does not contain role property, so we need to create custom identity object.
For e.g:
public class CustomPricipal : IPrincipal
{
public CustomPricipal(string username)
{
this.Identity = new CustomIdentity(username);
}
public IIdentity Identity
{
get;
private set;
}
public bool IsInRole(string role)
{
return this.Identity != null && ((CustomIdentity)this.Identity).Roles.Any(x => x.ToLower() == role.ToLower());
}
}
public class CustomIdentity : IIdentity
{
public CustomIdentity(string name)
{
// We can fetch the user information from database and create custom properties
this.Name = name;
this.IsAuthenticated = true;
this.AuthenticationType = "Forms";
this.Roles = new List<string>() { "Admin", "SuperAdmin" };
}
public string AuthenticationType
{
get;
private set;
}
public bool IsAuthenticated
{
get;
private set;
}
public string Name
{
get;
private set;
}
public List<string> Roles
{
get;
private set;
}
}
In global.asax.cs
public override void Init()
{
this.PostAuthenticateRequest += MvcApplication_PostAuthenticateRequest;
base.Init();
}
void MvcApplication_PostAuthenticateRequest(object sender, EventArgs e)
{
if (Request.IsAuthenticated)
{
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
Context.User = Thread.CurrentPrincipal = new CustomPricipal(authTicket.Name);
}
}
}
Now we can use the User.IsInRole("")

How to seed a user into the database with ASP.NET Identity?

For security, I don't want just anybody to be able to register on my site but I can't stop users from registering while there is a registration page accessible on the site, so I added an "Authorized" flag to the ApplicationUser model:
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public bool Authorized { get; set; }
}
The idea is that if a user, whether logged in or not, does not have the value for this property set to true, the user will not be able to access secured content. A previously authorized user will have to authorize new users.
This brings into question how to get the first user authorized so I figured I'd seed the user in on my Migration. See below:
namespace Ortund.Migrations
{
internal sealed class Configuration :
DbMigrationsConfiguration<Ortund.Models.ApplicationDbContext>
{
protected override void Seed(Ortund.Models.ApplicationDbContext context)
{
context.Users.AddOrUpdate(
u => u.Id,
new Models.ApplicationUser { EmailAddress = "email#site.com", Authorized = true, EmailConfirmed = true }
);
}
}
}
Obviously I'm missing some crucial fields here Password hashes for one and that's where I'm having trouble.
I don't know how the UserManager hashes the password so I can't seem replicate that process here since the function is not an async Task.
For reference, here's the Register method in the AccountController:
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await UserManager.CreateAsync(user, model.Password);
...
}
}
The CreateAsync function specifies a cleartext password string that will be hashed. Attempting to replicate this in my Seed method above, I only have access to ApplicationUserManager.Create(Microsoft.AspNet.Identity.Owin.IdentityFactorOptions<ApplicationUserManager> options, Microsoft.Owin.IOwinContext context) which as far as I can see, doesn't suit my requirement here.
How can I create a full ApplicationUser in my seeded data?
You could call in to the UserManager.Create method and pass it the username and plain text password. That will hash the password for you and get that into the database
Something like this:
var userStore = new UserStore<ApplicationUser>(context);
var userManager = new UserManager<ApplicationUser>(userStore);
var user = new ApplicationUser { UserName = "username"};
userManager.Create(user, "password");

Updating Claims in Microsoft Identity 3.0.0 rc1 and persisting the data

I've run into an issue when working with Microsoft MVC 5 and Identities 3.0.0 rc1-final.
So I've modified the AppUser.cs model that extends IdentityUser to include a FirstName and LastName column. My next objective is to create a claim for the user that stores there FullName, a concatenate of Firstname and Lastname. I've successfully done this when seeding the database. I can also successfully update the database when a user modifies there profile by changing there names. When a user makes a change to there profile I'd like to update the "FullName" claim within the database claims table as well as the users cookie so that I can display there full name in the header of the page. The problem is I can't seem to persist the data into the table. I am able to update the claims within the User object but that's about it. I've researched and tried a dozen different things but there doesn't seem to be a solution to my problem, below is some of my code:
AppUser.cs Model
public class AppUser : IdentityUser {
[StringLength(255)]
public string FirstName { get; set; }
[StringLength(255)]
public string LastName { get; set; }
}
Helper Classes. This was derived from my research and came accross this post. I realize I'm missing part of his method, the last 2 lines, but I could never get past compiliation errors when calling the authentication manager.
public static void AddUpdateClaim(this IPrincipal currentPrincipal, string key, string value) {
var identity = currentPrincipal.Identity as ClaimsIdentity;
if (identity == null)
return;
// check for existing claim and remove it
var existingClaim = identity.FindFirst(key);
if (existingClaim != null)
identity.RemoveClaim(existingClaim);
// add new claim
identity.AddClaim(new Claim(key, value));
}
ManageController, this is the post method for updating user profile:
public async Task<IActionResult> Index(IndexViewModel model) {
if (!ModelState.IsValid) {
return View(ModelState);
}
var user = await base.GetCurrentUserAsync();
if (user != null) {
user.Email = model.Email;
user.FirstName = model.FirstName;
user.LastName = model.LastName;
User.AddUpdateClaim("FullName", "Test User");
var result = await _userManager.UpdateAsync(user);
if (result.Succeeded) {
return RedirectToAction(nameof(Index), new { Message = ManageMessageId.AccoutUpdated });
} else {
_logger.LogError(1, "Error updating user: {username}", user.UserName);
base.AddErrors(result);
return View(model);
}
}
// If we got this far, something failed, redisplay form with errors.
return View(model);
}
Any help at this point would be greatly appreciated as I've beaten my head against this wall for a while now!
I figured this out a couple days ago... basically I over complicated it. :P

Setup roles programmatically from a console application

I'm writing a setup-application for our web application. One of the tasks is to setup the database for the built-in SqlRoleProvider. I've got my script to create the database with the aspnet_regsql.exe-command, but now I've run into troubles creating default roles and user-mappings programatically.
What I'd like to do:
...
private class UserRole
{
public string Username { get; set; }
public string Role { get; set; }
}
...
const string applicationName = "foo";
var roles = new List<string> { "Administrator", "Editor" };
var userRoles =
new List<UserRole>
{
new UserRole {Username = "joli", Role = "Administrator"},
new UserRole {Username = "test", Role = "Editor"}
};
Roles.ApplicationName = applicationName;
foreach (var userRole in userRoles)
{
Roles.AddUserToRole(userRole.Username, userRole.Role);
}
...
The problem is that when running this, I get an exception saying "The rolemanager is not activated", which of course is correct because normally Roles are only handled from a web application.
I've made a workaround for this with executing the built-in stored procedures directly, but I'm curious on how to solve this programmatically.
should not be a app.config file enough?
Seems like you have not configured your Membership-provider in your web.config correctly. Can you post the section of your config where the provider is stated?

Categories