UpdateAsync method is not work in web api? - c#

I am create small demo for user registration using web api mvc c#.i am register Succeeded for user using 'Register' in web api.now i want to using this method also edit this user by id so how can do that i don't know any one know please let me know. i my code i will manage add/edit call in one method so i will first check the condition on id is null then add and id is not null then go for the edit but how can edit this record and role.
here this my method :
[Route("Register")]
public async Task<IHttpActionResult> Register(RegisterBindingModel model)
{
try
{
if (model.Id == "")
{
//here is for add user method
var user = new ApplicationUser() { UserName = model.Email, Email = model.Email,PhoneNumber = model.PhoneNumber };
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
var UsersContext = new ApplicationDbContext();
var res = UsersContext.Users.Where(x => x.UserName == user.UserName).FirstOrDefault();
var UserId = res.Id;
await UserManager.AddToRoleAsync(UserId, model.UserRole);
return Ok(result);
}
return Ok(result);
}
else
{
//here i want write edit code
var UsersContext = new ApplicationDbContext();
var Team = UsersContext.Users.Find(model.Id);
Team.UserName = model.UserName;
Team.Email = model.Email;
Team.PhoneNumber = model.PhoneNumber;
IdentityResult result = await UserManager.UpdateAsync(Team); //here getting error.
return Ok(result);
}
return Ok("Done");
}
catch (Exception ex)
{
}
return Ok();
}

