Refreshing claimsPrincipal after changing roles - c#

I'm having some issues with changing role in dotnetcore identity.
I have the following code.
private async Task SetRoleToX(ClaimsPrincipal claimsPrincipal, string X)
{
var currentUser = await UserManager.GetUserAsync(claimsPrincipal);
var roles = await UserManager.GetRolesAsync(currentUser);
await UserManager.RemoveFromRolesAsync(currentUser, roles);
await UserManager.AddToRoleAsync(currentUser, X);
await SignInManager.RefreshSignInAsync(currentUser);
}
I cannot get the ClaimsPrincipal to update.
I have tried using sign in and sign out.
The role switch works fine if I manually sign in and out.
I have been searching the web and alot of people say this should work :(

Rather annoyingly all I had to do was send the token back with the request.
I cant believe i didn't think of it hope this helps someone.
Update with some code as requested
// In controller
public async Task SwapRole([FromBody]RoleSwapRequestDto dto)
{
await _service.SwapRole(
User,
dto.RoleName
);
return await AddCookieToResponse();
}
private async Task AddCookieToResponse()
{
// Make your token however your app does this (generic dotnet core stuff.)
var response = await _tokenService.RegenToken(User);
if (response.Data != null && response.Data.Authenticated && response.Data.TokenExpires.HasValue)
{
Response.Cookies.Append(AuthToken, response.Data.Token, new CookieOptions
{
HttpOnly = false,
Expires = response.Data.TokenExpires.Value
});
}
return response;
}
/// inside _service
public async Task SwapRole(ClaimsPrincipal claimsPrincipal, string X)
{
var currentUser = await UserManager.GetUserAsync(claimsPrincipal);
var roles = await UserManager.GetRolesAsync(currentUser);
await UserManager.RemoveFromRolesAsync(currentUser, roles);
await UserManager.AddToRoleAsync(currentUser, X);
await SignInManager.RefreshSignInAsync(currentUser);
}

Related

ASP MVC ConfirmEmailAsync not working

I register a user, receive a token via email which looks like this:
Please confirm your account by clicking here
I click the link and I can see that the ConfirmEmail method in AccountController fires:
[AllowAnonymous]
public async Task<ActionResult> ConfirmEmail(string userId, string code)
{
if (userId == null || code == null)
{
return View("Error");
}
var result = await UserManager.ConfirmEmailAsync(userId, code);
return View(result.Succeeded ? "ConfirmEmail" : "Error");
}
And that result.Succeeded is true.
Everything appears fine, but when trying to log in after completing this process I get taken to the page telling me my account is locked
Locked out.
This account has been locked out, please try again later.
What couldI be doing wrong? Do I need to manually change the lockout flag in the db? If so, what is the point of the ConfirmEmailAsync method?
ConfirmEmailAsync just sets the EmailConfirmed on the user account record to true. From UserManager (edited for brevity):
public virtual async Task<IdentityResult> ConfirmEmailAsync(TUser user, string token)
{
...
var store = GetEmailStore();
...
await store.SetEmailConfirmedAsync(user, true, CancellationToken);
return await UpdateUserAsync(user);
}
Where GetEmailStore returns the IUserEmailStore (which is implemented by UserStore by default), which sets the flag:
public virtual Task SetEmailConfirmedAsync(TUser user, bool confirmed, CancellationToken cancellationToken = default(CancellationToken))
{
...
user.EmailConfirmed = confirmed;
return Task.CompletedTask;
}
The error you're getting indicated that the LockoutEnabled flag on the user account is true. You can set this to false by calling the SetLockoutEnabledAsync method on the UserManager.
There is also a SupportsUserLockout flag on the UserManager which unlocks accounts by default on creation. In order to set this you will need to create your own UserManager and override this flag to false.
At first, I had challenges getting these to work and after a series of research no success. Finally, I got the root of the problem(s) and fixed them thus sharing my experience. Follow the following process and I am sure it will help.
Step 1
Goto Startup.cs and remove the code below if you have it initialised;
services.Configure<RouteOptions>(options =>
{
options.LowercaseUrls = true;
//options.LowercaseQueryStrings = true; //(comment or remove this line)
});
Step 2 For GenerateEmailConfirmationTokenAsync() / ConfirmEmailAsync()
2a. On registering new user for token generation go as thus;
var originalCode = await userManager.GenerateEmailConfirmationTokenAsync(user);
var code = HttpUtility.UrlEncode(originalCode);
var confirmationLink = Url.Action("ConfirmEmail", "Account",
new { userId = user.Id, token = code }, Request.Scheme);
2b. On receiving confrimationLink for email confirmation, go as thus
var originalCode = HttpUtility.UrlDecode(token);
var result = await userManager.ConfirmEmailAsync(user, originalCode);
if (result.Succeeded)
{
return View(); //this returns login page if successful
}
For GeneratePasswordResetTokenAsync() and ResetPasswordAsync()
a.
var originalCode = await userManager.GeneratePasswordResetTokenAsync(user);
var code = HttpUtility.UrlEncode(originalCode);
var passwordResetLink = Url.Action("ResetPassword", "Account",
new { email = model.Email, token = code }, Request.Scheme);
b.
var orginalCode = HttpUtility.UrlDecode(model.Token);
var result = await userManager.ResetPasswordAsync(user, orginalCode, model.Password);
if (result.Succeeded)
{
return View("ResetPasswordConfirmation");
}

async getting no where

I am refactoring my ASP MVC code in session_start in Global.asax.cs with an async call to external service. I either get a white page with endless spinning in IE, or execution immediately returns to the calling thread. In the Session_start() when I tried .Result, I got white page with spinning IE icon. When I tried .ContinueWith(), the execution return to the next line which depends on the result from the async. Thus authResult is always null. Can someone help? Thanks.
This is from the Session_Start()
if (Session["userProfile"] == null) {
//call into an async method
//authResult = uc.checkUserViaWebApi(networkLogin[userLoginIdx]).Result;
var userProfileTask = uc.checkUserViaWebApi(networkLogin[userLoginIdx])
.ContinueWith(result => {
if (result.IsCompleted) {
authResult = result.Result;
}
});
Task.WhenAll(userProfileTask);
if (authResult.Result == enumAuthenticationResult.Authorized) {
This is from User_Controller class
public async Task < AuthResult > checkUserViaWebApi(string networkName) {
UserProfile _thisProfile = await VhaHelpersLib.WebApiBroker.Get < UserProfile > (
System.Configuration.ConfigurationManager.AppSettings["userWebApiEndpoint"], "User/Profile/" + networkName);
AuthResult authenticationResult = new AuthResult();
if (_thisProfile == null) /*no user profile*/ {
authenticationResult.Result = enumAuthenticationResult.NoLSV;
authenticationResult.Controller = "AccessRequest";
authenticationResult.Action = "LSVInstruction";
}
This is helper class that does the actual call using HttpClient
public static async Task<T> Get<T>(string baseUrl, string urlSegment)
{
string content = string.Empty;
using(HttpClient client = GetClient(baseUrl))
{
HttpResponseMessage response = await client.GetAsync(urlSegment.TrimStart('/')).ConfigureAwait(false);
if(response.IsSuccessStatusCode)
{
content = await response.Content.ReadAsStringAsync();
}
return JsonConvert.DeserializeObject<T>(content);
}
It doesn't make sense to call User_Controller from Session_Start.
You want to call VhaHelpersLib directly inside Session_Start, if VhaHelpersLib doesn't have any dependencies.
Since Session_Start is not async, you want to use Result.
var setting = ConfigurationManager.AppSettings["userWebApiEndpoint"];
UserProfile profile = await VhaHelpersLib.WebApiBroker.Get<UserProfile>(
setting, "User/Profile/" + networkName).Result;
if (profile == enumAuthenticationResult.Authorized) {
...
}

Fetching User by Id does not return

I have made a successful connection to Azure AD (at least I hope so, unable to tell the details), and now I am trying to get some user's details. But the step-through debugger does not proceed over the following line:
IUser result = azureDirectoryClient.Users.GetByObjectId(someId).ExecuteAsync().Result;
That I got until here, no error is thrown and the function does not return - what does that tell me? How can I debug the issue further?
What do you catch when you debug this:
try
{
IUser result = azureDirectoryClient.Users.GetByObjectId(objectId).ExecuteAsync().Result;
}
catch(exception e)
{
console.white(e.message)
}
How do you connect to Azure AD? Make sure you get the accurate Accesstoken:
public static string GetTokenForApplication()
{
AuthenticationContext authenticationContext = new AuthenticationContext(Constants.AuthString, false);
// Config for OAuth client credentials
ClientCredential clientCred = new ClientCredential(Constants.ClientId, Constants.ClientSecret);
AuthenticationResult authenticationResult = authenticationContext.AcquireToken(Constants.ResourceUrl, clientCred);
string token = authenticationResult.AccessToken;
return token;
}
ActiveDirectoryClient activeDirectoryClient = new ActiveDirectoryClient(new Uri(https://graph.windows.net, TenantId),
async () => await GetTokenForApplication());
Don't forget to wait for the result somewhere using the await Keyword, also specify that the method is async so it runs asynchronously
public async Task<ActionResult> SomeAction()
{
IUser result = await azureDirectoryClient.Users.GetByObjectId(someId).ExecuteAsync();
return View(result);
}

WebException on HTTP request while debugging

I have a ASP.NET project which involves sending HTTP requests via the Web-API Framework. The following exception is only raised when debugging:
The server committed a protocol violation. Section=ResponseStatusLine
The project runs perfectly if I "Start Without Debugging".
How should I resolve this exception?
Any help is appreciated!
Update
The problem seems related to the ASP.NET MVC Identity Framework.
To access other Web-API methods, the client application has to first POST a login request (The login request does not need to be secure yet, and so I am sending the username and password strings directly to the Web-API POST method). If I comment out the login request, no more exception is raised.
Below are the relevant code snippets:
The Post method:
UserManager<ApplicationUser> UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
AccountAccess ac = new AccountAccess();
public async Task<HttpResponseMessage> Post()
{
string result = await Request.Content.ReadAsStringAsync();
LoginMessage msg = JsonConvert.DeserializeObject<LoginMessage>(result);
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
var user = UserManager.Find(msg.username, msg.password);
if (user == null)
return response;
if (user.Roles == null)
return response;
var role = from r in user.Roles where (r.RoleId == "1" || r.RoleId == "2") select r;
if (role.Count() == 0)
{
return response;
}
bool task = await ac.LoginAsync(msg.username, msg.password);
response.Content = new StringContent(task.ToString());
return response;
}
The Account Access class (simulating the default AccountController in MVC template):
public class AccountAccess
{
public static bool success = false;
public AccountAccess()
: this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
{
}
public AccountAccess(UserManager<ApplicationUser> userManager)
{
UserManager = userManager;
}
public UserManager<ApplicationUser> UserManager { get; private set; }
public async Task<bool> LoginAsync(string username, string password)
{
var user = await UserManager.FindAsync(username, password);
if (user != null)
{
await SignInAsync(user, isPersistent: false);
return true;
}
else
{
return false;
}
}
~AccountAccess()
{
if (UserManager != null)
{
UserManager.Dispose();
UserManager = null;
}
}
private IAuthenticationManager AuthenticationManager
{
get
{
return HttpContext.Current.GetOwinContext().Authentication;
}
}
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);
}
}
Below are the relevant code snippets:
In client application:
public static async Task<List<T>> getItemAsync<T>(string urlAction)
{
message = new HttpRequestMessage();
message.Method = HttpMethod.Get;
message.RequestUri = new Uri(urlBase + urlAction);
HttpResponseMessage response = await client.SendAsync(message);
string result = await response.Content.ReadAsStringAsync();
List<T> msgs = JsonConvert.DeserializeObject<List<T>>(result);
return msgs;
}
In Web-API controller:
public HttpResponseMessage Get(string id)
{
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
if (id == "ItemA")
{
List<ItemAMessage> msgs = new List<ItemAMessage>();
// some code...
response.Content = new StringContent(JsonConvert.SerializeObject(msgs));
}
else if (id == "ItemB")
{
List<ItemBMessage> msgs = new List<ItemBMessage>();
// some code...
response.Content = new StringContent(JsonConvert.SerializeObject(msgs));
}
return response;
}
Some observations I have:
I thought that I may need to send the request asynchronously (with the async-await syntax), but the exception still persists that way.
If I step through the code, the request does enter the HTTP method, but the code breaks at random line (Why?!) before returning the response, so I assume no response is being sent back.
I have tried the following solutions, as suggested in answers to similar questions, none of which works for me:
Setting useUnsafeHeaderParsing to true
Adding the header Keep-Alive: false
Changing the port setting of Skype (I don't have Skype, and port 80 and 443 are not occupied)
Additional information, in case they matter:
Mac OS running Windows 8.1 with VMware Fusion
Visual Studio 2013
.NET Framework 4.5
IIS Express Server
Update 2
The exception is resolved, but I am unsure of which modification did the trick. AFAIK, either one or both of the following fixed it:
I have a checkConnection() method, which basically sends a GET request and return true on success. I added await to the HttpClient.SendAsync() method and enforced async all the way up.
I retracted all code in the MainWindow constructor, except for the InitializeComponent() method, into the Window Initialized event handler.
Any idea?
Below are relevant code to the modifications illustrated above:
the checkConnectionAsync method:
public static async Task<bool> checkConnectionAsync()
{
message = new HttpRequestMessage();
message.Method = HttpMethod.Get;
message.RequestUri = new Uri(urlBase);
try
{
HttpResponseMessage response = await client.SendAsync(message);
return (response.IsSuccessStatusCode);
}
catch (AggregateException)
{
return false;
}
}
Window Initialized event handler (retracted from the MainWindow constructor):
private async void Window_Initialized(object sender, EventArgs e)
{
if (await checkConnectionAsync())
{
await loggingIn();
getItemA();
getItemB();
}
else
{
logMsg.Content = "Connection Lost. Restart GUI and try again.";
}
}
Update 3
Although this may be a little off-topic, I'd like to add a side note in case anyone else falls into this – I have been using the wrong authentication approach for Web-API to start with. The Web-API project template already has a built-in Identity framework, and I somehow "replaced" it with a rather simple yet broken approach...
This video is a nice tutorial to start with.
This article provides a more comprehensive explanation.
In the Client Application you are not awaiting task. Accessing Result without awaiting may cause unpredictable errors. If it only fails during Debug mode, I can't say for sure, but it certainly isn't the same program (extra checks added, optimizations generally not enabled). Regardless of when Debugging is active, if you have a code error, you should fix that and it should work in either modes.
So either make that function async and call the task with the await modifier, or call task.WaitAndUnwrapException() on the task so it will block synchronously until the result is returned from the server.
Make sure URL has ID query string with value either as Item A or Item B. Otherwise, you will be returning no content with Http status code 200 which could lead to protocol violation.
When you use SendAsync, you are required to provide all relevant message headers yourself, including message.Headers.Authorization = new AuthenticationHeaderValue("Basic", token); for example.
You might want to use GetAsync instead (and call a specific get method on the server).
Also, are you sure the exception is resolved? If you have some high level async method that returns a Task and not void, that exception might be silently ignored.

ASP.NET MVC Identity login without password

I have been given the assignment of modifying an ASP.NET MVC application in such a way that navigating to myurl?username=xxxxxx automatically logs in user xxxxxx, without asking for a password.
I already made it very clear that this is a terrible idea for many security-related reasons and scenarios, but the people in charge are determined. The site would not be publicly available.
So: is there any way of signing in without a password by, for example, extending the Microsoft.AspNet.Identity.UserManager and modifying the AccountController?
Some code:
var user = await _userManager.FindAsync(model.UserName, model.Password);
if (user != null && IsAllowedToLoginIntoTheCurrentSite(user))
{
user = _genericRepository.LoadById<User>(user.Id);
if (user.Active)
{
await SignInAsync(user, model.RememberMe);
_userManager holds an instance of a Microsoft.AspNet.Identity.UserManager.
and SignInAsync():
private async Task SignInAsync(User user, bool isPersistent)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
var identity = await _userManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
if (user.UserGroupId.IsSet())
user.UserGroup = await _userManager.Load<UserGroup>(user.UserGroupId);
//adding claims here ... //
AuthenticationManager.SignIn(
new AuthenticationProperties { IsPersistent = isPersistent },
new CustomClaimsIdentity(identity));
}
AuthenticationManager would be OwinSecurity.
You just need to use the usermanager to find the user by name. If you have a record then just sign them in.
public ActionResult StupidCompanyLogin()
{
return View();
}
[HttpPost]
//[ValidateAntiForgeryToken] - Whats the point? F**k security
public async Task<ActionResult> StupidCompanyLogin(string name)
{
var user = await UserManager.FindByNameAsync(name);
if (user != null)
{
await SignInManager.SignInAsync(user, true, true);
}
return View();
}
For those who receive an error from Mr. #heymega's post. The result keyword is missing. The short and correct answer is:
var user = UserManager.FindByNameAsync(model.UserName).Result;
try
{
await SignInManager.SignInAsync(user, isPersistent:
false,rememberBrowser:false) ;
return Json(new { success = true, message = "Success" });
}
catch (Exception ex)
{
return Json(new { success = false, message = "Wrong username" });
}

Categories

Resources