Authorize an Action using URL parameters - url

I have a MVC 5 application that has controllers dressed with the [Authorize] attribute. However, requirements state that clients may not be able to login to the web application (as this web app will be deployed on signage players -- not web browsers -- that accepts URLs as input to access specific actions in the web app).
In response, I was thinking about using a URL that a client could use that would authorize their actions, in place of logging in...in the format of:
http://{website}/Action?token={/* randomly generated key for this user */}
How would I go about implementing this without changing my current code dressed with the [Authorize] attribute?

I did change my code slightly. Instead of dressing my controllers with [Authorize], I created a custom authorize attribute called [TokenAuthorize].
I created a SecurityManager class to generate and validate a token. This was adapted from Primary Objects, although I chose not to use any of their client-side code or hash using ip or userAgent. Also, I created a TokenIdentity class to create an Identity for authorizing an action. This was adapted from Steve's Coding Blog. Jelle's link also helped in authorizing a user by token using GenericPrincipal: HttpContext.Current.User = new GenericPrincipal(new TokenIdentity(username), roles.ToArray());
After implementing this, I was able to authorize an action using a url parameter called token: http://{website}/Action?token={/* randomly generated key for this user */}
TokenAuthorize.cs
public class TokenAuthorize : AuthorizeAttribute
{
private const string SecurityToken = "token";
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (Authorize(filterContext))
{
return;
}
HandleUnauthorizedRequest(filterContext);
}
private bool Authorize(AuthorizationContext actionContext)
{
try
{
HttpContextBase context = actionContext.RequestContext.HttpContext;
string token = context.Request.Params[SecurityToken];
// check if the token is valid. if so, authorize action.
bool isTokenAuthorized = SecurityManager.IsTokenValid(token);
if (isTokenAuthorized) return true;
// if the token is not valid, check if the user is authorized by default.
bool isDefaultAuthorized = AuthorizeCore(context);
return isDefaultAuthorized;
}
catch (Exception)
{
return false;
}
}
}
SecurityManager.cs (adapted from Primary Objects)
public class SecurityManager
{
private const string Alg = "HmacSHA256";
private const string Salt = "rz8LuOtFBXphj9WQfvFh";
// Generates a token to be used in API calls.
// The token is generated by hashing a message with a key, using HMAC SHA256.
// The message is: username
// The key is: password:salt
public static string GenerateToken(string username, string password)
{
string hash = string.Join(":", new string[] { username });
string hashLeft;
string hashRight;
using (HMAC hmac = HMAC.Create(Alg))
{
hmac.Key = Encoding.UTF8.GetBytes(GetHashedPassword(password));
hmac.ComputeHash(Encoding.UTF8.GetBytes(hash));
hashLeft = Convert.ToBase64String(hmac.Hash);
hashRight = string.Join(":", username);
}
return Convert.ToBase64String(Encoding.UTF8.GetBytes(string.Join(":", hashLeft, hashRight)));
}
// used in generating a token
private static string GetHashedPassword(string password)
{
string key = string.Join(":", new string[] { password, Salt });
using (HMAC hmac = HMAC.Create(Alg))
{
// Hash the key.
hmac.Key = Encoding.UTF8.GetBytes(Salt);
hmac.ComputeHash(Encoding.UTF8.GetBytes(key));
return Convert.ToBase64String(hmac.Hash);
}
}
// Checks if a token is valid.
public static bool IsTokenValid(string token)
{
var context = new ApplicationDbContext();
try
{
// Base64 decode the string, obtaining the token:username.
string key = Encoding.UTF8.GetString(Convert.FromBase64String(token));
// Split the parts.
string[] parts = key.Split(':');
if (parts.Length != 2) return false;
// Get the username.
string username = parts[1];
// Get the token for said user
// var computedToken = ...
// Compare the computed token with the one supplied and ensure they match.
var result = (token == computedToken);
if (!result) return false;
// get roles for user (ASP.NET Identity 2.0)
var user = context.Users.Single(u => u.UserName == username);
var rolesPerUser = context.UserRoles.Where(x => x.UserId == user.Id).ToList();
var roles = rolesPerUser.Select(role => context.Roles.Single(r => r.Id == role.RoleId).Name);
// NOTE: define public DbSet<IdentityUserRole> UserRoles { get; set; } ...
// ... in your DbContext in IdentityModels.cs
HttpContext.Current.User = new GenericPrincipal(new TokenIdentity(username), roles.ToArray());
return true;
}
catch
{
return false;
}
}
}
TokenIdentity.cs (adapted from Steve's Coding Blog)
public class TokenIdentity : IIdentity
{
public string User { get; private set; }
public TokenIdentity(string user)
{
this.User = user;
}
public string Name => User;
public string AuthenticationType => "ApplicationToken";
public bool IsAuthenticated => true;
}

Related

Implement Active Directory login in an existing ASP.NET MVC 4.6 web project

I have to change the existing (Windows) login for my ASP.NET MVC + Knockout application with Active Directory authentication.
It consists of mvc controllers and webapi controllers. Both have to be authenticated.
I thought I would do this by changing to forms authentication and create a login page and when the users clicks login, query Active Directory with the System.DirectoryServices.DirectoryEntry.
Then the other processes like change password, register etc. would also get a custom html page and do their actions via the System.DirectoryServices.DirectoryEntry on our Active Directory.
(that is, I could not find any other way that people would do it, and I did find some who would do it like this, and it sounds the same like previous forms authentications I've seen. In this case the user/passwords would not be in a database table but in Active Directory. Same idea, swap database table by active directory).
To see how this would be on a brandnew project, I created a new ASP.NET MVC project and choose 'work- or school acounts' (which says 'for applications that authenticate users with active directory) and choose 'on premise'.
However, then I have to provide these items:
on-premises authority
app Id url
I don't know what to do with that. The only thing I have is an active directory url like ldap://etc..
Is this another/newer/better way of doing active directory login? Or the only right one (is forms authentication wrong?) or the wrong one?
I'm confused.
You can use the following approach in order to implement Active Directory Authentication in ASP.NET MVC.
Step 1: Modify the Login methods in the AccountController as shown below (also add the necessary references):
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
try
{
if (!ModelState.IsValid)
{
return View(model);
}
// Check if the User exists in LDAP
if (Membership.GetUser(model.UserName) == null)
{
ModelState.AddModelError("", "Wrong username or password");
return this.View(model);
}
ApplicationGroupManager groupManager = new ApplicationGroupManager();
// Validate the user using LDAP
if (Membership.ValidateUser(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
// FormsAuthentication.SetAuthCookie(model.UserName, false);
// Check if the User exists in the ASP.NET Identity table (AspNetUsers)
string userName = model.UserName.ToString().ToLower(new CultureInfo("en-US", false)); // When UserName is entered in uppercase containing "I", the user cannot be found in LDAP
//ApplicationUser user = UserManager.FindByName(userName);
ApplicationUser user = await UserManager.FindByNameAsync(userName); //Asynchronous method
if (user == null) // If the User DOES NOT exists in the ASP.NET Identity table (AspNetUsers)
{
// Create a new user using the User data retrieved 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);
user = new ApplicationUser();
// For more information about "User Attributes - Inside Active Directory" : http://www.kouti.com/tables/userattributes.htm
user.UserName = userInfo.GetDirectoryEntry().Properties["samaccountname"].Value.ToString();
user.Name = userInfo.GetDirectoryEntry().Properties["givenname"].Value.ToString();
user.Surname = userInfo.GetDirectoryEntry().Properties["sn"].Value.ToString();
user.Email = userInfo.GetDirectoryEntry().Properties["mail"].Value.ToString();
user.EmailConfirmed = true;
//user.PasswordHash = null;
//user.Department = GetDepartmentId(userInfo.GetDirectoryEntry().Properties["physicalDeliveryOfficeName"].Value.ToString());
//await Register(user);
var result = await UserManager.CreateAsync(user); //Asynchronous method
//If the User has succesfully been created
//if (result.Succeeded)
//{
// //var code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
// //var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
// //await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking this link: link");
// //ViewBag.Link = callbackUrl;
// //return View("DisplayEmail");
//}
// Define user group (and roles)
var defaultGroup = "751b30d7-80be-4b3e-bfdb-3ff8c13be05e"; // Id of the ApplicationGroup for the Default roles
//groupManager.SetUserGroups(newUser.Id, new string[] { defaultGroup });
await groupManager.SetUserGroupsAsync(user.Id, new string[] { defaultGroup }); //Asynchronous method
//groupManager.SetGroupRoles(newGroup.Id, new string[] { role.Name });
}
// !!! THERE IS NO NEED TO ASSIGN ROLES AS IT IS ASSIGNED AUTOMATICALLY IN ASP.NET Identity 2.0
//else // If the User exists in the ASP.NET Identity table (AspNetUsers)
//{
// //##################### Some useful ASP.NET Identity 2.0 methods (for Info) #####################
// //ApplicationGroupManager gm = new ApplicationGroupManager();
// //string roleName = RoleManager.FindById("").Name; // Returns Role Name by using Role Id parameter
// //var userGroupRoles = gm.GetUserGroupRoles(""); // Returns Group Id and Role Id by using User Id parameter
// //var groupRoles = gm.GetGroupRoles(""); // Returns Group Roles by using Group Id parameter
// //string[] groupRoleNames = groupRoles.Select(p => p.Name).ToArray(); // Assing Group Role Names to a string array
// //###############################################################################################
// // Assign Default ApplicationGroupRoles to the User
// // As the default roles are already defined to the User after the first login to the system, there is no need to check if the role is NULL (otherwise it must be checked!!!)
// //var groupRoles = groupManager.GetGroupRoles("751b30d7-80be-4b3e-bfdb-3ff8c13be05e"); // Returns Group Roles by using Group Id parameter
// var groupRoles = await groupManager.GetGroupRolesAsync("751b30d7-80be-4b3e-bfdb-3ff8c13be05e"); // Returns Group Roles by using Group Id parameter (Asynchronous method)
// foreach (var role in groupRoles)
// {
// //Assign ApplicationGroupRoles to the User
// string roleName = RoleManager.FindById(role.Id).Name;
// UserManager.AddToRole(user.Id, roleName);
// }
//}
//Sign in the user
await SignInAsync(user, model.RememberMe);
if (this.Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
&& !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
{
return this.Redirect(returnUrl);
//return RedirectToLocal(returnUrl);
}
return this.RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError("", "Wrong username or password");
return this.View(model);
}
}
catch (Exception ex)
{
TempData["ErrorMessage"] = ex.Message.ToString();
return View("Error", TempData["ErrorMessage"]);
}
}
/* Since ASP.NET Identity and OWIN Cookie Authentication are claims-based system, the framework requires the app to generate a ClaimsIdentity for the user.
ClaimsIdentity has information about all the claims for the user, such as what roles the user belongs to. You can also add more claims for the user at this stage.
The highlighted code below in the SignInAsync method signs in the user by using the AuthenticationManager from OWIN and calling SignIn and passing in the ClaimsIdentity. */
private async Task SignInAsync(ApplicationUser user, bool isPersistent)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}
static SearchResult CreateDirectoryEntry(string sAMAccountName, string[] requiredProperties)
{
DirectoryEntry ldapConnection = null;
try
{
// Create LDAP connection object
//ldapConnection = new DirectoryEntry("alpha.company.com");
ldapConnection = new DirectoryEntry("LDAP://OU=Company_Infrastructure, DC=company, DC=mydomain", "******", "******");
//ldapConnection.Path = connectionPath;
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();
if (result != null)
{
//foreach (String property in requiredProperties)
// foreach (Object myCollection in result.Properties[property])
// Console.WriteLine(String.Format("{0,-20} : {1}",
// property, myCollection.ToString()));
// return searchResultCollection;
return result;
}
else
{
return null;
//Console.WriteLine("User not found!");
}
//return ldapConnection;
}
catch (Exception e)
{
Console.WriteLine("Exception caught:\n\n" + e.ToString());
}
return null;
}
Note: In order to force signout in LDAP authentication, add FormsAuthentication.SignOut() line to the LogOff() method as shown below:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
AuthenticationManager.SignOut();
FormsAuthentication.SignOut(); //In order to force logout in LDAP authentication
return RedirectToAction("Login", "Account");
}
Step 2: Update your LoginViewModel (or whatever your Account model class is named) to contain only this LoginModel class:
public class LoginViewModel
{
[Required]
public string UserName { get; set; }
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
public bool RememberMe { get; set; }
}
On the other hand, add the custom properties i.e. Name, Surname, UserName, Department, etc. to the necessary model i.e. ApplicationUser, RegisterViewModel.
Step 3: Finally, update your Web.config file to include these elements:
<connectionStrings>
<!-- for LDAP -->
<add name="ADConnectionString" connectionString="LDAP://**.**.***:000/DC=abc,DC=xyz" />
</connectionStrings>
<system.web>
<!-- For LDAP -->
<httpCookies httpOnlyCookies="true" />
<authentication mode="Forms">
<forms name=".ADAuthCookie" loginUrl="~/Account/Login" timeout="30" slidingExpiration="true" protection="All" />
</authentication>
<membership defaultProvider="ADMembershipProvider">
<providers>
<clear />
<add name="ADMembershipProvider" type="System.Web.Security.ActiveDirectoryMembershipProvider" connectionStringName="ADConnectionString" attributeMapUsername="sAMAccountName" connectionUsername="******" connectionPassword="******" />
</providers>
</membership>
...
</system.web>
Update: Here is the ApplicationUser class used in the example:
// Must be expressed in terms of our custom Role and other types:
public class ApplicationUser : IdentityUser<int, ApplicationUserLogin,
ApplicationUserRole, ApplicationUserClaim>, IUser<int>
{
public string Name { get; set; }
public string Surname { get; set; }
public async Task<ClaimsIdentity>
GenerateUserIdentityAsync(UserManager<ApplicationUser, int> manager)
{
var userIdentity = await manager
.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
return userIdentity;
}
}
I have used Active Directory Authetication at work.
I created application MVC with Windows Authetication and it was done. Application automatically displays AD login with domain.
Choose membership: [Authorize(Roles=#"DomainName\GroupName")]
Ye can see domain and groups in cmd: net user Username /domain
You haven't to use LDAP.
With LDAP see:
see here
i have a mixed system running. Users from a Database (external users) mixed with AD users (internal users) that can login to our system. to communicate with the AD i use a nuget package called LinqToLdap (https://github.com/madhatter22/LinqToLdap). this uses the LDAP protocol so it can be used for authenticating against Unix Ldap servers also.
this is the Authentication method
public bool AuthenticateUser(string userName, string password)
{
InitConfig();
using (var context = new DirectoryContext(_config))
{
var user = context.Query<LdapUserInfo>().FirstOrDefault(x => x.UserPrincipalName.Equals(userName));
var dn = user?.DistinguishedName;
if (string.IsNullOrWhiteSpace(dn))
return false;
using (var ldap = new LdapConnection(new LdapDirectoryIdentifier(_myConfig.Server)))
{
ldap.SessionOptions.ProtocolVersion = 3;
ldap.AuthType = AuthType.Basic;
ldap.Credential = _credentials;
ldap.Bind();
try
{
ldap.AuthType = AuthType.Basic;
ldap.Bind(new NetworkCredential(dn, password));
return true;
}
catch (DirectoryOperationException)
{ }
catch (LdapException)
{ }
}
return false;
}
}
private void InitConfig()
{
if (_config != null)
return;
_config = new LdapConfiguration();
_credentials = new NetworkCredential(_myConfig.Username, _myConfig.Password, _myConfig.Domain);
_config.AddMapping(new AutoClassMap<LdapGroupInfo>(), _myConfig.NamingContext, new[] { "*" });
_config.AddMapping(new AutoClassMap<LdapUserInfo>(), _myConfig.NamingContext, new[] { "*" });
_config.ConfigureFactory(_myConfig.Server).AuthenticateAs(_credentials).AuthenticateBy(AuthType.Basic);
}

Secure Web Api to consumed by console Appplicatuion [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I have created one Asp core web api which will be consumed by C# console application outside the organization. This console application is scheduled to run periodically . So hits on Web api will come when console application run.
Please assist How Can i secure my Web Api to malware hit or unauthentic access. I can't use AD authentication as I am unable to register client application in AAD(Azure active directory) Please assist.
Generally speaking , there're lots ways to do that . For example , use a basic scheme authentication in which the client sends username:password with the base64-encoding . However . It's not that safe .
I suggest you use JWT token . The authentication of Jwt scheme is dead simple :
The client send a request to ask for a JWT token with client_id and client_key . (You might configure them in configuration file or database on server)
Tf the client_id and client_key matches , the Server send a response with a JWT access token , maybe an additional refresh token if you like ; otherwise , send a response with a 401.
The client consumes webapi with a Authorization: Bearer ${access_token} header. The server will decrypt the access_token and hit the correct action if valid.
Here's a how-to in details:
Dummy class to hold information
To represent the client_id and client_key sent by your console , Let's create a dummy Dto class :
public class AskForTokenRequest
{
public string ClientId { get; set; }
public string ClientKey { get; set; }
}
When creating and validating Jwt token , we need information about issuer , audience , and secret keys . To hold these information , let's create another dummy class :
public class SecurityInfo {
public static readonly string Issuer = "xxx";
public static readonly string[] Audiences = new[] { "yyy" };
public static readonly string SecretKey = "!##$%^&*()&!!!##$%^&*()&!!!##$%^&*()&!!!##$%^&*()&!!!##$%^&*()&!";
}
Before we move on , let's create a JwtTokenHelper to generate token :
The JwtTokenHelper helps to validate client_id & client_key and generate Jwt Token .
public class JwtTokenHelper
{
//private AppDbContext _dbContext { get; set; }
//public JwtTokenHelper(AppDbContext dbContext) {
// this._dbContext = dbContext;
//}
public virtual bool ValidateClient(string clientId, string clientKey)
{
// check the client_id & clientKey with database , config file , or sth else
if (clientId == "your_console_client_id" && clientKey == "your_console_client_key")
return true;
return false;
}
/// construct a token
public virtual JwtSecurityToken GenerateToken(string clientId, DateTime expiry, string audience)
{
ClaimsIdentity identity = new ClaimsIdentity(new GenericIdentity(clientId, "jwt"));
var token=new JwtSecurityToken
(
claims: identity.Claims,
issuer: SecurityInfo.Issuer,
audience: audience,
expires: expiry,
notBefore: DateTime.UtcNow,
signingCredentials: new SigningCredentials(
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecurityInfo.SecretKey)),
SecurityAlgorithms.HmacSha256
)
);
return token;
}
public virtual string GenerateTokenString(string clientId, DateTime expiry,string audience)
{
// construct a jwt token
var token = GenerateToken(clientId,expiry,audience);
// convert the token to string
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
return tokenHandler.WriteToken(token);
}
}
Configure the server to enable JwtBearer authentication :
Add JwtTokenHelper to DI Container and Add authentication scheme of JwtBearer
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<JwtTokenHelper>();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.RequireHttpsMetadata = false;
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = SecurityInfo.Issuer,
ValidAudiences = SecurityInfo.Audiences,
ValidateAudience = true,
ValidateIssuer = true,
ValidateIssuerSigningKey = true,
IssuerSigningKeys = new List<SecurityKey> {
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(SecurityInfo.SecretKey) )
},
ValidateLifetime = true,
ClockSkew = TimeSpan.FromMinutes(60)
};
});
services.AddMvc();
}
Don't forget to add app.UseAuthentication(); in your Configure() method .
How to use:
Now , Create a controller to generate Jwt token
[Route("/api/token")]
public class TokenController : Controller
{
private readonly JwtTokenHelper _tokenHelper;
public TokenController(JwtTokenHelper tokenHelper) {
this._tokenHelper = tokenHelper;
}
[HttpPost]
public IActionResult Create([FromBody] AskForTokenRequest client)
{
if(! this._tokenHelper.ValidateClient(client.ClientId , client.ClientKey))
return new StatusCodeResult(401);
DateTime expiry = DateTime.UtcNow.AddMinutes(60); // expires in 1 hour
var audience = "yyy";
var access_token = this._tokenHelper.GenerateTokenString(client.ClientKey, expiry,audience);
return new JsonResult(new {
access_token = access_token,
});
}
}
and protect you webapi with [Authorize] attribute :
public class HomeController : Controller
{
[Authorize]
public IActionResult GetYourWebApiMethod(){
return new ObjectResult(new {
Username = User.Identity.Name
});
}
}

