This is the second part of Building Simple Membership system using ASP.NET Identity 2.1, ASP.NET Web API 2.2 and AngularJS. The topics we’ll cover are:
- Configure ASP.NET Identity with ASP.NET Web API (Accounts Management) – Part 1.
- ASP.NET Identity 2.1 Accounts Confirmation, and Password/User Policy Configuration – (This Post)
- Implement OAuth JSON Web Tokens Authentication in ASP.NET Web API and Identity 2.1 – Part 3
- ASP.NET Identity 2.1 Roles Based Authorization with ASP.NET Web API – Part 4
- ASP.NET Web API Claims Authorization with ASP.NET Identity 2.1 – Part 5
- AngularJS Authentication and Authorization with ASP.NET Web API and Identity 2.1 – Part 6
The source code for this tutorial is available on GitHub.
ASP.NET Identity 2.1 Accounts Confirmation, and Password/User Policy Configuration
In this post we’ll complete on top of what we’ve already built, and we’ll cover the below topics:
- Send Confirmation Emails after Account Creation.
- Configure User (Username, Email) and Password policy.
- Enable Changing Password and Deleting Account.
1 . Send Confirmation Emails after Account Creation
ASP.NET Identity 2.1 users table (AspNetUsers) comes by default with a Boolean column named “EmailConfirmed”, this column is used to flag if the email provided by the registered user is valid and belongs to this user in other words that user can access the email provided and he is not impersonating another identity. So our membership system should not allow users without valid email address to log into the system.
The scenario we want to implement that user will register in the system, then a confirmation email will be sent to the email provided upon the registration, this email will include an activation link and a token (code) which is tied to this user only and valid for certain period.
Once the user opens this email and clicks on the activation link, and if the token (code) is valid the field “EmailConfirmed” will be set to “true” and this proves that the email belongs to the registered user.
To do so we need to add a service which is responsible to send emails to users, in my case I’ll use Send Grid which is service provider for sending emails, but you can use any other service provider or your exchange change server to do this. If you want to follow along with this tutorial you can create a free account with Send Grid which provides you with 400 email per day, pretty good!
1.1 Install Send Grid
Now open Package Manager Console and type the below to install Send Grid package, this is not required step if you want to use another email service provider. This packages contains Send Grid APIs which makes sending emails very easy:
1 |
install-package Sendgrid |
1.2 Add Email Service
Now add new folder named “Services” then add new class named “EmailService” and paste the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
public class EmailService : IIdentityMessageService { public async Task SendAsync(IdentityMessage message) { await configSendGridasync(message); } // Use NuGet to install SendGrid (Basic C# client lib) private async Task configSendGridasync(IdentityMessage message) { var myMessage = new SendGridMessage(); myMessage.AddTo(message.Destination); myMessage.From = new System.Net.Mail.MailAddress("taiseer@bitoftech.net", "Taiseer Joudeh"); myMessage.Subject = message.Subject; myMessage.Text = message.Body; myMessage.Html = message.Body; var credentials = new NetworkCredential(ConfigurationManager.AppSettings["emailService:Account"], ConfigurationManager.AppSettings["emailService:Password"]); // Create a Web transport for sending email. var transportWeb = new Web(credentials); // Send the email. if (transportWeb != null) { await transportWeb.DeliverAsync(myMessage); } else { //Trace.TraceError("Failed to create Web transport."); await Task.FromResult(0); } } } |
What worth noting here that the class “EmailService” implements the interface “IIdentityMessageService”, this interface can be used to configure your service to send emails or SMS messages, all you need to do is to implement your email or SMS Service in method “SendAsync” and your are good to go.
In our case we want to send emails, so I’ve implemented the sending process using Send Grid in method “configSendGridasync”, all you need to do is to replace the sender name and address by yours, as well do not forget to add 2 new keys named “emailService:Account” and “emailService:Password” as AppSettings to store Send Grid credentials.
After we configured the “EmailService”, we need to hock it with our Identity system, and this is very simple step, open file “ApplicationUserManager” and inside method “Create” paste the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context) { //Rest of code is removed for clarity appUserManager.EmailService = new AspNetIdentity.WebApi.Services.EmailService(); var dataProtectionProvider = options.DataProtectionProvider; if (dataProtectionProvider != null) { appUserManager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity")) { //Code for email confirmation and reset password life time TokenLifespan = TimeSpan.FromHours(6) }; } return appUserManager; } |
As you see from the code above, the “appUserManager” instance contains property named “EmailService” which you set it the class we’ve just created “EmailService”.
Note: There is another property named “SmsService” if you would like to use it for sending SMS messages instead of emails.
Notice how we are setting the expiration time for the code (token) send by the email to 6 hours, so if the user tried to open the confirmation email after 6 hours from receiving it, the code will be invalid.
1.3 Send the Email after Account Creation
Now the email service is ready and we can start sending emails after successful account creation, to do so we need to modify the existing code in the method “CreateUser” in controller “AccountsController“, so open file “AccountsController” and paste the code below at the end of the method:
1 2 3 4 5 6 7 8 9 10 11 |
//Rest of code is removed for brevity string code = await this.AppUserManager.GenerateEmailConfirmationTokenAsync(user.Id); var callbackUrl = new Uri(Url.Link("ConfirmEmailRoute", new { userId = user.Id, code = code })); await this.AppUserManager.SendEmailAsync(user.Id,"Confirm your account", "Please confirm your account by clicking <a href=\"" + callbackUrl + "\">here</a>"); Uri locationHeader = new Uri(Url.Link("GetUserById", new { id = user.Id })); return Created(locationHeader, TheModelFactory.Create(user)); |
The implementation is straight forward, what we’ve done here is creating a unique code (token) which is valid for the next 6 hours and tied to this user Id only this happen when calling “GenerateEmailConfirmationTokenAsync” method, then we want to build an activation link to send it in the email body, this link will contain the user Id and the code created.
Eventually this link will be sent to the registered user to the email he used in registration, and the user needs to click on it to activate the account, the route “ConfirmEmailRoute” which maps to this activation link is not implemented yet, we’ll implement it the next step.
Lastly we need to send the email including the link we’ve built by calling the method “SendEmailAsync” where the constructor accepts the user Id, email subject, and email body.
1.4 Add the Confirm Email URL
The activation link which the user will receive will look as the below:
1 |
http://localhost/api/account/ConfirmEmail?userid=xxxx&code=xxxx |
So we need to build a route in our API which receives this request when the user clicks on the activation link and issue HTTP GET request, to do so we need to implement the below method, so in class “AccountsController” as the new method as the below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
[HttpGet] [Route("ConfirmEmail", Name = "ConfirmEmailRoute")] public async Task<IHttpActionResult> ConfirmEmail(string userId = "", string code = "") { if (string.IsNullOrWhiteSpace(userId) || string.IsNullOrWhiteSpace(code)) { ModelState.AddModelError("", "User Id and Code are required"); return BadRequest(ModelState); } IdentityResult result = await this.AppUserManager.ConfirmEmailAsync(userId, code); if (result.Succeeded) { return Ok(); } else { return GetErrorResult(result); } } |
The implementation is simple, we only validate that the user Id and code is not not empty, then we depend on the method “ConfirmEmailAsync” to do the validation for the user Id and the code, so if the user Id is not tied to this code then it will fail, if the code is expired then it will fail too, if all is good this method will update the database field “EmailConfirmed” in table “AspNetUsers” and set it to “True”, and you are done, you have implemented email account activation!
Important Note: It is recommenced to validate the password before confirming the email account, in some cases the user might miss type the email during the registration, so you do not want end sending the confirmation email for someone else and he receives this email and activate the account on your behalf, so better way is to ask for the account password before activating it, if you want to do this you need to change the “ConfirmEmail” method to POST and send the Password along with user Id and code in the request body, you have the idea so you can implement it by yourself 🙂
2. Configure User (Username, Email) and Password policy
2.1 Change User Policy
In some cases you want to enforce certain rules on the username and password when users register into your system, so ASP.NET Identity 2.1 system offers this feature, for example if we want to enforce that our username only allows alphanumeric characters and the email associated with this user is unique then all we need to do is to set those properties in class “ApplicationUserManager”, to do so open file “ApplicationUserManager” and paste the code below inside method “Create”:
1 2 3 4 5 6 7 |
//Rest of code is removed for brevity //Configure validation logic for usernames appUserManager.UserValidator = new UserValidator<ApplicationUser>(appUserManager) { AllowOnlyAlphanumericUserNames = true, RequireUniqueEmail = true }; |
2.2 Change Password Policy
The same applies for the password policy, for example you can enforce that the password policy must match (minimum 6 characters, requires special character, requires at least one lower case and at least one upper case character), so to implement this policy all we need to do is to set those properties in the same class “ApplicationUserManager” inside method “Create” as the code below:
1 2 3 4 5 6 7 8 9 10 |
//Rest of code is removed for brevity //Configure validation logic for passwords appUserManager.PasswordValidator = new PasswordValidator { RequiredLength = 6, RequireNonLetterOrDigit = true, RequireDigit = false, RequireLowercase = true, RequireUppercase = true, }; |
2.3 Implement Custom Policy for User Email and Password
In some scenarios you want to apply your own custom policy for validating email, or password. This can be done easily by creating your own validation classes and hock it to “UserValidator” and “PasswordValidator” properties in class “ApplicationUserManager”.
For example if we want to enforce using only the following domains (“outlook.com”, “hotmail.com”, “gmail.com”, “yahoo.com”) when the user self registers then we need to create a class and derive it from “UserValidator<ApplicationUser>” class, to do so add new folder named “Validators” then add new class named “MyCustomUserValidator” and paste the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
public class MyCustomUserValidator : UserValidator<ApplicationUser> { List<string> _allowedEmailDomains = new List<string> { "outlook.com", "hotmail.com", "gmail.com", "yahoo.com" }; public MyCustomUserValidator(ApplicationUserManager appUserManager) : base(appUserManager) { } public override async Task<IdentityResult> ValidateAsync(ApplicationUser user) { IdentityResult result = await base.ValidateAsync(user); var emailDomain = user.Email.Split('@')[1]; if (!_allowedEmailDomains.Contains(emailDomain.ToLower())) { var errors = result.Errors.ToList(); errors.Add(String.Format("Email domain '{0}' is not allowed", emailDomain)); result = new IdentityResult(errors); } return result; } } |
What we have implemented above that the default validation will take place then this custom validation in method “ValidateAsync” will be applied, if there is validation errors it will be added to the existing “Errors” list and returned in the response.
In order to fire this custom validation, we need to open class “ApplicationUserManager” again and hock this custom class to the property “UserValidator” as the code below:
1 2 3 4 5 6 7 |
//Rest of code is removed for brevity //Configure validation logic for usernames appUserManager.UserValidator = new MyCustomUserValidator(appUserManager) { AllowOnlyAlphanumericUserNames = true, RequireUniqueEmail = true }; |
Note: The tutorial code is not using the custom “MyCustomUserValidator” class, it exists in the source code for your reference.
Now the same applies for adding custom password policy, all you need to do is to create class named “MyCustomPasswordValidator” and derive it from class “PasswordValidator”, then you override the method “ValidateAsync” implementation as below, so add new file named “MyCustomPasswordValidator” in folder “Validators” and use the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class MyCustomPasswordValidator : PasswordValidator { public override async Task<IdentityResult> ValidateAsync(string password) { IdentityResult result = await base.ValidateAsync(password); if (password.Contains("abcdef") || password.Contains("123456")) { var errors = result.Errors.ToList(); errors.Add("Password can not contain sequence of chars"); result = new IdentityResult(errors); } return result; } } |
In this implementation we added some basic rule which checks if the password contains sequence of characters and reject this type of password by adding this validation result to the Errors list, it is exactly the same as the custom users policy.
Now to attach this class as the default password validator, all you need to do is to open class “ApplicationUserManager” and use the code below:
1 2 3 4 5 6 7 8 9 10 |
//Rest of code is removed for brevity // Configure validation logic for passwords appUserManager.PasswordValidator = new MyCustomPasswordValidator { RequiredLength = 6, RequireNonLetterOrDigit = true, RequireDigit = false, RequireLowercase = true, RequireUppercase = true, }; |
All other validation rules will take place (i.e checking minimum password length, checking for special characters) then it will apply the implementation in our “MyCustomPasswordValidator”.
3. Enable Changing Password and Deleting Account
Now we need to add other endpoints which allow the user to change the password, and allow a user in “Admin” role to delete other users account, but those end points should be accessed only if the user is authenticated, we need to know the identity of the user doing this action and in which role(s) the user belongs to. Until now all our endpoints are called anonymously, so lets add those endpoints and we’ll cover the authentication and authorization part next.
3.1 Add Change Password Endpoint
This is easy to implement, all you need to do is to open controller “AccountsController” and paste the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
[Route("ChangePassword")] public async Task<IHttpActionResult> ChangePassword(ChangePasswordBindingModel model) { if (!ModelState.IsValid) { return BadRequest(ModelState); } IdentityResult result = await this.AppUserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword, model.NewPassword); if (!result.Succeeded) { return GetErrorResult(result); } return Ok(); } |
Notice how we are calling the method “ChangePasswordAsync” and passing the authenticated User Id, old password and new password. If you tried to call this endpoint, the extension method “GetUserId” will not work because you are calling it as anonymous user and the system doesn’t know your identity, so hold on the testing until we implement authentication part.
The method “ChangePasswordAsync” will take care of validating your current password, as well validating your new password policy, and then updating your old password with new one.
Do not forget to add the “ChangePasswordBindingModel” to the class “AccountBindingModels” as the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public class ChangePasswordBindingModel { [Required] [DataType(DataType.Password)] [Display(Name = "Current password")] public string OldPassword { get; set; } [Required] [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)] [DataType(DataType.Password)] [Display(Name = "New password")] public string NewPassword { get; set; } [Required] [DataType(DataType.Password)] [Display(Name = "Confirm new password")] [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] public string ConfirmPassword { get; set; } } |
3.2 Delete User Account
We want to add the feature which allows a user in “Admin” role to delete user account, until now we didn’t introduce Roles management or authorization, so we’ll add this end point now and later we’ll do slight modification on it, for now any anonymous user can invoke it and delete any user by passing the user Id.
To implement this we need add new method named “DeleteUser” to the “AccountsController” as the code below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
[Route("user/{id:guid}")] public async Task<IHttpActionResult> DeleteUser(string id) { //Only SuperAdmin or Admin can delete users (Later when implement roles) var appUser = await this.AppUserManager.FindByIdAsync(id); if (appUser != null) { IdentityResult result = await this.AppUserManager.DeleteAsync(appUser); if (!result.Succeeded) { return GetErrorResult(result); } return Ok(); } return NotFound(); } |
This method will check the existence of the user id and based on this it will delete the user. To test this method we need to issue HTTP DELETE request to the end point “api/accounts/user/{id}”.
The source code for this tutorial is available on GitHub.
In the next post we’ll see how we’ll implement Json Web Token (JWTs) Authentication and manage access for all the methods we added until now.
Good article, but you should really change all the references to ‘He/him’ etc to something gender neutral.
Thanks Liam for your comment, It was a mistake, all is fixed now, thanks again 🙂
Your Post have been a life saving material for us in my region, with all your past post and this, you have change our thinking toward DotNet Development. Thanks.
Please we need this series to be completed on time, so as to use it to complete our school project.
(We are student from Africa), We enjoy your series.
Thanks.
Glad to hear this, happy to know that I’m helping students in other contents to learn some cool technologies, good luck 🙂
When is the next post? Waiting for it!
Nice posts. Identity is hard for me to understand because it has so many functions.
It’s the first time i begin understanding it. Thanks!
Glad it was useful, most probably tomorrow 🙂 keep tuned!
Been here in the past months and I am learning new things in every post you have.. –I’m just another kid who wants to be something great.. and I tell you.. when that happen.. I will put your name as one my “hero”… Thank you so much…
Thanks for your sweet message, I’m really happy to know that my contribution is helping a lot of people out there, love what you do and have passion for it and I’m sure you will be a shining star =)
I’ve been following you for some time now, and I appreciate your approach to start with an empty project and add what you need with adequate explanation as you go. I find myself eager and watching for your next post as you are regular reading for me. Great job Taiseer!
Thank you Tony for your comment, glad to know that posts are useful. Part 3 is ready and should be published this Monday.
Hello Taisser, thanks for all your posts and very happy to see you MVP.
Alas, I hit a bit of a bump implementing your code. I create the user and while creating the link to send the confirmation email, it raises an exception:
on the instruction
var callbackUrl = new Uri(Url.Link(“ConfirmEmailRoute”, new { userId = user.Id, code = code }));
the Url.Link raises the exception
{“Value cannot be null.\r\nParameter name: request”}
I checked the function signature and it matches…
Do you have any idea on how to fix this… I can always hardcode the uri, but I’d rather suffer the pains of hell…
Thanks in advance
Miguel Delgado
Hi Miguel,
Thanks for your nice words,
Double check that you have created and named the route “ConfirmEmailRoute” correctly, as well you are passing the exact parameters. This is only what I can think of now.
Download the repo and compare the code, I’m sure there is simple glitch there.
Hope this helps!
Hi Taiseer,
What´s the point in creating an email confirmation api endpoint?
The user will see nothing else than a white screen in the browser.
The logical thing would be creating a web page to show a confirmation message, but I guess in this post you just wanted to focus on webapi.
Apart from that, please tell me what you think about this
In my case, I´m implementing all this from a mobile application and I´m trying to make things easy to the user. Imagine the situation: a user registers in the app and receives a message asking him/her to check the email. The email will redirect the user to a web page (confirmation page), and then, that user has to go back to the app. 4 step process (including registration form) is a bit overhead IMO.
So I was thinking about sending a short code (like the kind of sms confirmation codes) to the email so the user can just write it in a textbox (inside the mobile app) instead of using urls. I´m not sure if there is any way to modify the confirmation token asp.net generates to make for example, a 4 character number code. In that case you could see the code in the push notification of your email box and even no need to open it
Any thoughts?
Hi Diego,
As you suggested you need to build a GUI on your system where in contains a link for the “ConfirmEmailRoute” end point, once the user invokes this endpoint (click on that link) and you receive 200 OK status from the API, youir GUI needs to display confirmation message and maybe redirect him to the application as this SO question. Never tried it before 🙂
Regarding send SMS, as you suggested here you are going to send the SMS to the email so the user might need to open the email to read the SMS code there and then close the email and go back to application to enter it, so the same number of steps.
As well I’m not sure if you can generate friendly token (4 digits) and attach it to the user Id so the validation for this token happens seamlessly as it happens in method “AppUserManager.ConfirmEmailAsync”, I need to check this.
Hi Taiseer,
About the fiendly token:
If you do something like this in your asp.net userManager…
> IDataProtector dataProtector = dataProtectionProvider.Create(“ASP.NET Identity”);
> this.UserTokenProvider = new DataProtectorTokenProvider(dataProtector);
you get a huge token (kind of oAuth token), but I realized you can get a 6 digit “token” if you do this instead:
> this.UserTokenProvider = new EmailTokenProvider();
Not sure about the impact in the application or any related security issues… but I can say the confirmation works fine as well in this way.
About the confirmation message:
I´m not using legacy MVC in my project. Just webapi so I´m writing an html response like this:
[ActionName(“emailconfirmation”)]
public async Task ConfirmEmailAccount(string userId, string token)
{
IdentityResult result = await aspNetUserManager.ConfirmEmailAsync(userId, token);
if (result.Succeeded)
{
string body = “bla bla bla””; // this will be a proper web page
var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StringContent(body, Encoding.UTF8, “text/html”);
return response;
}
else
{
GetErrorResult(result.Errors);
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
}
About steps overhead and usability in mobile applications:
I´ve been reading about this in stack-overflow and many people agree that email confirmations on mobile applications are a no-go for many users. Of course that depends on your project needs but reasonable in many cases. So a nice way to go would be:
1. You send the welcome-confirmation email
2. The user can start using the app without confirming
3. After a time period you remind the user to confirm the account (by email)
4. After another time period, if the account has not been confirmed, you just delete it (this can be done in a background scheduled job)
That way, the users just trying to test your app will be able to do it quickly. And if they are interested after all, clicking a link shouldn´t be a problem.
Thanks for your answer!
ps: is there anyway to tag text as code in the comments?
I forgot to mention about redirecting user to the app from the email box.
That´s a nice solution if the user opens the email in the mobile device where the app is.
This can be done (I´m using Xamarin) like this: http://developer.xamarin.com/recipes/cross-platform/app-links/app-links-ios/ and this: http://developer.xamarin.com/recipes/cross-platform/app-links/app-links-android/
I ran the source code from the updated one from github, after update the package manager, I want to the create a new account from the create end-point
http://prntscr.com/6g70sh
This is the error i get http://prntscr.com/6g70z4
I have done all i needed to do, i dont know am doing wrong
1. http://prntscr.com/6g7180
2. http://prntscr.com/6g71s5 : Here, this is the credentials from my signup in sendgrid
Can someone help me out.
This issue with the SendGrid service trying to send confirmation email, make sure you are using the same NuGet package I have used in the project by checking packages.config file and double check that SendGrid username and password are set correctly in web.config as you obtained them from SendGrid after creating a SendGrid account.
I dont get the interface >> IIdentityMessageService
What is not clear about it? It interface used to separate the implementation of your SMS service or Email service from Web API logic, it has method named “SendAsync” in order to implement your sending logic in it
Hi Taiseer,
First off I would like to thank you for your post and all the information concerning JWT. I am currently doing an internship in a company where I was asked to do a small authentication and authorization using Identity + JWT. I ran into a small bug and I don’t know if its me that did not implement your explications properly but for some reason I cannot seem to be able to do any calls to Identity, for example User.Identity.GetUserId(); is always null. It feels like the data/information from my token does not get passed. Would you be able to give me any pointers or would you have any idea? I have been looking at your AngularJSAuthentication solution and all your other post and I can’t seem to be able to find out what it is.
Thank you,
Simon.
Hi Simon, are you calling the method User.Identity.GetUserId(); inside a protected controller? A controller or action method attributed with [Authorize]? If yes then the Identity should be set, can you try checking the property User.Identity.Name too? Does it return the authenticated UserId?
Hello Taisser, how are you doing? I’m having this issue too. The User.Identity.Name is OK, but the ID is always null. When I inspect the User.Identity on Imediate Window I receive the following:
{System.Security.Claims.ClaimsIdentity}
[System.Security.Claims.ClaimsIdentity]: {System.Security.Claims.ClaimsIdentity}
AuthenticationType: “Bearer”
IsAuthenticated: true
Name: “bla@bla.com.br”
I’m not sure if I’m missing something here.
Hello Taiseer,
I am also having the problem with User.Identity except User.Identity.Name = “”. Also for me IsAuthenticated = false. I can decode the JWT token and my username is contained therein. Not sure why Identity is not picking it up.
Hi,
I had the same issue. My user was authorized, but the returned UserId was null. I guess, the name claim has the wrong scheme. It had “http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name” instead of “nameidentity” at the end. Thus the method returnes null.
I solved it by using the name to get the id:
userManager.FindByNameAsync(User.Identity.Name)
Regards,
Torsten
I think I just found the origin of my problem why GetUserId() returns null. In the method “GrantResourceOwnerCredentials” of my AuthorizationServerProvider the claims are added to the user like this:
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
identity.AddClaim(new Claim(“sub”, context.UserName));
identity.AddClaim(new Claim(“role”, “user”));
I guess if you add the claimtype “NameIdentifier” the GetUserId() method should return the user.
Note: in my answer above I wrote “namedidentity”. I guess this is wrong and should be “NameIdentifier”.
Regards,
Torsten
You are correct, the claim of type “NameIdentifier” should be used to return the UserId when calling GetUserId()
Hi,
List _allowedEmailDomains = new List { “outlook.com”, “hotmail.com”, “gmail.com”, “yahoo.com” };
How do i create a CustomUserValidator for user and admin email separately
My application will be split into two admin registration and user registration
I want the users to register with any email address (e.g gmail.com, yahoo.com) while the admin will only register with the official company email (e.g admin@company.com)
How can i seperate the logic for this or conditionally configure this, since the validation logic for the user admin are called from one single point in the startup.cs
Is it advisable to create the API for admin seperate from the user, or i can use thesame API for both logic.
Thanks
Hi Taiseer,
Microsoft.Owin.Testing doesn’t provide a DataProtectionProvider in the options parameter of ApplicationUserManager.Create(). I’ve changed the method to:
…
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
appUserManager.UserTokenProvider = new DataProtectorTokenProvider(dataProtectionProvider.Create(“ASP.NET Identity”))
{
//Code for email confirmation and reset password life time
TokenLifespan = TimeSpan.FromHours(6)
};
}
else
{
var provider = new Microsoft.Owin.Security.DataProtection.DpapiDataProtectionProvider(“ASP.NET Identity”);
UserManager userManager = new UserManager(new UserStore());
appUserManager.UserTokenProvider = new DataProtectorTokenProvider(provider.Create(“ASP.NET Identity”)) {
//Code for email confirmation and reset password life time
TokenLifespan = TimeSpan.FromHours(6)
};
}
…
Now it’s working.
Hi!
can u please tell me how to change the password in mvc using asp.net identity
Hi Taiseer,
Great post! It really helps me on my project.
Just a quick heads up for those who might experience same as mine.
I was setting up our own SMTP server and testing email confirmation through Postman, “Invalid Token” happened for URL confirmation.
Solution:
http://tech.trailmax.info/2015/05/asp-net-identity-invalid-token-for-password-reset-or-email-confirmation/
It works for me:
CreateUser() add
code = System.Web.HttpUtility.UrlEncode(code);
ConfirmEmail() add
code = System.Web.HttpUtility.UrlDecode(code);
Thanks for sharing this, what you are doing is better practice because the token might contain unsafe URL chars which needs to be Url encoded before.
The callback URL is for a GET to the API, but how do you handle the case where the front end is solely Angular? How can I get the ConfirmEmail API function to, upon execution from the email link click event, redirect back to my front end Angular site after updating the EmailConfirmed flag?
@Doug were you able to find the solution if yes then please share with me
You can edit the call back URL at this section of code in the AccountsController
var code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
var callbackUrl = new Uri(Url.Link(“ConfirmEmailRoute”, new { userId = user.Id, code }));
await UserManager.SendEmailAsync(user.Id, “Confirm your account”, “Please confirm your account by clicking here“);
Hi Taiseer,
I tried to create a free SendGrid account. They didn’t provide me an account since I do not have a website. Can you please show me how to use gmail or hotmail account instead SendGrid ? (even asp.net tutorials use sendGrid, it looks like they may have stoped the service for leaners, if you can provide a free email alternative it would be beneficial to new learners)
This is what SendGrid support said:
“Thank you for providing further information. Although to provision your account we’ll need to know what type of emails you’ll be sending using our service, the website provided during registration was not sufficient to help us determine this. Without a valid, working website upon which we can determine this, your account will not be provisioned. Please get back in touch with us when your website is nearing a state of completion.
Best,
IULIANA O.
Technical Support Engineer
SendGrid”
Thanks for posting this.
just an expansion on the comment:
“Notice how we are setting the expiration time for the code (token) send by the email to 6 hours”
so far, my testing on the Indentity 2.1 framework would indicate that the token contains datetime information on when it was sent, rather than any data on when it is to expire. The ConfirmEmailAsync method then seems to apply the TokenLifespan as part of validating the token. The relevance of this is that changing TokenLifespan (in testing or production) will be effective retrospectively on tokens previously sent.
Did you find that GenerateEmailConfirmationTokenAsync is creating a 500 character token? i.e. http://localhost/api/account/ConfirmEmail?userid=xxxx&code=
Sending such a long URL is running up against spam filters in our case.
Hi Nathan,
To be honest I didn’t notice that huge number of generated characters, were you able to find solution for this issue?
Slm from Africa,
thx for the stuff
i’m unable to pull the namespace for AspNetIdentity.WebApi.Services.EmailService()
I Got it Sorry, Just Need Some rest
No problem Ali 🙂
Thank you for the good article! Although i followed your steps exactly, there seems to be an issue, at least for me: When i send a create-user request, the programme creates a new user, but does not call the EmailService class so no email is sent. Is this a known issue? Thanks in advance 🙂
Hi Michael, glad you liked it, I guess you are missing registering the service in AppUserManager as this LOC. If it is already there, check that your SendGrid ApiId and Secret are correct.
Hope this will help.
Hi, thank you for your quick response! I checked the registering of the EmailService in AppUserManager, that wasn’t the problem. The credentials for the SendGridAPI are fine aswell. But by inserting Debug.writeline commands i found out that the ApplicationUserManager class is not called. I tried your downloaded your version from github and there it worked just perfectly and the output-commands were shown in the console. Do you know anything else that could be the problem?
Hi Taiseer, Great article, all your articles have helped me a lot, thanks so much.
I’m still a bit confused about the emailservice, I followed your AngularJsAuthentication tutorials and so i dont have a ApplicationUserManager class or a “Create” method to plug in the the EmailService with Identity system, where should I place the body of code?
Hi Mazin,, and assign the “EmailService” property to your email sending logic, hope this somehow clarifies your concern.
If you need to use the email service to send emails, then you have to user the ASP.NET Identity system and create an instance of the UserManager
Yeah, I totally should buy you a beer. Thanks for the very detailed information!
You are welcome, happy to help 🙂
Hello Taiseer,
I have a quick question. May be simple however I’ve googled away and haven’t exactly found the answer I am looking for. SO I am building a web app that utilizes AngularJS for the Frontend. Specifically using the Ui,router rather than ngRoute. So I configured a state(route) that has the parameters of UserId and Code in order to use the parameters in my ConfirmEmailController.js . In the ConfirmEmailController.js I call to the Account controller which will then confirm the email. Upon failure or success the html will display the response message. My Issue is this… In the email that gets sent to the User, the link needs to be something like this Here
However, in the AccountController.cs under method Register() , I cannot get the callbackUrl to be correct. I mean it does output the link as I would expect, but when clicked I get this error “The request filtering module is configured to deny a request that contains a double escape sequence.” . So I ask you how can I build a link that will pass the UserId and Code in an email that doesn’t compromise the built in security that’s blocking double spacing, but handle the View and Controller in Angular. OR Am I putting much more work into it than I need to and there is a simpler way to accomplish this? Thank you for taking the time to read and respond to my question! Sorry for the long message but I wanted to be thorough.
Hi Steven,
I’m not sure if you need to URL encode the code generated then decode it once you receive it, this is the only thing that I’m suspecting now. As well you might try sending the activation code as a query string not part of the URI.
Let me know if this solve your issue.
Hello! And thank you for your reply! I ended up sending it as query strings. Both userId and code. Just have to have the user login and it’ll call to the api/Account . My only issue is that the ‘+’ in the userId’s gets replaced by spaces. Its Angular. It used to be encoded as %2B which is what I need, however many people filed issues with it on github because they needed it to be treated as %20 ( a space ) for search query strings. Since then it has been updated and now creates a problem since I need the literal plus sign. I am going to see if there is a way to just replace its encoding back to %2B. If you’ve got any insight or a work around that would be much appreciated! . Thank you again!
SO It was an easy fix. Just do a string.replace on the spaces and put +’s there. However my final error is “Invalid Token” . The result.Succeeded from the ConfirmEmailAsync comes back false. I placed breakpoints in the Register method and the ConfirmEmail method to verify the userId and code generated at both spots. They are identical. I’m not exactly sure what is going on at this point.
FIXED! Sorry for the various replies but it’s been an interesting journey haha. SO seems like even if I do the string.replace in the frontend, when I do the $http call to the backend angular re-encodes and decodes it. Easy solution. Just do the code = code.replace(” “,”+”); right before calling ConfirmEmailAsync();
THANK YOU for all your help!
Hi Steven,
It is encoding issue as you suggested, I do not know why the ASP.NET team didn’t generate the confirmation code using safe URL characters, they know that those codes will be transmitted in URL.
Hi Taiseer,
I keep getting this error when I run it locally , could you please advise?
FYI I added the below keys which are valid:
exceptionMessage: “Bad Request Check
Errors
for a list of errors returned by the API.”exceptionType: “Exceptions.InvalidApiRequestException”
stackTrace: ” at SendGrid.ErrorChecker.CheckForErrors(HttpResponseMessage response, Stream stream) at SendGrid.ErrorChecker.d__0.MoveNext() — End of stack trace from previous location where exception was thrown — at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at SendGrid.Web.d__0.MoveNext() — End of stack trace from previous location where exception was thrown — at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.GetResult() at AspNetIdentity.Services.EmailService.d__1.MoveNext() in c:\users\armaghanbabak\documents\visual studio
Hi Khalaf,
Make sure you are using the latest version of SendGrid assembly, I’m not sure why it is generating this error. Please share it on Stack Overflow.
Solved a lot of issues. This article is the masterpiece of all my research on ASP .NET Identity and OWIN Token.
Glad you find it useful Biswa, thanks for your comment 🙂
Hi Taiseer,
I have followed your part 1 tutorial, so far it has worked awesome, but in this part 2, you said place the code in ApplicationUserManager class, which you have not specified when it was created. I am looking your project in the GitHub and seeing you have Infrastructure folder and there you have it already, so I am trying to copy the code from there and trying to understand each bit of code. Please advice if I missed something.
Thanks,
Hassan
Hi Hassan,
I’m not sure if you missed something or I forgot a simple step but follow up with the all the posts and everything is described clearly, hope the series will be useful for you.
Hi Taiseer,
Sorry It was my mistake, I went to this Part 2 of ‘
ASP.NET Identity 2.1 Accounts Confirmation, and Password Policy Configuration – Part 2’, thought that this is a Part 2 of ‘Token Based Authentication using ASP.NET Web API 2, Owin, and Identity’. But I have finally found the Part 2 here https://bitoftech.net/2014/06/09/angularjs-token-authentication-using-asp-net-web-api-2-owin-asp-net-identity/
Got confused with so many parts of authentication. This is fabulous article you have written, I am following and understanding the authentication much better now. Thanks a lot man.
You are welcome, hope you grasped the concepts by now 🙂
Same thing happened to me. Exact same thing.
For anyone who might run in to this kind of issue that if you are using different attributes than just [Route] or [HttpGet]… On confirmEmail action you must put the attribute [HttpGet] as well.. otherwise your link to confirm email won’t work ..
Not sure why, may be Taiseer could add something awesome here on this just like his articles 🙂
Hey Taiseer, Very useful articles, specially in a world where there aren’t much resources on the subject of identity … Hats off my dear !!!
Hi Saad, thanks for your message, it is working at my end. But I need to check this and update the post if something is missing. Thanks again.
Hi Taiseer,
Thanks for the way you have narrated the story. I stand at a little confused point and need your help in getting out of this. it is a bit lengthy one, request you to go through it patiently. Thanks for your time.
I work on an application where I have a separate MVC layer and Web API Layer, both have the same authentication mechanism, I have chosen the individual accounts authentication option while adding the projects. The web api service layer will be directly accessed by some other mobile clients also.
But when the user logs in through MVC he should be able to access Web Api seamlessly, but I don’t want to really pass the username and password fro MVC to the Web Api layer, I am told it is a bad practice. but i need to authenticate and authorize my user, so the only option i have thought of is to have a default account at Web API level to issue tokens, and this will be called from MVC post the authentication and a token will be returned which is written to a cookie in the client. Now the Ajax calls from the UI can use this bearer token and get the job done.
The only glitch I have here is that, because I am using a default account I need user details again for authorization at service level, though I am doing authorization at my UI level. The user can spoof the system. I was lost here and came up with a solution like, when the user logs in to MVC will send across user details also along with the call to get the WebAPI token and issue another token to the user so that the user uses both of the tokens to make a call to web api from MVC.
I am not sure if this works or if it is even the best way. I just wanted to check with you, how I should go from here. Any help on this will be really great.
Regards,
Aditya
Your proposed solution is good, you need to obtain an access token while you are authenticating the user for the MVC application, so you only send the username/password only once, once you obtain the access token you can store in a cookie for further use.
Hi Taiseer,
First of all, this article is AWESOME. I am able to get a clear view of OAuth Identity Services because of your articles. I created the confirm email functionality for new User. The confirm email link validity is set to 24 hours as shown in this article, but somehow the link expires after 50 mins. I am unable to figure out where the issue is. Could you please suggest something.
Regards,
Rashmi
I found the solution to this issue. I am posting it if somebody faced the same issue. In my case the services and web API were on different servers. Different machine keys caused this issue. So I generated the machine key for my Web application and posted the same machine key in web.config file of Identity service. After that it worked. For more information on generating machine key, following link is helpful.
http://gunaatita.com/Blog/How-to-Generate-Machine-Key-using-IIS/1058
The confusion around ApplicationUserManager is because it was not created in Part 1. According to the Index at the top of this post, this is Part 2. And up until now, there has been no mention of an ApplicationUserManager. But, seeing that it is in the GIT repository, I’m not too worried. But it does kind of negatively impact the didactic flow of the articles.
Whoops. Looks like I’m getting this series mixed up with your 2014 series of articles. Sorry. Disregard my last comment. And thank you for the great articles!
You are welcome, no problem and let me know if you need further help.
Easily, the best posts on asp.net Identity on the ‘Net. Puts Microsoft documentation to shame.
Congratulations !
Thank you, happy that posts are helpful.
Thank you for the great post. In my case I need to validate both email and phone number for one given account.
I can generate email token with GenerateEmailConfirmationTokenAsync and then ConfirmEmailAsync.
I didn’t find related methods for SMS.
Could you please clarify?
Taiseer….awesome series. Everything working perfectly. Your tutorials are extremely clear and accurate. Very rare. I do have one issue….I am using SendGrid for my Email Confirmation. When I change EmailConfirmation to POST as you recommend for doing Password comparison, the link that arrives in the confirmation email is no longer functional since a link can’t generate HTTP POST without intervening JScript. I just get a ‘The requested resource does not support http method ‘GET” error. Can you give a little detail on how to implement the password check in the confirmation (including maybe some suggestions for encrypting it)?
Hi Dennis,
I think you facing an issue with sending the validation token generated by asp.net identity, it contains unsafe url chars, so you should encode it before sending it, then decode it once you receive it.
The below helper functions might help:
public static string Base64ForUrlEncode(string str)
{
byte[] encbuff = Encoding.UTF8.GetBytes(str);
return TextEncodings.Base64Url.Encode(encbuff);
}
public static string Base64ForUrlDecode(string str)
{
byte[] decbuff = TextEncodings.Base64Url.Decode(str);
return Encoding.UTF8.GetString(decbuff);
}
var callbackUrl = string.Format("{0}?code={1}&email={2}", model.RedirectUri, base64UrlEnCode, model.Email);
string decodedCode = Base64ForUrlDecode(model.Code);
Thanks for the reply. Unfortunately that has the same result.
The resulting link from CreateUser looked like this:
http://localhost/rcatshop/api/accounts/ConfirmEmail?userId=1007&code=XXX
I can’t see anything bad about the format of the link.
Here is my CreateUser:
[Route(“create”)]
public async Task CreateUser(CreateUserBindingModel createUserModel)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var user = new ApplicationUser()
{
UserName = createUserModel.Username,
Email = createUserModel.Email,
FirstName = createUserModel.FirstName,
LastName = createUserModel.LastName,
Level = 3,
JoinDate = DateTime.Now.Date,
};
IdentityResult addUserResult = await this.AppUserManager.CreateAsync(user, createUserModel.Password);
if (!addUserResult.Succeeded)
{
return GetErrorResult(addUserResult);
}
// Generate confirmation token
string code = await this.AppUserManager.GenerateEmailConfirmationTokenAsync(user.Id);
// Create link for “click on this to confirm”
var callbackUrl = new Uri(Url.Link(“ConfirmEmailRoute”, new { userId = user.Id, code = UrlEncodeDecode.Base64ForUrlEncode(code), password = UrlEncodeDecode.Base64ForUrlEncode(createUserModel.Password) }));
// Send the confirmation email
await this.AppUserManager.SendEmailAsync(user.Id, “Confirm your account”, “Please confirm your account by clicking here“);
Uri locationHeader = new Uri(Url.Link(“GetUserById”, new { id = user.Id }));
return Created(locationHeader, TheModelFactory.Create(user));
}
Here is the text of the email I receive:
Please confirm your account by clicking here
Here is my Confirm endpoint:
[HttpPost]
[Route(“ConfirmEmail”, Name = “ConfirmEmailRoute”)]
public async Task ConfirmEmail(int userId, string code = “”, string password = “”)
{
string cd = UrlEncodeDecode.Base64ForUrlDecode(code);
string pw = UrlEncodeDecode.Base64ForUrlDecode(password);
if (userId == null || string.IsNullOrWhiteSpace(cd) || string.IsNullOrWhiteSpace(pw))
{
ModelState.AddModelError(“”, “User Id, Code, and Password are required”);
return BadRequest(ModelState);
}
var temp = await this.AppUserManager.FindByIdAsync(userId);
bool PasswordOk = false;
if (temp != null)
{
PasswordOk = await this.AppUserManager.CheckPasswordAsync(temp, pw);
}
if (PasswordOk)
{
IdentityResult result = await this.AppUserManager.ConfirmEmailAsync(userId, cd);
if (result.Succeeded)
{
return Ok();
}
else
{
return GetErrorResult(result);
}
}
else
{
return StatusCode(System.Net.HttpStatusCode.Unauthorized);
}
}
And here is the result when I click the link from my email:
The requested resource does not support http method ‘GET’.
As long as I decorate the Confirm as:
[HttpGet]
[Route(“ConfirmEmail”, Name = “ConfirmEmailRoute”)]
public async Task ConfirmEmail(int userId, string code = “”, string password = “”)
All works. But as you stated in your tutorial, shouldn’t pass a password in a get param.
I am not clear how to get around this.
Is there something else I have missed?
Thanks!
@tauseer joudeh It is great article but I have one problem after email confirmation I want to redirect user to login page how that could be done please suggest?
@taiseer joudeh its very nice article could you please help me if I would like to change callbackurl from web api route to angular how could be done ?
Hi,
This is very deep implementation detail for your case, I really can not look at it right now but you could add any link you want in your response and the user will receive it in the email and click on it and get redirected to this URL.
Hi Taiseer,
Like others I have found your series on web api identity and token authorization very useful.
I have a question about using email/text to validate the user identity. I am writing an API for a mobile app; on registration user will need either to enter email or mobile number so that their identity can be verified. Since sending email/text message to user is not free, I want to protect from being attached by someone who tries to create many fake registrations. For example, if someone somehow knows my user registration url and since user post request has to allow anonymous (to allow real user to register through the mobile app); he/she can post many fake registrations each of which will trigger sending an email or a text message; which can be costly.
Do you have any advice on preventing such an attack? My idea is to add a custom header in my new user post request so that I can validate this header value in each post request and if the header value is not matching a secret then I will reject the post request. And since this is for a mobile app, it’s safe to include the secret in the header on the client side for real user registration. Does this make sense?
Thanks.
Maybe you need to implement some sort of throttling (rate limiting) for this endpoint, check out this repo, I think it will be better than building thing from scratch.
Hi Taiseer,
i really love your articles! They help me soooooo much, but i still got a problem 🙁
The Destination of IdentityMessage in my EmailService is always null. So i tried to get the email of the newly created user, and “UserManager.GetEmail” always returns null too, but the user is created as i can see in the database and his id is correct too. Do you have any idea what this can be, or do you had any issue like this before?
That is strange, did you try to download the repo and test it out and compare it to your code?
Really hard to troubleshoot the issue without seeing your code, so my recommendation is to download the repo and compare.
Hello,
I am very much enjoying this tutorial. It is helping me with the understanding i have needed, but then i run into this error which i still can’t solve. I am still on the first post “account management” and i get this error:
{“message”:”The request is invalid.”,”modelState”:{“”:[“Name cannot be null or empty.”]}}
I do not know where to find the error to fix it.
Please help me
hi … Thanks for the wonderful education.
I am EmailService class to using Gmail instead of Sendgrid have changed.But I could not get the answer. Please help me to fix the error.
using Microsoft.AspNet.Identity;
using System.Net;
using System.Net.Mail;
using System.Threading.Tasks;
namespace Myapp.Services
{
public class EmailService : IIdentityMessageService
{
public async Task SendAsync(IdentityMessage message)
{
await configSendasync(message);
}
// Use NuGet to install SendGrid (Basic C# client lib)
private async Task configSendasync(IdentityMessage message)
{
using (MailMessage email = new MailMessage())
{
string emailFrom = “xxxx@gmail.com”;
string password = “xxxxx”;
// Create the message:
email.From = new MailAddress(emailFrom,”email sender”);
email.To.Add(new MailAddress(message.Destination));
email.BodyEncoding = System.Text.Encoding.UTF8;
email.SubjectEncoding = System.Text.Encoding.UTF8;
email.Subject = message.Subject;
email.Body = message.Body;
email.IsBodyHtml = true;
// Can set to false, if you are sending pure text.
//email.Attachments.Add(new Attachment(“C:\\SomeFile.txt”));
//email.Attachments.Add(new Attachment(“C:\\SomeZip.zip”));
using (SmtpClient smtp = new SmtpClient())
{
// Configure the client:
// Credentials:
smtp.Host = “smtp.gmail.com”;
//TLS port: 25, 587
//SSL port:465
smtp.Port = 25;
// Create the credentials:
smtp.Credentials = new NetworkCredential(emailFrom, password);
smtp.DeliveryMethod = SmtpDeliveryMethod.Network;
smtp.UseDefaultCredentials = false;
smtp.EnableSsl = true;
//smtp.Send(email);
// Send:
await smtp.SendMailAsync(email);
}
}
}
}
}
hi … Thanks for the wonderful education.
I am EmailService class to using Gmail instead of Sendgrid have changed.But I could not get the answer. Please help me to fix the error.
private async Task configSendasync(IdentityMessage message)
{
using (MailMessage email = new MailMessage())
{
string emailFrom = “xxxxxx@gmail.com”;
string password = “xxxxxxx”;
// Create the message:
email.From = new MailAddress(emailFrom,”xxxxxxx”);
email.To.Add(new MailAddress(message.Destination));
email.BodyEncoding = System.Text.Encoding.UTF8;
email.SubjectEncoding = System.Text.Encoding.UTF8;
email.Subject = message.Subject;
email.Body = message.Body;
email.IsBodyHtml = true;
using (SmtpClient smtp = new SmtpClient())
{
// Configure the client:
// Credentials:
smtp.Host = “smtp.gmail.com”;
//TLS port: 25, 587
//SSL port:465
smtp.Port = 25;
// Create the credentials:
smtp.Credentials = new NetworkCredential(emailFrom, password);
smtp.DeliveryMethod = SmtpDeliveryMethod.Network;
smtp.UseDefaultCredentials = false;
smtp.EnableSsl = true;
//smtp.Send(email);
// Send:
await smtp.SendMailAsync(email);
}
}
}
Hi,
At that time I do this tutorial, I used Web API v2 and Net Framework 4.5. I need install SendGrid-Net40 instead of SendGrid.
SendGrid does not have SendGridMessage Class.
Hi Ninh,
Using Install-Package Sendgrid -Version 5.0.0
hey clark, you saved my day, thanks
Hello Taiser, great tutorials 🙂
i get an error on
applicationUserManager.cs
appUserManager.EmailService = new AspNetIdentity.WebApi.Services.EmailService();
Saying: The type or namespace name ‘AspNetIdentity’ could not be found (are you missing a using directive or an assembly reference?)
Hello Vlad,
Make sure you are updating the packages using NuGet package manager, if this didn’t work please search the issue on GitHub as I’m sure you will find a detailed solution.
Hi Vladimir,
Hope you already found the solution. If not, the problem is just that here Taiseer specifies the full namespace of the EmailService you created before. Just remove the “AspNetIdentity.WebApi.Services.” and it should work perfectly 😉
Hello friend.
Thanks for the excellent article. Its saving my life.
Im having problem with the username validation and UniqueEmail validation.
I have added as you mentioned the :
appUserManager.UserValidator = new MyCustomUserValidator(appUserManager)
{
AllowOnlyAlphanumericUserNames = true,
RequireUniqueEmail = true
};
to my ApplicationUserManager class, but when i try to create a new user, its not being used.
Im making some mistake?
Thanks
Hello Fabio,
Can you show me how did you register your AppUserManager there should be soemthing missing.
Hello Taiser,
Great stuff!!!
In the last code snippet of this post: DeleteUser, shouldn’t the method be decorated with [HttpDelete]?
Otherwise how will MVC know to associate it with a DELETE verb?
I tried it and it only worked if I decorate it…
Thank you!
2 years later and this walk-through is still extremely helpful. Bravo and thank you!
I’m new to API access security, so a few questions for you:
1) When you mention changing the “ConfirmEmail” route to POST, could you elaborate on how this would be done? The only way I can fathom would be to send a link that leads to a webpage (something like http://www.mywebsite.com/validate?userid=xxxx&code=xxxx), where the user would then be prompted to enter the password that they just created for the account. This would effectively stop an unintended user from validating an account by accident, that they didn’t create.
2) Seeing as these posts ARE 2 years old now, are there any major differences in implementation that you would recommend given the current state of the .NET Identity Framework?
3) Would you still say that the .NET Identity Framework is one of the best ways to securely manage WebAPI access by today’s standards? I want to make sure I’m not implementing / familiarizing myself with something that has quickly fallen behind standard-wise.
Hello LK,
Thanks for your kind comment 🙂
Please find the answers below:
1. If you need to have ultimate security workflow, asking for the password is a better approach, so you need to include the password when you validate the code and userid/password.
2. If you are using ASP.NET 4.6 (Not ASP.NET Core) this post is very relevant, I believe I was using the latest version of ASP.NET Identity (2.1) which is used with ASP.NET 4.6
3. Well the Identity framework is used to store your users, roles, claims, etc.. the OWIN middlewares for Authorization are used to protect your WebAPI, I recommend always to take a look at the ThinkTecture Identity Server if you want to learn something which is built on OAuth and OpenId connect standards and provides you SSO, support for multiple clients, etc..
Hi,
First of all awesome work helped me a lot!!
Second there were some bugs since sendgrid is updated but i figure out error on this line
var transportWeb = new Web(credentials);
what is Web here i cant find its namespace
install old version 5.0.0 of sendgrid
Great series of articles! Thanks for it!
You are welcome Dima 🙂
Hi do you have any tutorial of this implementation with MySQL
Hi please i need your help here. when ever i change user password with both
var identityResult = await UserManager.ChangePasswordAsync(model.UserId, model.OldPassword, model.NewPassword);
and this
var token = await UserManager.GeneratePasswordResetTokenAsync(userId);
var result = await UserManager.ResetPasswordAsync(userId, token, newPassword);
the password do change successfully but i can’t login with the new password but the old password.
the new password only work when i restart the application.
i use this for login
var user = await UserManager.FindAsync(userName, password);
but return null when ever i change password
here is my user manager
public class ApplicationUserManager : UserManager
{
public ApplicationUserManager(IUserStore store)
: base(store)
{
}
public static ApplicationUserManager Create(IdentityFactoryOptions options,
IOwinContext context)
{
var manager = new ApplicationUserManager(new UserStore(context.Get()));
var dataProtectorProvider = options.DataProtectionProvider;
var dataProtector = dataProtectorProvider.Create(“My Asp.Net Identity”);
manager.UserTokenProvider = new DataProtectorTokenProvider(dataProtector)
{
TokenLifespan = TimeSpan.FromMinutes(30)
};
// Configure validation logic for usernames
manager.UserValidator = new UserValidator(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configure validation logic for passwords
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = false,
RequireDigit = false,
RequireLowercase = false,
RequireUppercase = false
};
// Configure user lockout defaults
manager.UserLockoutEnabledByDefault = false;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
manager.MaxFailedAccessAttemptsBeforeLockout = 5;
manager.RegisterTwoFactorProvider(“Phone Code”, new PhoneNumberTokenProvider
{
MessageFormat = “Your security code is {0}”
});
manager.RegisterTwoFactorProvider(“Email Code”, new EmailTokenProvider
{
Subject = “Security Code”,
BodyFormat = “Your security code is {0}”
});
//manager.EmailService = new EmailService();
manager.SmsService = new SmsService();
return manager;
}
}
Hоwdy, i read your blog from time to time and і own a similar one and
i was just curious if уou get a lot of spam responses?
If so how do you prevent it, any plugin or anything y᧐u can advise?
I get so much lateⅼy it’s driving me insane so any ѕupport is νery mᥙch apprеciated.
Ꭼxcellent web site. Lots of useful info here. I am sending
it to a few buddies ans additionally sharing in delicious.
And certainly, thanks on your sweat!
Hi Taiseer
I haven’t seen your new blog for a long time and look forward to sharing your new blog!
ApplicationUser.Level is defined as byte, but the binding model defines it as int. The default value you are setting it to is 3 which is not valid for a byte. Please explain.
disregard this…byte, not bit. 🙂
Hi sir . What do you fill a code=xxxx ?. Thank you
how to confirm email?
Great post, Taiseer!
I am implementing a system where only the admin would create a new user. So, for a new user to be created, the admin would have to be logged in. There is no telling when the new user would receive the confirmation email and click on the link. I notice the ConfirmEmail method directs to either an Error page or a ConfirmEmail page based on result of confirmation. Would all logged in users be directed to one of these two pages on receiving the confirmation? Could the system simply log the result using iLogger (or any other system) and not return any view? If so, how would this be implemented ?
Good article
thank you!