i am using WebAPI MVC.Net project which has built in Web API codes.
For registration, it has function in Accounts Controller
// POST api/Account/Register
[AllowAnonymous]
[Route("Register")]
public async Task<IHttpActionResult> Register(RegisterBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
IdentityUser user = new IdentityUser
{
UserName = model.UserName
};
IdentityResult result = await UserManager.CreateAsync(user, model.Password);
IHttpActionResult errorResult = GetErrorResult(result);
if (errorResult != null)
{
return errorResult;
}
return Ok();
}
my API link for registration is epolleasy.azurewebsites.net/api/Account/Register which is giving error. I am unable to find the reason for this error.
Related
I have a .net core 3.1 WEB Api service where I use OData (V4)
I have an endpoint where every query through GET METHOD is working fine, and with the same endpoint I can use POST METHOD and insert new records to database.
The problem is, the DELETE and PATCH METHODs are not working (or maybe they are, but if I try to use update or delete I always get a 404 Not Found error). I try to call them ( DELETE, PATCH) from POSTMAN, but I get the same 404 error, however the methods are in the controller.
MyController:
[ApiController]
[Route("[controller]")]
public class UsersController : ODataController
{
[HttpGet]
[EnableQuery()]
public IEnumerable<User> Get()
{
return new Context().Userek;
}
[HttpPatch]
[EnableQuery]
public async Task<IActionResult> Patch([FromODataUri] int id, Delta<User> user)
{
var ctx = new Context();
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var entity = await ctx.Userek.FindAsync(id);
if (entity == null)
{
return NotFound();
}
user.Patch(entity);
try
{
await ctx.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (id != 23)
{
return NotFound();
}
else
{
throw;
}
}
return Updated(entity);
}
public async Task<IActionResult> Delete([FromODataUri] int id)
{
var ctx = new Context();
var user = await ctx.Userek.FindAsync(id);
if (user == null)
{
return NotFound();
}
ctx.Userek.Remove(user);
await ctx.SaveChangesAsync();
return StatusCode(404);
}
}
Thank you for any help!
In an ASP.NET Core Rest API project, I have set up custom authentication, and I can annotate Controller Actions with the [Authorize] attribute, which redirects unauthorized requests back to my AuthController:
[Route("api/[controller]")]
[ApiController]
public class ResponseController : ControllerBase
{
[Authorize]
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
return JsonConvert.SerializeObject(Repository.GetResponse(id), Formatting.Indented);
}
}
[Route("api/[controller]")]
[ApiController]
public class MetaController : ControllerBase
{
[Authorize]
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
return JsonConvert.SerializeObject(Repository.GetMeta(id), Formatting.Indented);
}
}
[Route("api/[controller]")]
[ApiController]
public class AuthController : Controller
{
UserManager _userManager;
public AuthController(UserManager userManager)
{
_userManager = userManager;
}
[HttpGet]
[HttpPost]
public ActionResult<string> LogIn()
{
try
{
//authenticate
var username = Request.Headers["username"];
var password = Request.Headers["pass"];
if (String.IsNullOrEmpty(username) || String.IsNullOrEmpty(password))
throw new Exception("Authentication Exception: Missing Username or Password");
Task.Run(async () => {
await _userManager.SignInAsync(this.HttpContext, username, password);
}).GetAwaiter().GetResult();
return RedirectToAction("Search", "Home", null);
//^^^ How to send back to intended action?^^^
}
catch (Exception ex)
{
return AuthError();
}
}
}
This works, except how do I use the RedirectToAction method to return back to the intended controller (MetaController or ResponseController, in this case)? (the one with the method marked [Authorize], which put us into this authentication controller to begin with)
You need to use returnUrl parameter, like so:
[HttpGet]
[HttpPost]
public async Task<IActionResult> LogIn(string returnUrl = null)
{
try
{
//authenticate
var username = Request.Headers["username"];
var password = Request.Headers["pass"];
if (String.IsNullOrEmpty(username) || String.IsNullOrEmpty(password))
throw new Exception("Authentication Exception: Missing Username or Password");
await _userManager.SignInAsync(this.HttpContext, username, password);
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
return RedirectToAction("Search", "Home", null);
}
catch (Exception ex)
{
return BadRequest(new {error = "Authentication Failed"});
}
}
I also fix async/await for controller's action. You need use async Task<ActionResult> insted of your ActionResult<string>
Instead of redirection, create your own attribute inheriting from the AuthorizeAttribute and override the OnAuthorization method. That way you don't have to worry about redirection.
In my ASP.NET Core application I have made facebook login, which works fine. In my Controller there are two methods to make that facebook login
public IActionResult ExternalLogin(string provider, string returnUrl)
public async Task<IActionResult> ExternalLoginCallback(string returnUrl, string remoteError = null)
ExternalLogin makes the request to facebook and opens the approval page. When I click Continue in that approval page ExternalLoginCallback is called.
Now the problem is that I made api calls to these two actions and I am using them in my Angular2 application.
So when I click facebook login button in angular2 app my ExternalLogin action is called and it returns success response, because facebook approval page is opened, but now I can't get ant response from ExternalLoginCallback, which is called automatically. How could I get the wanted response in my facebookLogin method???
[HttpPost]
[AllowAnonymous]
public IActionResult ExternalLogin(string provider, string returnUrl)
{
// Request a redirect to the external login provider.
var redirectUrl = Url.Action(nameof(ExternalLoginCallback), "User", new { ReturnUrl = returnUrl });
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
return Challenge(properties, provider);
}
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> ExternalLoginCallback(string returnUrl, string remoteError = null)
{
var info = await _signInManager.GetExternalLoginInfoAsync();
if (!string.IsNullOrEmpty(remoteError))
{
return new StatusCodeResult(401);
}
if (info == null)
{
return new StatusCodeResult(401);
}
// Sign in the user with this external login provider if the user already has a login.
var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, false, true);
if (result.Succeeded)
{
return new StatusCodeResult(200);
}
//Register with facebook
var email = info.Principal.FindFirstValue(ClaimTypes.Email);
if (string.IsNullOrEmpty(email))
{
return new StatusCodeResult(401);
}
var user = new User
{
UserName = email,
Email = email
};
var createResult = await _userManager.CreateAsync(user);
if (createResult.Succeeded)
{
createResult = await _userManager.AddLoginAsync(_userManager.Users.First(m => m.Email == user.Email), info);
if (createResult.Succeeded)
{
await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, false, true);
return new StatusCodeResult(200);
}
}
return new StatusCodeResult(401);
}
This is my typescript method of subscribing the api call:
facebookLogin(provider: string, returnUrl: string) {
this.productService.externalLogin(provider, returnUrl).subscribe(x => {
console.log(x);
},
error => {
});
}
And this is my api call:
public externalLogin(provider: string, returnUrl: string) {
var url = 'http://mywebsite.com/api/User/ExternalLogin?Provider=Facebook&ReturnUrl=%2F';
return this.http.post(url, `Provider=${provider}&ReturnUrl=${returnUrl}`);
}
Is it possible to test whether a confirm email token is expired using Identity Framework's UserManager? No matter what the error is, from the following:
var result = await UserManager.ConfirmEmailAsync(userId, code);
I get a generic "Invalid Token" error.
I found a way to parse the token for the date issued, which you can then check to see if is within the allowed timespan (default of 24hours if not specified).
Identity.cs
ApplicationUserManager
public IDataProtector Protector { get; set; }
public TimeSpan TokenLifespan { get; set; }
ApplicationUserManager Create()
// Explicitly set token expiration to 24 hours.
manager.TokenLifespan = TimeSpan.FromHours(24);
var dataProtectionProvider = options.DataProtectionProvider;
manager.Protector = dataProtectionProvider.Create("ASP.NET Identity");
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"))
{
TokenLifespan = manager.TokenLifespan
};
}
AccountController.cs
public async Task<ActionResult> ConfirmEmail(string Code, string UserId)
{
// Try/catch, validation, etc.
var tokenExpired = false;
var unprotectedData = UserManager.Protector.Unprotect(Convert.FromBase64String(Code));
var ms = new MemoryStream(unprotectedData);
using (BinaryReader reader = new BinaryReader(ms))
{
var creationTime = new DateTimeOffset(reader.ReadInt64(), TimeSpan.Zero);
var expirationTime = creationTime + UserManager.TokenLifespan;
if (expirationTime < DateTimeOffset.UtcNow)
{
tokenExpired = true;
}
}
// Do something if token is expired, else continue with confirmation
}
I found this blog post and Nkosi's answer to be extremely helpful, and if you want to go through the Identity source code, Microsoft has it here (The previous versions of Identity for MVC5 and lower here). Also, I apologize if its in poor form to answer a question that you, yourself put a bounty on, but I couldn't help but continue looking for a better solution.
I get around this by keeping/storing a copy of the generated token
public class ApplicationUser : IdentityUser {
public string EmailConfirmationToken { get; set; }
public string ResetPasswordToken { get; set; }
}
and associating it with the user in derived UserManager<ApplicationUser>.
public override async System.Threading.Tasks.Task<string> GenerateEmailConfirmationTokenAsync(string userId) {
/* NOTE:
* The default UserTokenProvider generates tokens based on the users's SecurityStamp, so until that changes
* (like when the user's password changes), the tokens will always be the same, and remain valid.
* So if you want to simply invalidate old tokens, just call manager.UpdateSecurityStampAsync().
*/
//await base.UpdateSecurityStampAsync(userId);
var token = await base.GenerateEmailConfirmationTokenAsync(userId);
if (!string.IsNullOrEmpty(token)) {
var user = await FindByIdAsync(userId);
user.EmailConfirmationToken = token; //<<< Last issued token
//Note: If a token is generated then the current email is no longer confirmed.
user.EmailConfirmed = false;
await UpdateAsync(user);
}
return token;
}
When the token is provided for confirmation, a search for the user via the token is done.
public static class ApplicationUserManagerExtension {
public static Task<string> FindIdByEmailConfirmationTokenAsync(this UserManager<ApplicationUser> manager, string confirmationToken) {
string result = null;
ApplicationUser user = manager.Users.SingleOrDefault(u => u.EmailConfirmationToken != null && u.EmailConfirmationToken == confirmationToken);
if (user != null) {
result = user.Id;
}
return Task.FromResult(result);
}
}
If the token matches a known user that indicates that it was a validly issued token.
Will then attempt to confirm token with User manager.
If confirmation fails then token has expired and an appropriate action is taken.
Else if the token confirmed, it is removed from associated user and thus invalidating the reuse of that token.
public override async System.Threading.Tasks.Task<IdentityResult> ConfirmEmailAsync(string userId, string token) {
var user = await FindByIdAsync(userId);
if (user == null) {
return IdentityResult.Failed("User Id Not Found");
}
var result = await base.ConfirmEmailAsync(userId, token);
if (result.Succeeded) {
user.EmailConfirmationToken = null;
return await UpdateAsync(user);
} else if (user.EmailConfirmationToken == token) {
//Previously Issued Token expired
result = IdentityResult.Failed("Expired Token");
}
return result;
}
A similar approach was implemented for password reset as well.
Here comes an .NET Core 2.1 adaption of the solution provided by #Nkosi :
ApplicationUser class
public class ApplicationUser : IdentityUser
{
public string EmailConfirmationToken { get; set; }
public string ResetPasswordToken { get; set; }
}
Derived UserManager class
public class CustomUserManager : UserManager<ApplicationUser>
{
public CustomUserManager(IUserStore<ApplicationUser> store,
IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<ApplicationUser> passwordHasher,
IEnumerable<IUserValidator<ApplicationUser>> userValidators,
IEnumerable<IPasswordValidator<ApplicationUser>> passwordValidators,
ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors,
IServiceProvider services,
ILogger<UserManager<ApplicationUser>> logger)
: base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
{
}
public override async Task<string> GenerateEmailConfirmationTokenAsync(ApplicationUser user)
{
/* NOTE:
* The default UserTokenProvider generates tokens based on the users's SecurityStamp, so until that changes
* (like when the user's password changes), the tokens will always be the same, and remain valid.
* So if you want to simply invalidate old tokens, just call manager.UpdateSecurityStampAsync().
*/
//await base.UpdateSecurityStampAsync(userId);
var token = await base.GenerateEmailConfirmationTokenAsync(user);
if (!string.IsNullOrEmpty(token))
{
user.EmailConfirmationToken = token; //<<< Last issued token
//Note: If a token is generated then the current email is no longer confirmed.
user.EmailConfirmed = false;
await UpdateAsync(user);
}
return token;
}
public override async Task<IdentityResult> ConfirmEmailAsync(ApplicationUser user, string token)
{
if (user == null)
{
return IdentityResult.Failed(new IdentityError {Description = "User not found."});
}
var result = await base.ConfirmEmailAsync(user, token);
if (result.Succeeded)
{
user.EmailConfirmationToken = null;
return await UpdateAsync(user);
}
else if (user.EmailConfirmationToken == token)
{
//Previously Issued Token expired
result = IdentityResult.Failed(new IdentityError { Description = "Expired token." });
}
return result;
}
}
UserManager Extension
public static class ApplicationUserManagerExtension
{
public static Task<string> FindIdByEmailConfirmationTokenAsync(this UserManager<ApplicationUser> manager, string confirmationToken)
{
string result = null;
ApplicationUser user = manager.Users
.SingleOrDefault(u => u.EmailConfirmationToken != null && u.EmailConfirmationToken == confirmationToken);
if (user != null)
{
result = user.Id;
}
return Task.FromResult(result);
}
}
Update:
The CustomUserManager has to be added to services in Startup.cs in ConfigureServices Method.
services.AddTransient<CustomUserManager>();
Without this, DependencyInjection fails.
You can use my controller.It's working mate.
public IActionResult ForgotPassword()
{
return View();
}
[HttpPost]
public async Task<IActionResult> ForgotPassword(string Email)
{
if (string.IsNullOrEmpty(Email))
{
return View();
}
var user = await _userManager.FindByEmailAsync(Email);
if (user == null)
{
return View();
}
var code =await _userManager.GeneratePasswordResetTokenAsync(user);
var callback = Url.Action("ResetPassword", "Account", new
{
token=code,
},Request.Scheme);
// send email
await _emailSender.SendEmailAsync(Email, "Confirm Password Reset", $"<a href='{callback}'>If you want to reset your password click please !</a>");
return RedirectToAction("ForgotPasswordConfirmation", "Account");
}
public IActionResult ForgotPasswordConfirmation() => View();
public IActionResult ResetPassword(string token)
{
if (token == null)
{
return View();
}
var model = new ResetPasswordModel()
{
Token = token,
};
return View(model);
}
[HttpPost]
public async Task<IActionResult> ResetPassword(ResetPasswordModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await _userManager.FindByEmailAsync(model.Email);
if (user == null)
{
return RedirectToAction("Index", "Home");
}
var result = await _userManager.ResetPasswordAsync(user, model.Token, model.Password);
if (result.Succeeded)
{
return RedirectToAction("ResetPasswordConfirmation", "Account");
}
return View(model);
}
public ActionResult ResetPasswordConfirmation() => View();
I'm new in ASP.NET and I'm making a simple project to practice. I started with a simple MVC project without authentication because it adds many code that I didn't understand.
But now I want to add a membership to my sistem so I followed this guide:
http://benfoster.io/blog/aspnet-identity-stripped-bare-mvc-part-1
http://benfoster.io/blog/aspnet-identity-stripped-bare-mvc-part-2
But I don't know where I can add a role to the user entity..
My Startup class is this:
public class Startup
{
public static Func<UserManager<Usuario>> UserManagerFactory { get; private set; }
public void Configuration(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "ApplicationCookie"
});
UserManagerFactory = () =>
{
var usermanager = new UserManager<Usuario>(new UserStore<Usuario>(new Vetpet3Context()));
usermanager.UserValidator = new UserValidator<Usuario>(usermanager)
{
AllowOnlyAlphanumericUserNames = false
};
return usermanager;
};
}
}
And I'm creating (for now) my users with this Action:
[HttpPost]
public async Task<ActionResult> Register(RegisterModel model)
{
if (!ModelState.IsValid)
{
return View();
}
var user = new Usuario
{
UserName=model.correo,
correo = model.correo
};
var result = await userManager.CreateAsync(user, model.password);
if (result.Succeeded)
{
await SignIn(user);
return RedirectToAction("index", "Home");
}
foreach (var error in result.Errors)
{
ModelState.AddModelError("", error);
}
return View();
}
So everything works fine at this point, but I need to add different roles to the users , and this is the question, how I implement this part? I've read many guides but everyone does different things and I'm not sure how to add a role to my new users and how to claim these roles when they log in
to add role to the user after he successfully register :
if (result.Succeeded)
{
var roleResult = userManager.AddToRole(user.Id, "Admin");
await SignIn(user);
return RedirectToAction("index", "Home");
}
to check if the user in role :
userManager.IsInRole(user.Id, "Admin");
or even simpler in any ASP.NET MVC [Authorized] controller :
User.IsInRole("roleName");