So i'm trying to authenticate my email in .Net 6 core application but without front end (without cshtml), only by sending email,clicking the link and changing the EmailConfirmed box on the user from false to true.
However i have some problem with generating link form Url.Action which i use to send email with the link
var confirmationLink = Request.Scheme + "://" + Request.Host + Url.Action("ConfirmEmail", "Register",new { code = code,userId = userToAdd.Id });
Console.WriteLine(confirmationLink);
When i do this i get this output
and nothing else.
This is my ConfirmEmail action which the email should redirect to but when i do this i get 404 and no action from this controller is triggered.
[HttpPost]
[Route("ConfirmEmail/{token}/{userId}")]
public async Task<IActionResult> EmailAuthentication([FromRoute]string token, [FromRoute]string userId) {
var user = await _userManager.FindByIdAsync(userId);
Console.WriteLine(user.Email);
Console.WriteLine(user.UserName);
Console.WriteLine(token);
Console.WriteLine(user.EmailConfirmationToken);
Console.WriteLine(token == user.EmailConfirmationToken);
if(user == null)
{
return NotFound();
}
if (token == user.EmailConfirmationToken)
{
user.EmailConfirmed = true;
return Ok();
}
return BadRequest();
}
And this is the email sender:
public async Task SendEmailAsync(string toEmail, string subject, string message)
{
if (string.IsNullOrEmpty(Options.SendGridKey))
{
throw new Exception("Null SendGridKey");
}
await Execute(Options.SendGridKey, subject, message, toEmail);
}
public async Task Execute(string apiKey, string subject, string message, string toEmail)
{
var client = new SendGridClient(apiKey);
var emailPass = _config["emailPassword"];
var msg = new SendGridMessage()
{
From = new EmailAddress("employmentlyy#gmail.com", "wlqegxqlkepwrguh"),
Subject = subject,
PlainTextContent = message,
HtmlContent = message
};
msg.AddTo(new EmailAddress(toEmail));
// Disable click tracking.
// See https://sendgrid.com/docs/User_Guide/Settings/tracking.html
msg.SetClickTracking(false, false);
var response = await client.SendEmailAsync(msg);
_logger.LogInformation(response.IsSuccessStatusCode
? $"Email to {toEmail} queued successfully!"
: $"Failure Email to {toEmail}");
}
}
Related
I want to make an email confirmation callback link from asp.net core web API repository class,
This is easy when done in the controller of the API, but I am having trouble doing this from the repository class.
I keep getting this error:
Could not find an IRouter associated with the ActionContext. If your application is using endpoint routing then you can get a IUrlHelperFactory with dependency injection and use it to create a UrlHelper, or use Microsoft.AspNetCore.Routing.LinkGenerator.
this is what I have so far:
this is my repository code for registration:
public async Task<(bool IsSuccess, string ErrorMessage)> Register(RegisterDTO model)
{
if (model != null)
{
var user = _mapper.Map<ApplicationUser>(model);
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await _userManager.AddToRoleAsync(user, "Admin");
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
string _scheme = _urlHelper.ActionContext.HttpContext.Request.Scheme;
//var callbackurl = _urlHelper.Link("ConfirmEmail", new { email =
user.Email, code = code });
var callbackurl =
_urlHelper.Action("ConfirmEmail",nameof(AccountController), new { email = user.Email, code = code }, _scheme);
var mailresult = _mailSender.SendEmail(model.Email, "Please confirm your
account by clicking here: link");
return (true, "Account created successfully");
}
return (false, "Eror occured while creating your account");
}
return (false, "Please provide the user data");
}
Account Controller
[HttpGet("{token}/{email}")]
public async Task<IActionResult> ConfirmEmail(string token, string email)
{
var result = await _repository.ConfirmEmail(token, email);
if (result.IsSuccess)
return Ok(result.ErrorMessage);
else
return BadRequest(result.ErrorMessage);
}
I managed to get it to work.
see the code below:
enter code here
public async Task<(bool IsSuccess, string ErrorMessage)>
Register(RegisterDTO model)
{
if (model != null)
{
var user = _mapper.Map<ApplicationUser>(model);
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await _userManager.AddToRoleAsync(user, "Admin");
var _urlHelper = _urlHelperFactory.GetUrlHelper(
_actionContextAccessor.ActionContext);
var code = await _userManager.
GenerateEmailConfirmationTokenAsync(user);
string _scheme = _urlHelper.ActionContext.
HttpContext.Request.Scheme;
var callbackurl = _urlHelper.Action(action:
nameof(AccountController.ConfirmEmail),
controller: "Account", values:
new { email = user.Email, code = code },
protocol: _scheme);
var mailresult = _mailSender.SendEmail(model.Email, "Please
confirm your account by clicking here: <a href=\"" +
callbackurl + "\">link</a>");
if (mailresult.IsSuccess)
{
return (true, "Account created successfully, Please visit
your email to confirm your email");
}
else
{
return (true, $"Account created successfully,
{mailresult.ErrorMessage}");
}
}
return (false, result.Errors.ToString());
}
return (false, "Please provide the user data");
}
I am developing telegram notifications in my existing asp.net core 3.1 project. I have written below code in controller.
#region Telegram
TelegramBotClient _botService;
private const string token = "332435:45345345345dflskdfjksdjskdjflkdd";
[HttpPost("Update")]
[AllowAnonymous]
public async Task<IActionResult> Update([FromBody] Update update)
{
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
if (_botService == null)
_botService = new TelegramBotClient(token);
if (update.Type != UpdateType.Message)
return Ok(new Response
{
code = (int)HttpStatusCode.OK,
status = "Ok",
message = "Success"
});
var message = update.Message;
try
{
_logger.LogInformation("Received Message from {0}", message.Chat.Id);
switch (message.Type)
{
case MessageType.Text:
if (message.Text.Contains("/Reset"))
{
//Delete(string chatid)
var response = _UserRepository.DeleteTeleBotChatID(message.Chat.Id);
if (response)
await _botService.SendTextMessageAsync(message.Chat.Id, "You have successfully unsubscribed.");
else
await _botService.SendTextMessageAsync(message.Chat.Id, "You are not registered yet.");
}
else
if (message.Text.Contains("/") && !message.Text.ToLower().Contains("/start"))
{
var user = Crypto.decrypt(Encoding.UTF8.GetString(Convert.FromBase64String(message.Text.Split('/').Last())));
var response = _UserRepository.UpdateTeleBotChatIDByUser(new TeleBotModel() { ChatId = message.Chat.Id, Username = user });
if (response)
await _botService.SendTextMessageAsync(message.Chat.Id, $"You have successfully subscribe notifications for {user}.");
else
await _botService.SendTextMessageAsync(message.Chat.Id, "Username is not valid");
// var chat=modifyus(string username,chatid)
}
else
{
await _botService.SendTextMessageAsync(message.Chat.Id, "Enter your encrypted username.\n Type /Reset to unsubscribe.");
}
break;
case MessageType.Photo:
// Download Photo
var fileId = message.Photo.LastOrDefault()?.FileId;
var file = await _botService.GetFileAsync(fileId);
var filename = file.FileId + "." + file.FilePath.Split('.').Last();
using (var saveImageStream = System.IO.File.Open(filename, FileMode.Create))
{
await _botService.DownloadFileAsync(file.FilePath, saveImageStream);
}
await _botService.SendTextMessageAsync(message.Chat.Id, "Thx for the Pics");
break;
}
}
catch (Exception exp)
{
//LoggerSimple.Error(exp);
await _botService.SendTextMessageAsync(message.Chat.Id, "Wrong Bot command");
}
return Ok(new Response
{
code = (int)HttpStatusCode.OK,
status = "Ok",
message = "Success"
});
}
[HttpPost]
public async Task<IActionResult> sendTeleMsg(TelegramMessgae Data)
{
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
if (_botService == null)
_botService = new TelegramBotClient(token);
//check username exist
long ChatId = _UserRepository.GetChatIdByUsername(Data.Username);
if (ChatId == -1)
{
return Ok(new Response
{
error = "true",
code = HttpStatusCode.BadRequest,
status = HttpStatus.OK,
message = "Not registered with telegram bot"
});
}
try
{
await _botService.SendTextMessageAsync(ChatId, string.Format("*{0}*\n{1}", parseMText(Data.Subject), parseMText(Data.Message)), ParseMode.Markdown);
return Ok(new Response
{
code = HttpStatusCode.OK,
status = HttpStatus.OK,
message = "Message Sent"
});
}
catch (Exception exp)
{
//if wrong chatid
_UserRepository.DeleteTeleBotChatID(ChatId);
return Ok(new Response
{
error = "true",
code = HttpStatusCode.BadRequest,
status = HttpStatus.OK,
message = exp.Message
});
}
}
private string parseMText(string txt)
{
var vs = new string[] { "*", "_", "`", "[", "]" };
foreach (var item in vs)
{
txt = txt.Replace(item, "\\" + item);
}
return txt;
}
#endregion
then used ngrok for tunnelling and exposed localhost so that I can connect with telegram bot. After creating and subscribing the bot, I am able to receive a breakpoint in above Update method but data was nothing. I sent messages on bot but always there is no data in update object. See below screenshot.
I am unable to figure-out the issue in the code. Can anyone pls help?
Calling AddNewtonsoftJson() function in starup.cs file fixed the issue.
services.AddControllers().AddNewtonsoftJson();
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;
}
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.
I would like the following Post method to return a failure by assigning a failure value to the "result" variable but I am not sure how to achieve that. Ideally it would say that the installation id is invalid, but not sure I could do that:
[Authorize]
[HttpPost, Route("sendForDevelopment")]
public async Task<NotificationOutcome> Post([FromBody]string message, string installationId)
{
string hubName = "myHubName";
string hubNameDefaultShared = "myHubNameDefaultShared";
NotificationHubClient hub = NotificationHubClient
.CreateClientFromConnectionString(hubNameDefaultShared, hubName, enableTestSend: true);
var templateParams = new Dictionary<string, string>
{
["messageParam"] = message
};
NotificationOutcome result = null;
if (string.IsNullOrWhiteSpace(installationId))
{
// output a installation id is null or empty message or assign failure to the result variable
}
else
{
result = await hub.SendTemplateNotificationAsync(templateParams, "$InstallationId:{" + installationId + "}").ConfigureAwait(false);
}
return result;
}
Have the result of the action be a IHttpActionResult derived object.
That should allow greater flexibility as to what you can return when the request is not valid
For example
[Authorize]
[HttpPost, Route("sendForDevelopment")]
public async Task<IHttpActionResult> Post([FromBody]string message, string installationId) {
if (string.IsNullOrWhiteSpace(installationId)) {
var model = new {
error = new {
code = 400,
message = "installation id is null or empty"
}
}
return Content(HttpStatusCode.Badrequest, model); //400 Bad Request with error message
}
string hubName = "myHubName";
string hubNameDefaultShared = "myHubNameDefaultShared";
var hub = NotificationHubClient
.CreateClientFromConnectionString(hubNameDefaultShared, hubName, enableTestSend: true);
var templateParams = new Dictionary<string, string>
{
["messageParam"] = message
};
NotificationOutcome result = await hub.SendTemplateNotificationAsync(templateParams, "$InstallationId:{" + installationId + "}").ConfigureAwait(false);
return Ok(result); //200 OK with model result
}
For a bad request the response body would look something like
{
"error": {
"code": 400,
"message": "installation id is null or empty"
}
}
On the client side you check the status code of the response and proceed accordingly.
var response = await client.PostAsync(url, content);
if(response.IsSuccessStatusCode)
var result = await response.Content.ReadAsAsync<NotificationOutcomeResult>();
//...
else {
//...check why request failed.
var model = await response.Content.ReadAsAsync<ErrorResponse>();
var message = model.error.message;
//...
}
//...
[HttpPost]
public ActionResult Register(User user)
{
if (ModelState.IsValid)
{
UserAPIController uApi = new UserAPIController(true);
HttpResponseMessage response = uApi.Register(user, Request.QueryString["TeamId"]);
if (response.StatusCode == System.Net.HttpStatusCode.Conflict)
{
ModelState.AddModelError("", HttpContext.GetGlobalResourceObject("LanguageResource", "DuplicateEmailErrorMessage").ToString());
return View();
}
//Send Registration Email
string EmailBodyStr = string.Empty;
string EmailFrom = Helpers.CommonFunctions.GetApplicationSettingValue("SystemEmailId");
string EmailSub = HttpContext.GetGlobalResourceObject("LanguageResource", "EmailTemplate_Reistration_Subject").ToString();
string userName = user.FullName;
if (string.IsNullOrEmpty(userName))
{
userName = user.Email;
}
EmailBodyStr = HttpContext.GetGlobalResourceObject("LanguageResource", "EmailTemplate_Registration_TeamLeader_Body").ToString();
EmailBodyStr = EmailBodyStr.Replace("[UserFullName]", userName);
string email = HttpUtility.UrlEncode(Helpers.Encrypt.EncryptString(user.Email));
EmailBodyStr = EmailBodyStr.Replace("[NavigationLink]", "click here");
if (EmailFrom != string.Empty)
{
Helpers.Emailer.SendEmail(user.Email, EmailSub, EmailBodyStr, EmailFrom);
}
ModelState.AddModelError("", HttpContext.GetGlobalResourceObject("LanguageResource", "SuccessfullRegistrationMessage").ToString());
}
return View(user);
}
I have created a registration form in mvc4 in which the user get confirmation email once it get registered but its sending the same registration email two times. Above is the code which is used for registration confirmation.
Please let me know where is the problem in code and why its triggering same event two times.