I'm trying to explore how it's possible to add additional claims to a User during the OWIN pipeline.
I know that I can do this at the login stage or at some other points, possibly in the Application_PostAuthenticate section in the Global.asax since I don't have a Login section (it's a Windows Authentication app), but I was wondering if it's possible or even better to do it in the OWIN pipeline.
My idea comes from the fact that OWIN too has a PostAuthenticate stage in the pipeline. So I tried this:
app.Use((context, next) =>
{
var user = context.Authentication.User.Identity as ClaimsIdentity;
if (user != null)
{
user.AddClaim(new Claim(ClaimTypes.Role, "Admin"));
user.AddClaim(new Claim(ClaimTypes.GivenName, "Mr. Tallmaris"));
}
return next.Invoke();
});
With a break point I can see that the user is the correct one and that the claims are added, but in my View I have something like this:
<ul>
#foreach (var claim in ClaimsPrincipal.Current.Claims)
{
<li>#claim.Type - #claim.Value</li>
}
</ul>
But my newly added claims are not showing up. Any ideas?
I know that "some" claims need to be added at Application level anyway (roles coming from DB and other things) but I would like to explore adding certain claims straight from the OWIN pipeline.
I used this principle in my asp.net application for converting groupsid claims to role claims.
app.Use((context, func) => {
var claimsIdentity = context.Authentication.User.Identity as ClaimsIdentity;
if (claimsIdentity != null) {
var claimsToAdd = new List<Claim>();
//check if we have any groupsid claims, add these as role claims
foreach (var claim in claimsIdentity.Claims) {
if (claim.Type == ClaimTypes.GroupSid) {
claimsToAdd.Add(new Claim(ClaimTypes.Role,
new SecurityIdentifier(claim.Value)
.Translate(typeof(NTAccount)).ToString()));
}
}
if (claimsToAdd.Count > 0) {
claimsIdentity.AddClaims(claimsToAdd);
}
}
return func.Invoke();
});
This worked for me using Microsoft.Owin v3.0.0.0
Related
I have an application that is in .net 4.7 and i am wanting to bring authentication over to use Azure AD from windows authentication.
I have this working but i want to use User.Identity.Name which is currently null
I have seen that i can configure this somehow within startup.cs in configuration using NameClaimTypeReceiver
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = false,
NameClaimTypeRetriever = OnNameClaimTypeRetriever
}
I cant see an example of writing this method anywhere OnNameClaimTypeRetriever
would you please be able to provide an example.
I am wanting to set it to OnPremisesSamAccountName which i can get via azure graph
NameClaimTypeRetriever is invoked from TokenValidationParameters.CreateClaimsIdentity(SecurityToken securityToken, string issuer) using those same parameters.
Example:
NameClaimTypeRetriever = (SecurityToken securityToken, string issuer) =>
{
var validUserNameTokens = new[] { "preferred_username", JwtRegisteredClaimNames.Email };
return validUserNameTokens.FirstOrDefault(token => (securityToken as JwtSecurityToken).Claims.Any(claim => token == claim.Type));
}
This checks for any claim which type is either 'preferred_username' or 'email'. If not present, returning null makes CreateClaimsIdentity() use ClaimsIdentity.DefaultNameClaimType instead.
We protect action with authorize attribute with specific role name this way
[Authorize(Roles="members, admin")]
suppose users and roles are mapped in db table. so when user login then how could i attach role with logged in user using identity.
here i am posting url and sample which show how people do the same in mvc4 with custom form authentication. just see the code and i hope surely understand what i am trying to do with asp.net mvc 5 using identity.
https://www.codeproject.com/Articles/408306/Understanding-and-Implementing-ASP-NET-Custom-Form
see this above url for custom form authentication with asp.net mvc 4
protected void FormsAuthentication_OnAuthenticate(Object sender, FormsAuthenticationEventArgs e)
{
if (FormsAuthentication.CookiesSupported == true)
{
if (Request.Cookies[FormsAuthentication.FormsCookieName] != null)
{
try
{
//let us take out the username now
string username = FormsAuthentication.Decrypt(Request.Cookies[FormsAuthentication.FormsCookieName].Value).Name;
//let us extract the roles from our own custom cookie
string roles = DBHelper.GetUserRoles(username);
//Let us set the Pricipal with our user specific details
e.User = new System.Security.Principal.GenericPrincipal(
new System.Security.Principal.GenericIdentity(username, "Forms"), roles.Split(';'));
}
catch (Exception)
{
//somehting went wrong
}
}
}
}
i am working with asp.net mvc 5 & identity system. please help and guide me. thanks
You have to get logged in user id first
var UserName= await User.Identity.GetUserId()
then you can assign any role to that logged in user like
var _context = new ApplicationDbContext();
var UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(_context));
UserManager.AddToRole("UserName", "UserRole");
Core 2.0, using AspnetCore.Identity. I created a few roles, including "Admin".
//initializing custom roles
var RoleManager = serviceProvider.GetRequiredService<RoleManager<IdentityRole>>();
var UserManager = serviceProvider.GetRequiredService<UserManager<ApplicationUser>>();
string[] roleNames = { "Admin", "Training", "Operations", "Membership", "Individual" };
IdentityResult roleResult;
foreach (var roleName in roleNames)
{
var roleExist = await RoleManager.RoleExistsAsync(roleName);
// ensure that the role does not exist
if (!roleExist)
{
//create the roles and seed them to the database:
roleResult = await RoleManager.CreateAsync(new IdentityRole(roleName));
}
}
I checked the SQL tables, and they're all there.:
Then I add myself to the Admin role (not the full method, but the relevant parts):
var UserManager = serviceProvider.GetRequiredService<UserManager<ApplicationUser>>();
var _eric = await UserManager.FindByEmailAsync("username#gmail.com");
await UserManager.AddToRoleAsync(_eric, "Admin");
I check the tables, and I'm in there (along with another guy I added to a different role):
I then travel over to my method and slap Authorize on it with two of the roles, one of which I'm in (Admin):
[Authorize(Roles ="Training, Admin")]
public ActionResult Index()
{
return View();
}
And then I get access denied. I'm missing something, but can't figure out what step I messed up. User is in there, I'm logged in, the data tables show me as having the role assigned, and the Authorize tag looks good.
Roles are claims and claims are loaded only on sign in. If you modify a claim (such as by adding a role), you must sign the user out and either automatically sign them back in or prompt the user to re-authenticate, to reload the claims.
My ASP.NET Core web app is using an Azure Active Directory tenant and using OpenID Connect to sign-in users. I'm able to login successfully and I'm able to view the full list of Claims on a user with the following code:
return new JsonResult(from c in User.Claims select new { c.Type, c.Value });
My security token includes the following "groups":
{
type: "groups",
value: "e8f1a447-336a-47bb-8c26-79f1183f989f"
},
{
type: "groups",
value: "38421450-61ba-457b-bec2-e908d42d6b92"
}
I'm having trouble trying to determine how to capture these groups to perform logic in my Razor views and controllers. For example, I need to hide/show a button in my Razor view depending on whether a user is in a specific group. In my controllers I may need to allow/deny an action.
What is the standard/preferred method to do this in ASP.NET Core?
When Azure AD adds applicable group claims to the token it issues for users, the value for the group claim will be the Object ID of the security group and not the name of the security group(a group’s name can be changed in the directory so it is not a reliable identifier for the group ) .You could check whether the user’s existence in the security group in controller by :
// Look for the groups claim for the 'Dev/Test' group.
const string devTestGroup = "99dbdfac-91f7-4a0f-8eb0-57bf422abf29";
Claim groupDevTestClaim = User.Claims.FirstOrDefault(
c => c.Type == "groups" &&
c.Value.Equals(devTestGroup, StringComparison.CurrentCultureIgnoreCase));
// If the app has write permissions and the user is in the Dev/Test group...
if (null != groupDevTestClaim)
{
//
// Code to add the resource goes here.
//
ViewBag.inGroup = true;
}
else
{
ViewBag.inGroup = false;
}
Then in view , you could control whether show/hide links/buttons :
#if (ViewBag.inGroup)
{
<div>show/hide button/link goes here</div>
}
In your AppSettings.json, add your group's name and GUID object ID:
"AzureAdAuthorizationGroups": {
"MyGroup": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"
}
Next, hook up authorisation in your Startup.cs ConfigureServices method
services.AddAuthorization(options => {
options.AddPolicy("MyGroup", policyBuilder => policyBuilder.RequireClaim("groups", Configuration.GetValue<string>("AzureAdAuthorizationGroups:MyGroup")));
});
Finally in your view:
#if ((await AuthorizationService.AuthorizeAsync(User, "MyGroup")).Succeeded)
{
// ...
}
I am using Orchard CMS v1.9 and want to display a custom registration page to accept the usual username/password/email and an additional token (invite token). The token will be used to match the user to some to custom data on the server.
I have walked through this blog Customizing User Registation With Dynamic Forms And Workflows. But in addition to what is achieved in this blog I want to force a registering user to enter a token. The token is used to lookup data on the server and create a link to the userpart.
Adding the token to the form is not the issue - its the querying and linking the entered token to the backend data and storing it in the userpart that im finding awkward.
Is this possible using just workflows - or do i need a custom module? I did not see a custom action that allowed me to match the token and link.
Is there a custom module already available that does something
similar?
Disclaimer: This approach is currently based on Orchard 1.10 but was initially developed on the 1.9.x branch. It does not rely on Dynamic Forms and Workflows, but I think you could achieve something similar with those modules.
Okay so I ended up building an example module with our approach to extended users / activation system. I stripped out a lot of code, but also let some juicy parts, which aren't directly related to your answer, in it.
First you should check out the UsersController it has the activate actions you are searching for. You may need to extend the orchard LogOn-View and include some GET & POST Actions accordingly.
[AllowAnonymous]
[HttpGet]
public ActionResult Activate(string activationCode)
{
// validation stuff....
var viewModel = new CustomUserActivate
{
// This is the activationCode you're looking for
ActivationCode = userFromActivationCode.ActivationCode,
UserName = userFromActivationCode.User.UserName,
WelcomeText = userFromActivationCode.WelcomeText,
Email = userFromActivationCode.User.Email
};
return this.View(viewModel);
}
[AllowAnonymous]
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Activate(CustomUserActivate input)
{
if ( input == null )
{
this.ModelState.AddModelError("_form", this.T("The argument cannot be null").Text);
}
CustomUserPart customUserPart = null;
if ( this.ModelState.IsValid )
{
customUserPart = this.myService.GetCustomUserByActivationCode(input.ActivationCode);
if ( customUserPart == null || customUserPart.User == null || customUserPart.User.UserName != input.UserName )
{
this.notifier.Add(NotifyType.Error, this.T("The activation failed"));
}
if ( string.IsNullOrEmpty(input.Email) )
{
this.ModelState.AddModelError("Email", this.T("You must specify an email address.").Text);
}
else if ( input.Email.Length >= 255 )
{
this.ModelState.AddModelError("Email", this.T("The email address you provided is too long.").Text);
}
else if ( !Regex.IsMatch(input.Email, UserPart.EmailPattern, RegexOptions.IgnoreCase) )
{
// http://haacked.com/archive/2007/08/21/i-knew-how-to-validate-an-email-address-until-i.aspx
this.ModelState.AddModelError("Email", this.T("You must specify a valid email address.").Text);
}
else if ( !this.myService.VerifyEmailUnicity(customUserPart.User.Id, input.Email) )
{
this.ModelState.AddModelError("Email", this.T("This email address is already in use.").Text);
}
}
if ( !this.ModelState.IsValid )
{
return this.View(input);
}
Debug.Assert(customUserPart != null, "customUserPart != null");
var user = customUserPart.User;
var userParams = new CreateUserParams(user.UserName, input.Password, input.Email, passwordQuestion: null, passwordAnswer: null, isApproved: true);
this.myService.ActivateCustomUser(customUserPart.Id, userParams);
this.notifier.Add(NotifyType.Information, this.T("Your account was activated. You can now log in."));
return this.RedirectToAction("LogOn", "Account", new { area = "Orchard.Users" });
}
The interesting stuff happens in MyService.cs.
We designed the activation system so that you can still leverage all the features of the Orchard.User Module like Email-Verifcation.
For this we've implemented some CustomSettings, where you can decide if your user get's completely activated when an ActivationCode is used or if you trigger the normal Orchard mechanism.
I guess it's best to checkout the module and step through the code in Visual Studio.
Here a two screenshots of our activation views.
Step 1 - Enter your activation code
Step 2 - Fill in the remaining fields
Profit!
All the additional source is to make use of the CustomUser / ActivationCode in Workflows, Events, Tokens, etc. But I leave this for you to discover.
If you want more detailed descriptions of the source on GitHub let me know.
Hope this helps!