Remove User from Roles in ASP.NET Identity 2.x

How can I remove User from Roles in ASP.NET Identity 2.x ?
about adding role to user there is no problem but when I want to remove a role from a user I cannot.It should be mentioned that there is no exception or error!
//POST: Admin/User/Edit/5
[AcceptVerbs(HttpVerbs.Post)]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit([Bind(Prefix = "")]UserViewModel userViewModel, List<int> availableRoles)
{
if (ModelState.IsValid)
{
List<int> newListOfRolesIDs = availableRoles;
List<int> oldListOfRolesIDs = UserBLL.Instance.GetRolesIDs(userViewModel.Id);
List<int> deletedList;
List<int> addedList;
var haschanged = oldListOfRolesIDs.ChangeTracking(newListOfRolesIDs, out deletedList, out addedList);
using (new EFUnitOfWorkFactory().Create())
{
if (haschanged)
{
UserBLL.Instance.InsertRoles(addedList, userViewModel.Id);
UserBLL.Instance.DeleteRoles(deletedList, userViewModel.Id);
}
await UserBLL.Instance.UpdateAsync(userViewModel);
}
//ArticleBLL.Instance.UpdatePartial(articleViewModel, m => m.Title);
return RedirectToAction("Edit");
}
return View(userViewModel);
}
Delete Role method:
public void DeleteRoles(List<int> deleteList, int? userId)
{
if (userId != null)
{
User user = UserManager.FindByIdAsync(userId.Value).Result;
foreach (var i in deleteList)
{
user.Roles.Remove(new UserRole { RoleId = i, UserId = user.Id }); // What's the problem?!
}
}
}
Insert Role method:
public void InsertRoles(List<int> insertList, int? userId)
{
if (userId != null)
{
User user = UserManager.FindByIdAsync(userId.Value).Result;
foreach (var i in insertList)
{
user.Roles.Add(new UserRole { RoleId = i, UserId = user.Id });
}
}
}
What you are looking for is the RemoveFromRoleAsync method. An example would look similar to the following:
public async Task DeleteRolesAsync(List<string> deleteList, int? userId)
{
if (userId != null)
{
foreach (var roleName in deleteList)
{
IdentityResult deletionResult = await UserManager.RemoveFromRoleAsync(userId, roleName);
}
}
}
If you already have the ID of the user, there's no need to get the user again (only if you want to make sure that the user really exists; then you have to wrap your foreach with an if-statement). The deletion methods needs the name of the role, instead of the ID, to delete the user from the role. You can use the result of the operation (in my example stored in deletionResult) to make sure that the operation was successful. Remember that the name of the user manager (in my example UserManager) can vary depending on your implementation.
I had the same issue and what I ended up using was the
RemoveFromRolesAsync(string userId, params string[] roles) Method
from the UserManager.
Using the role names in an array works.
But has an issue that is if the user is not in one of the roles in the array the user will not be removed from any roles in the array.
All or nothing.
var usr = UserManager.FindById(usrV.ID.ToString());
string[] deleteList;
deleteList= new string[1];
deleteList[0] = "Engineer";
var rresult1 = UserManager.RemoveFromRolesAsync(usr.Id, deleteList);
Hope it helps
You might want to check out this blog post. The ASP.NET team has a sample that includes adding and removing roles from a user.
ASP.NET Identity 2.0: Customizing Users and Roles

