Gicu Mironica
Gicu Mironica

Reputation: 635

Confirm Email in asp.net core web api

This API is intended for a mobile application. The goal is to let the user confirm the email upon registration. When the user registers, a confirmation link is generated and sent over the email. I've done it the same way in a MVC project, it worked fine, but in a Web API project looks like it ain't gonna cut. Now when the user clicks that link, the respective action method should be hit and do the job.

The only problem is, the ConfirmEmail action method is just not getting triggered when clicking the confirmation link although it looked fine.

Here are the main configurations which might help

MVC service configuration

services.AddMvc(options => 
                    {  
                        options.EnableEndpointRouting = true;
                        options.Filters.Add<ValidationFilter>();
                    })
                    .AddFluentValidation(mvcConfiguration => mvcConfiguration.RegisterValidatorsFromAssemblyContaining<Startup>())
                    .SetCompatibilityVersion(Microsoft.AspNetCore.Mvc.CompatibilityVersion.Version_3_0);

Identity Service

 public async Task<AuthenticationResult> RegisterAsync(string email, string password)
        {
            var existingUser = await _userManager.FindByEmailAsync(email);

            if(existingUser != null)
            {
                return new AuthenticationResult { Errors = new[] { "User with this email address exists" } };
            }

            // generate user
            var newUser = new AppUser
            {
                Email = email,
                UserName = email
            };

            // register user in system
            var result = await _userManager.CreateAsync(newUser, password);

            if (!result.Succeeded)
            {
                return new AuthenticationResult
                {
                    Errors = result.Errors.Select(x => x.Description)
                };
            }

            // when registering user, assign him user role, also need to be added in the JWT!!!
            await _userManager.AddToRoleAsync(newUser, "User");

            // force user to confirm email, generate token
            var token = await _userManager.GenerateEmailConfirmationTokenAsync(newUser);

            // generate url
            var confirmationLink = _urlHelper.Action("ConfirmEmail", "IdentityController",
                    new { userId = newUser.Id, token = token }, _httpRequest.HttpContext.Request.Scheme);

            // send it per email
            var mailresult = 
                await _emailService.SendEmail(newUser.Email, "BingoApp Email Confirmation",
                $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(confirmationLink)}'>clicking here</a>.");
            if (mailresult)
                return new AuthenticationResult { Success = true };
            else
                return new AuthenticationResult { Success = false, Errors = new List<string> { "Invalid Email Address"} };
        }

Controller

        [HttpPost(ApiRoutes.Identity.Register)]
        public async Task<IActionResult> Register([FromBody] UserRegistrationRequest request)
        {
            if (!ModelState.IsValid)
            {
                return BadRequest(new AuthFailedResponse
                {
                    Errors = ModelState.Values.SelectMany(x => x.Errors.Select(xx => xx.ErrorMessage))
                });
            }

            // register the incoming user data with identity service
            var authResponse = await _identityService.RegisterAsync(request.Email, request.Password);          

            if (!authResponse.Success)
            {
                return BadRequest(new AuthFailedResponse
                {
                    Errors = authResponse.Errors
                });
            }

            // confirm registration
            return Ok();
        }


        [HttpGet]
        public async Task<IActionResult> ConfirmEmail(string userId, string token)
        {
            if (userId == null || token == null)
            {
                return null;
            }

            var user = await _userManager.FindByIdAsync(userId);

            if (user == null)
            {
                return null;
            }

            var result = await _userManager.ConfirmEmailAsync(user, token);

            if (result.Succeeded)
            {
               await _emailService.SendEmail(user.Email, "BingoApp - Successfully Registered", "Congratulations,\n You have successfully activated your account!\n " +
                    "Welcome to the dark side.");
            }

            return null;
        }

Upvotes: 2

Views: 7960

Answers (1)

Dennis VW
Dennis VW

Reputation: 3177

Your _urlHelper.Action(..) looks a bit suspicious to me.

I'm not sure you should pass the full controller name, that is, including the actual word controller.

Try _urlHelper.Action("ConfirmEmail", "Identity", instead.

As a tip: I try to avoid magic strings like these by using nameof(IdentityController) because it will return the controller name without the controller postfix.

Upvotes: 1

Related Questions