UPDATED:
First check whether you are passing values to all required field or not.
Keep this UserStore after your controller class calling braces {.
[Authorize]
public class AccountController : Controller
{
UserStore<IdentityUser> myUserStore = new UserStore<IdentityUser>(new
ApplicationDbContext());
//rest code
Then inside your Register() Post method, do like this bellow,
if (model.Id == "")
{
//Add new user code
}
else
{
var context = myUserStore.Context as ApplicationDbContext;
var Team = context.Users.Find(model.Id);
//change the field value what ever you want to update
context.Users.Attach(Team);
context.Entry(Team).State = EntityState.Modified;
context.Configuration.ValidateOnSaveEnabled = false;
await context.SaveChangesAsync();
return Ok("Updated");
}
Hope it helps :)

Related

ASP.NET Post API Call doesnt find the controller route. Why?

I want to make an Post api call. the get method works without any problems, but the post call doesnt find the route. idk why...
Here's the Controller
[Authorize(Roles = AppRoles.Supervisor)]
[Route("api/[controller]/[action]")]
[ApiController]
public class SupportUserManagementController : ControllerBase
{
private readonly SupportUserService _supportUsersService;
public SupportUserManagementController(SupportUserService supportUserService)
{
this._supportUsersService = supportUserService;
}
// GET: api/GetSupportUsersListAsync
[HttpGet]
[ActionName("Test2")]
[Authorize(Roles = AppRoles.User)]
[ProducesResponseType(typeof(IEnumerable<ScopeSupportUsers>), StatusCodes.Status200OK)]
[SwaggerOperation("GetSupportUsers")]
public async Task<IEnumerable<ScopeSupportUsers>> GetSupportUsers(int id)
{
var result = await _supportUsersService.GetSupportUsersListAsync(id);
return result;
}
[HttpPost]
[ActionName("Test3")]
[Authorize(Roles = AppRoles.User)]
[ProducesResponseType(typeof(Guid?), StatusCodes.Status200OK)]
[SwaggerOperation("CreateSupportUsers")]
public async Task<Guid?> CreateSupportUsers(int id, ScopeSupportUsers user)
{
var existingUser = await GetSupportUsers(id);
var matches = existingUser.Where(u => u.Name == user.Name);
if (matches.Count() != 0)
{
return Guid.Empty;
}
var resultGuid = await _supportUsersService.CreateSupportUserAsync(id, user);
if (resultGuid != null)
{
var resultCertificate = await SetCertificate_1Async(id, user, (Guid)resultGuid);
if (resultCertificate == true)
return resultGuid;
}
return null;
}
Why does this work:
using var client = await httpClientHelper.GetHttpClientWithAccessToken();
string uri = $"{_serviceClientOptions.Value.BetriebstoolServiceAddress}/api/SupportUserManagement/Test2?id={selectedScope}";
supportUserResult = await client.GetFromJsonAsync<IEnumerable<ScopeSupportUsers>>(uri);
And this doesnt work?
using var client = await httpClientHelper.GetHttpClientWithAccessToken();
string uri = $"{_serviceClientOptions.Value.BetriebstoolServiceAddress}/api/SupportUserManagement/Test3?id={selectedScopeToCopyTo}";
var response = await client.PostAsJsonAsync<ScopeSupportUsers>(uri , user);
If i am debugging the GET works fine but at the POST Method it doesnt find the "way" to the controller"
This Breakpoint never reached :(

Azure AD authentication to existing ASP.NET Core Identity application

I currently have and application that is using Identity to authorize users. I need to change it to use Azure AD to login. After being authenticated through azure I need to use the information of the logged in user that we have in the identity database. After the user is authenticated I get a
NullReferenceException: Object reference not set to an instance of an object.
and fails at this point:
ApplicationUser user = await manager.FindByNameAsync(context.Principal.Identity.Name);
```
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication().AddOpenIdConnect(c =>
{
c.Authority = "https://login.microsoftonline.com/common";
c.ClientId = "<insert-registered-guid>";
c.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false
};
c.Events.OnTokenValidated = async context =>
{
UserManager<ApplicationUser> manager = context.HttpContext.RequestServices.GetService<UserManager<ApplicationUser>>();
SignInManager<ApplicationUser> signIn = context.HttpContext.RequestServices.GetService<SignInManager<ApplicationUser>>();
ApplicationUser user = await manager.FindByNameAsync(context.Principal.Identity.Name);
if (user != null)
{
await signIn.SignInAsync(user, false);
}
};
});
}
// HomeController.cs
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
public class HomeController : Controller
{
[AllowAnonymous]
public IActionResult LoginWithAzure()
{
string redirectUrl = Url.Content("~/");
return Challenge(new AuthenticationProperties { RedirectUri = redirectUrl }, OpenIdConnectDefaults.AuthenticationScheme);
}
}
```
UPDATE:
I was able to get past the error because I was missing
services.AddIdentity
Now the issue is that it gets stuck in a loop inside the OnTokenValidated.
UserManager<ApplicationUser> manager = context.HttpContext.RequestServices.GetService<UserManager<ApplicationUser>>();
SignInManager<ApplicationUser> signIn = context.HttpContext.RequestServices.GetService<SignInManager<ApplicationUser>>();
ApplicationUser user = await manager.FindByNameAsync(context.Principal.Identity.Name);
if (user != null)
{
await signIn.SignInAsync(user, false);
}
after the if statement it goes back to the manager line.
The above solution was not working so I changed it.
Startup.cs was changed to the following:
// Add Azure AD authentication
services.AddAuthentication(defaultScheme: AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => Configuration.Bind("AzureAd", options));
AccountController.cs was changed to this:
[AllowAnonymous]
[HttpGet]
public ChallengeResult InternalSignIn(string returnUrl = "/")
{
var redirectUrl = Url.Action(nameof(ExternalLoginCallback));
var properties = signInManager.ConfigureExternalAuthenticationProperties(AzureADDefaults.AuthenticationScheme, redirectUrl);
return new ChallengeResult(AzureADDefaults.AuthenticationScheme, properties);
}
[HttpGet]
public async Task<IActionResult> ExternalLoginCallback()
{
var info = await signInManager.GetExternalLoginInfoAsync();
if (info is null)
{
return BadRequest();
}
var signInResult = await signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor: false);
var email = info.Principal.FindFirstValue(ClaimTypes.Name);
var user = await userManager.FindByEmailAsync(email);
IdentityResult result;
if (user != null)
{
var logins = await userManager.GetLoginsAsync(user);
if (!logins.Any())
{
result = await userManager.AddLoginAsync(user, info);
if (!result.Succeeded)
{
return View();
}
}
await signInManager.SignInAsync(user, isPersistent: false);
return RedirectToAction(nameof(HomeController.Index),"Home");
}
return StatusCode(500, "Internal server error");
}

custom asp.net identity store - why is HttpContext.Current empty at times

I've implemented a custom User Store for ASP.NET Identity by following the example set here. That all works fine, except for this:
I need access to data about the currently logged in user in my user store. Normally, you'd access that by accessing
HttpContext.Current.User
Now, once auser has logged in, if he user then goes to the Manage controller (e.g. to try and change his/her password), when ASP.NET identity looks up the user again by calling
CustomUserManager.FindByIdAsync(string userId)
HttpContext.Current is empty altogether (that's prior to rendering the page). So, how do I get information about the HttpContext in this scenario? The user is properly logged in, so how do I figure out which user has been logged in?
#edit.. the problem is in CustomUserStore.. here's a bit of it
public class CustomUserStore<TUser> : IUserStore<TUser>, IUserLoginStore<TUser>, IUserClaimStore<TUser>, IUserPasswordStore<TUser>, IUserSecurityStampStore<TUser>, IUserEmailStore<TUser>, IUserPhoneNumberStore<TUser>,
IUserLockoutStore<TUser, string>, IUserTwoFactorStore<TUser, string>//, IQueryableUserStore<TUser>
where TUser: CustomUser<string>, IUser<string>
{
string storageFile = #"c:\temp\aspnetusers.json";
List<TUser> users;
public CustomUserStore()
{
if (File.Exists(storageFile))
{
string contents = File.ReadAllText(storageFile);
users = JsonConvert.DeserializeObject<List<TUser>>(contents);
if (users == null)
users = new List<TUser>();
}
else
users = new List<TUser>();
}
#region IUserStore implementation
public Task<TUser> FindByIdAsync(string userId)
{
string sessionId = HttpContext.Current?.Session?.SessionID;
return Task.FromResult<TUser>(users.FirstOrDefault(u => u.Id == userId));
}
public Task<TUser> FindByNameAsync(string userName)
{
string sessionId = HttpContext.Current?.Session?.SessionID;
return Task.FromResult<TUser>(users.FirstOrDefault(u => string.Compare(u.UserName, userName, true) == 0));
}
#endregion
}
and it's in the FindByAsync method where HttpContext.Current can be empty.
It happens in the Index method of the AccountController when the model is created
var model = new IndexViewModel
{
HasPassword = HasPassword(),
PhoneNumber = await UserManager.GetPhoneNumberAsync(userId),
TwoFactor = await UserManager.GetTwoFactorEnabledAsync(userId),
Logins = await UserManager.GetLoginsAsync(userId),
BrowserRemembered = await AuthenticationManager.TwoFactorBrowserRememberedAsync(userId)
};
And it's the FindById request in the HasPassword method that causes the problem
private bool HasPassword()
{
var user = UserManager.FindById(User.Identity.GetUserId());
if (user != null)
{
return user.PasswordHash != null;
}
return false;
}
The other 4 requests to the user manager all have a filled out HttpContext.Current. So it appears that it's calls to UserManager that cause the issue.
Having identified the exact source of the problem, it's easy enough to fix.
Add this async emthod to check if the user has a password:
private async Task<bool> HasPasswordAsync()
{
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
if (user != null)
{
return user.PasswordHash != null;
}
return false;
}
And in the Index method, use the new async methode
var model = new IndexViewModel
{
HasPassword = await HasPasswordAsync(),
PhoneNumber = await UserManager.GetPhoneNumberAsync(userId),
TwoFactor = await UserManager.GetTwoFactorEnabledAsync(userId),
Logins = await UserManager.GetLoginsAsync(userId),
BrowserRemembered = await AuthenticationManager.TwoFactorBrowserRememberedAsync(userId)
};
But, why does the synchronous method call break things? You'd imagine the sync call would run into the standard context where HttpContext.Current should be available.
I have a more custom User Store in my real project where I run into this problem a lot more frequently.. guess I need to check if contains a lot more synchronous access to UserManager methods.

How to create transaction with asp.net identity?

I am doing registration on which i am asking for 5 things:
FullName,EmailId,Password,ContactNumber,Gender
Now emailid and password i am storing with register method and given in below two link:
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
using var context = new MyEntities())
{
using (var transaction = context.Database.BeginTransaction())
{
try
{
var DataModel = new UserMaster();
DataModel.Gender = model.Gender.ToString();
DataModel.Name = string.Empty;
var result = await UserManager.CreateAsync(user, model.Password);//Doing entry in AspnetUser even if transaction fails
if (result.Succeeded)
{
await this.UserManager.AddToRoleAsync(user.Id, model.Role.ToString());
this.AddUser(DataModel, context);
transaction.Commit();
return View("DisplayEmail");
}
AddErrors(result);
}
catch (Exception ex)
{
transaction.Rollback();
return null;
}
}
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
public int AddUser(UserMaster _addUser, MyEntities _context)
{
_context.UserMaster.Add(_addUser);
_context.SaveChanges();
return 0;
}
Now with this below 2 lines:
var result = await UserManager.CreateAsync(user, model.Password);//entry is done in AspnetUsers table.
await this.UserManager.AddToRoleAsync(user.Id, model.Role.ToString());//entry is done is Aspnetuserrole table
Now this Fullname,contactno,gender i am having in another table that is UserMaster.
So when i will submit my registration form i will save this details in UserMaster and AspnetUsers,AspnetUserinrole table.
But consider if there any problem occurs while saving record in UserMaster then i dont want to save entry in Aspnetuser and Aspnetuserinrole too.
I would like to create a transaction where i would rollback if any problem occurs during saving any record in any table i.e no entry should be done in AspnetUser,AspnetUserinRole nd userMaster.
Records should be saved successfully only if there is no problem in saving record in this 3 tables otherwise whiole transaction should be role back.
I am using Microsoft.AspNet.Identity for login,Register,role management and other and following this tutorial:
http://www.asp.net/mvc/overview/security/create-an-aspnet-mvc-5-web-app-with-email-confirmation-and-password-reset
http://www.asp.net/identity/overview/features-api/account-confirmation-and-password-recovery-with-aspnet-identity
But as await UserManager.CreateAsync and UserManager.AddToRoleAsync method are built in method how would i synchonize it to work with entity framework.
So can anybody guide me how to create such transaction or anything that would solve this?
IdentityConfig:
public class ApplicationUserManager : UserManager<ApplicationUser>
{
public ApplicationUserManager(IUserStore<ApplicationUser> store)
: base(store)
{
}
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
// Configure validation logic for usernames
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
// Configure user lockout defaults
manager.UserLockoutEnabledByDefault = true;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
manager.MaxFailedAccessAttemptsBeforeLockout = 5;
// Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
// You can write your own provider and plug it in here.
manager.RegisterTwoFactorProvider("Phone Code", new PhoneNumberTokenProvider<ApplicationUser>
{
MessageFormat = "Your security code is {0}"
});
manager.RegisterTwoFactorProvider("Email Code", new EmailTokenProvider<ApplicationUser>
{
Subject = "Security Code",
BodyFormat = "Your security code is {0}"
});
manager.EmailService = new EmailService();
manager.SmsService = new SmsService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
}
// Configure the application sign-in manager which is used in this application.
public class ApplicationSignInManager : SignInManager<ApplicationUser, string>
{
public ApplicationSignInManager(ApplicationUserManager userManager, IAuthenticationManager authenticationManager)
: base(userManager, authenticationManager)
{
}
public override Task<ClaimsIdentity> CreateUserIdentityAsync(ApplicationUser user)
{
return user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager);
}
public static ApplicationSignInManager Create(IdentityFactoryOptions<ApplicationSignInManager> options, IOwinContext context)
{
return new ApplicationSignInManager(context.GetUserManager<ApplicationUserManager>(), context.Authentication);
}
}
You should not create a new db context, but use the existing one.
var context = Request.GetOwinContext().Get<MyEntities>()
It is created per request if you use default implementation.
app.CreatePerOwinContext(ApplicationDbContext.Create);
Update:
OK, since you are using two different contexts your code will look something like this:
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var appDbContext = HttpContext.GetOwinContext().Get<ApplicationDbContext>();
using( var context = new MyEntities())
using (var transaction = appDbContext.Database.BeginTransaction())
{
try
{
var DataModel = new UserMaster();
DataModel.Gender = model.Gender.ToString();
DataModel.Name = string.Empty;
// Doing entry in AspnetUser even if transaction fails
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await this.UserManager.AddToRoleAsync(user.Id, model.Role.ToString());
this.AddUser(DataModel, context);
transaction.Commit();
return View("DisplayEmail");
}
AddErrors(result);
}
catch (Exception ex)
{
transaction.Rollback();
return null;
}
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
public int AddUser(UserMaster _addUser, MyEntities _context)
{
_context.UserMaster.Add(_addUser);
_context.SaveChanges();
return 0;
}
Here, appDbContext is the same context that is used by UserManager.
You can solve it with TransactionScope class:
using (TransactionScope scope = new TransactionScope())
{
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await this.UserManager.AddToRoleAsync(user.Id, model.Role.ToString());
string callbackUrl = await SendEmailConfirmationTokenAsync(user.Id, "Confirm your account");
return View("DisplayEmail");
}
scope.Complete();
}
So, both actions will be done in one transaction and if method Comlete does not call, both actions will be canceled (roolback).
If you want to solve it with EF only (without TransactionScope), you need to refactor your code. I don't know implementation of class UserManager and methods CreateAsync and AddToRoleAsync, but I guess that they creates new DBContext for each operation. So, first of all, for all transactional operations you need one DBContext (for EF solution). If you add this methods, I'll modify my answer according to EF solution.
Backs alternative works for me, when i use this method: TransactionScope(TransactionScopeAsyncFlowOption.Enabled)
source: https://learn.microsoft.com/en-us/ef/ef6/saving/transactions

Is it possible to use Asp.net Identity with autoincremental key fields?

I am use my own EF database using long as the Id field. I have created all the entities and the relevant custom stores.
However when registering a new user, it is trying to call SetPasswordHashAsync before it has called CreateAsync, which means that it is trying to write to a database record that doesn't exist yet and of course it is throwing an error.
It looks like the system is expecting to know the Id field before it has been written to the DB but until then it cannot know the Id as it is set to be an auto-incremental field.
What am I missing? Is this even possible?
Here is the code from the AccountController (taken from the MVC project template)
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new User { UserName = model.Email, Email = model.Email };
var result = await userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
// We do not want the user to be logged in as soon as they register. We want them have to go through the login process.
//await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser:false);
// Send an email with this link
string 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 here");
return View("DisplayEmail");
}
AddErrors(result);
}
// If we got this far, something failed, redisplay form
return View(model);
}
and here is the code from UserManager (from Identity.core) that is called
public virtual async Task<IdentityResult> CreateAsync(TUser user, string password)
{
ThrowIfDisposed();
var passwordStore = GetPasswordStore();
if (user == null)
{
throw new ArgumentNullException("user");
}
if (password == null)
{
throw new ArgumentNullException("password");
}
var result = await UpdatePasswordInternal(passwordStore, user, password).WithCurrentCulture();
if (!result.Succeeded)
{
return result;
}
return await CreateAsync(user).WithCurrentCulture();
}
So you can see that updating the password before calling createUser is the intended process, but how is that meant to work? That just seems wrong to me.

Categories