How do I convert a refresh token to an access token using the LiveConnect API (C#)

I'm trying to create a LiveConnectClient with only a refresh token that was provided to me via asp.net identity (using OWIN) and the ProviderKey. It looks like the only way to do this without needing HttpContextBase is via InitializeSessionAsync.
When I try and create the client I'm getting:
Microsoft.Live.LiveAuthException: The user ID from the given RefreshTokenInfo instance does not match the refresh token.
Not really sure what user ID it is expecting as I'm giving it the provider key that was passed via ASP.NET Identity (17 chars in my case). Below is my code.
public class Class1
{
protected async Task<LiveConnectClient> GetLiveConnectClient()
{
var authClient = new LiveAuthClient(_clientId, _clientSecret, null, new RefreshTokenHandler(_refreshToken, _providerKey));
var session = await authClient.InitializeSessionAsync("http://.../signin-microsoft");
return new LiveConnectClient(session.Session);
}
}
public class RefreshTokenHandler : IRefreshTokenHandler
{
private readonly string _refreshToken;
private readonly string _userId;
public RefreshTokenHandler(string refreshToken, string userId)
{
_refreshToken = refreshToken;
_userId = userId;
}
public Task<RefreshTokenInfo> RetrieveRefreshTokenAsync()
{
return Task.FromResult(new RefreshTokenInfo(_refreshToken, _userId));
}
public async Task SaveRefreshTokenAsync(RefreshTokenInfo tokenInfo)
{
await Task.Delay(0);
}
}

Asp.net MVC Let user switch between roles

I'm developing a complex website with users having multiple roles. The users are also coupled on other items in the DB which, together with their roles, will define what they can see and do on the website.
Now, some users have more than 1 role, but the website can only handle 1 role at a time because of the complex structure.
the idea is that a user logs in and has a dropdown in the corner of the website where he can select one of his roles. if he has only 1 role there is no dropdown.
Now I store the last-selected role value in the DB with the user his other settings. When he returns, this way the role is still remembered.
The value of the dropdown should be accessible throughout the whole website.
I want to do 2 things:
Store the current role in a Session.
Override the IsInRole method or write a IsCurrentlyInRole method to check all access to the currently selected Role, and not all roles, as does the original IsInRole method
For the Storing in session part I thought it'd be good to do that in Global.asax
protected void Application_AuthenticateRequest(Object sender, EventArgs e) {
if (User != null && User.Identity.IsAuthenticated) {
//check for roles session.
if (Session["CurrentRole"] == null) {
NASDataContext _db = new NASDataContext();
var userparams = _db.aspnet_Users.First(q => q.LoweredUserName == User.Identity.Name).UserParam;
if (userparams.US_HuidigeRol.HasValue) {
var role = userparams.aspnet_Role;
if (User.IsInRole(role.LoweredRoleName)) {
//safe
Session["CurrentRole"] = role.LoweredRoleName;
} else {
userparams.US_HuidigeRol = null;
_db.SubmitChanges();
}
} else {
//no value
//check amount of roles
string[] roles = Roles.GetRolesForUser(userparams.aspnet_User.UserName);
if (roles.Length > 0) {
var role = _db.aspnet_Roles.First(q => q.LoweredRoleName == roles[0].ToLower());
userparams.US_HuidigeRol = role.RoleId;
Session["CurrentRole"] = role.LoweredRoleName;
}
}
}
}
}
but apparently this gives runtime errors. Session state is not available in this context.
How do I fix this, and is this
really the best place to put this
code?
How do I extend the user (IPrincipal?) with IsCurrentlyInRole without losing all other functionality
Maybe i'm doing this all wrong and there is a better way to do this?
Any help is greatly appreciated.
Yes, you can't access session in Application_AuthenticateRequest.
I've created my own CustomPrincipal. I'll show you an example of what I've done recently:
public class CustomPrincipal: IPrincipal
{
public CustomPrincipal(IIdentity identity, string[] roles, string ActiveRole)
{
this.Identity = identity;
this.Roles = roles;
this.Code = code;
}
public IIdentity Identity
{
get;
private set;
}
public string ActiveRole
{
get;
private set;
}
public string[] Roles
{
get;
private set;
}
public string ExtendedName { get; set; }
// you can add your IsCurrentlyInRole
public bool IsInRole(string role)
{
return (Array.BinarySearch(this.Roles, role) >= 0 ? true : false);
}
}
My Application_AuthenticateRequest reads the cookie if there's an authentication ticket (user has logged in):
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
HttpCookie authCookie = Request.Cookies[My.Application.FORMS_COOKIE_NAME];
if ((authCookie != null) && (authCookie.Value != null))
{
Context.User = Cookie.GetPrincipal(authCookie);
}
}
public class Cookie
{
public static IPrincipal GetPrincipal(HttpCookie authCookie)
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
if (authTicket != null)
{
string ActiveRole = "";
string[] Roles = { "" };
if ((authTicket.UserData != null) && (!String.IsNullOrEmpty(authTicket.UserData)))
{
// you have to parse the string and get the ActiveRole and Roles.
ActiveRole = authTicket.UserData.ToString();
Roles = authTicket.UserData.ToString();
}
var identity = new GenericIdentity(authTicket.Name, "FormAuthentication");
var principal = new CustomPrincipal(identity, Roles, ActiveRole );
principal.ExtendedName = ExtendedName;
return (principal);
}
return (null);
}
}
I've extended my cookie adding the UserData of the Authentication Ticket. I've put extra-info here:
This is the function which creates the cookie after the loging:
public static bool Create(string Username, bool Persistent, HttpContext currentContext, string ActiveRole , string[] Groups)
{
string userData = "";
// You can store your infos
userData = ActiveRole + "#" string.Join("|", Groups);
FormsAuthenticationTicket authTicket =
new FormsAuthenticationTicket(
1, // version
Username,
DateTime.Now, // creation
DateTime.Now.AddMinutes(My.Application.COOKIE_PERSISTENCE), // Expiration
Persistent, // Persistent
userData); // Additional informations
string encryptedTicket = System.Web.Security.FormsAuthentication.Encrypt(authTicket);
HttpCookie authCookie = new HttpCookie(My.Application.FORMS_COOKIE_NAME, encryptedTicket);
if (Persistent)
{
authCookie.Expires = authTicket.Expiration;
authCookie.Path = FormsAuthentication.FormsCookiePath;
}
currentContext.Response.Cookies.Add(authCookie);
return (true);
}
now you can access your infos everywhere in your app:
CustomPrincipal currentPrincipal = (CustomPrincipal)HttpContext.User;
so you can access your custom principal members: currentPrincipal.ActiveRole
When the user Changes it's role (active role) you can rewrite the cookie.
I've forgot to say that I store in the authTicket.UserData a JSON-serialized class, so it's easy to deserialize and parse.
You can find more infos here
If you truly want the user to only have 1 active role at a time (as implied by wanting to override IsInRole), maybe it would be easiest to store all of a user's "potential" roles in a separate place, but only actually allow them to be in 1 ASP.NET authentication role at a time. When they select a new role use the built-in methods to remove them from their current role and add them to the new one.

Resources