In ASP.NET Core 6 Web API, I am using MailKit for Email configuration. I am using the SMTP server of my company which doesn't need a password.
I have this:
public class MailSettings
{
public string SmtpServer { get; set; }
public string SenderName { get; set; }
public string SenderEmail { get; set; }
public int SmtpPort { get; set; }
}
Since I am using my company SMTP configuration that needs no password, I use this method to send mails:
public async Task<bool> SendEmailAsync(MailRequest mailRequest)
{
var email = new MimeMessage { Sender = MailboxAddress.Parse(_mailSettings.SenderEmail) };
email.To.Add(MailboxAddress.Parse(mailRequest.ToEmail));
email.Subject = mailRequest.Subject;
var builder = new BodyBuilder();
if (mailRequest.Attachments != null)
{
foreach (var file in mailRequest.Attachments.Where(file => file.Length > 0))
{
byte[] fileBytes;
await using (var ms = new MemoryStream())
{
file.CopyTo(ms);
fileBytes = ms.ToArray();
}
builder.Attachments.Add((file.FileName + Guid.NewGuid().ToString()), fileBytes, ContentType.Parse(file.ContentType));
}
}
builder.HtmlBody = mailRequest.Body;
email.Body = builder.ToMessageBody();
try
{
using var smtp = new SmtpClient();
smtp.Connect(_mailSettings.SmtpServer, _mailSettings.SmtpPort, SecureSocketOptions.StartTls);
smtp.Authenticate(_mailSettings.SenderEmail);
await smtp.SendAsync(email);
smtp.Disconnect(true);
return true;
}
catch (Exception e)
{
_logger.Error(e, e.Source, e.InnerException, e.Message, e.ToString());
return false;
}
}
I got this error:
Argument 1: cannot convert from 'string' to 'MailKit.Security.SaslMechanism'
and it highlights this line of code:
smtp.Authenticate(_mailSettings.SenderEmail);
Expecting me to do it this way:
smtp.Authenticate(_mailSettings.SenderEmail, _mailSettings.Password);
How do I resolve this without password?
Thanks
I try to implement a PayPal cart payment in ASP.NET Core. I have a working example in ASP.NET MVC 5 and I try to convert it to ASP.NET Core but I had no success. The point that I can not resolve is how to get the values that I have to get the transactionID, amount paid and Order ID. In ASP.NET MVC 5 the IPN action is as follows:
public ActionResult IPN()
{
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var formVals = new Dictionary<string, string>();
formVals.Add("cmd", "_notify-validate");
string response = GetPayPalResponse(formVals, true);
if (response == "VERIFIED")
{
string transactionID = Request["txn_id"];
string sAmountPaid = Request["mc_gross"];
string orderID = Request["custom"];
:
:
In my ASP.NET Core application the IPN action is executed by PayPal and I have a VERIFIED response but I can not get the next three values. I have tried various ways to get these values without success.
My initial approach was the following:
string transactionID = Request.Query["txn_id"];
string sAmountPaid = Request.Query["mc_gross"];
string orderID = Request.Query["custom"];
Can someone suggest me a way to get these values?
I found a solution to my problem and I will post it just in case someone wants to do something similar.
[Route("PayPal/IPN")]
[HttpPost]
public ActionResult IPN()
{
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
PayPalRespond response = GetPayPalResponse();
if (response.RespondType == RespondTypeEnum.Verified)
{
System.IO.File.AppendAllText(_env.WebRootPath + Path.DirectorySeparatorChar.ToString() + "data.txt", $"{DateTime.Now.ToString()} {response.JsonData}." + Environment.NewLine);
Order order = GetOrder(154);
//check the amount paid
if (order.Total <= response.AmountPaid)
{
// IPN Order successfully transacted. Save changes to database
return Ok();
}
else
{
// Amount Paid is incorrect
}
}
else
{
// Not verified
}
return Content("");
}
PayPalRespond GetPayPalResponse()
{
PayPalRespond output = new PayPalRespond();
var formVals = new Dictionary<string, string>();
formVals.Add("cmd", "_notify-validate");
string paypalUrl = UseSandbox ? "https://www.sandbox.paypal.com/cgi-bin/webscr" : "https://www.paypal.com/cgi-bin/webscr";
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(paypalUrl);
// Set values for the request back
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
byte[] param;
using (var ms = new MemoryStream(2048))
{
Request.Body.CopyTo(ms);
param = ms.ToArray();
}
string strRequest = Encoding.ASCII.GetString(param);
var QueryValues = System.Web.HttpUtility.ParseQueryString(strRequest);
output.Data = new List<QueryValue>();
foreach (var item in QueryValues.AllKeys)
{
if (item.Equals("txn_id"))
output.TransactionID = QueryValues[item];
else if (item.Equals("mc_gross"))
{
CultureInfo culture = CultureInfo.CreateSpecificCulture("en-US");
NumberStyles style = NumberStyles.Number;
Decimal amountPaid = 0;
Decimal.TryParse(QueryValues[item], style, culture, out amountPaid);
output.AmountPaid = amountPaid;
}
else if (item.Equals("custom"))
output.OrderID = QueryValues[item];
output.Data.Add(new QueryValue { Name = item, Value = QueryValues[item] });
}
output.JsonData = Newtonsoft.Json.JsonConvert.SerializeObject(output.Data);
StringBuilder sb = new StringBuilder();
sb.Append(strRequest);
foreach (string key in formVals.Keys)
{
sb.AppendFormat("&{0}={1}", key, formVals[key]);
}
strRequest += sb.ToString();
req.ContentLength = strRequest.Length;
//Send the request to PayPal and get the response
string response = "";
using (StreamWriter streamOut = new StreamWriter(req.GetRequestStream(), System.Text.Encoding.ASCII))
{
streamOut.Write(strRequest);
streamOut.Close();
using (StreamReader streamIn = new StreamReader(req.GetResponse().GetResponseStream()))
{
response = streamIn.ReadToEnd();
}
}
output.RespondType = response.Equals("VERIFIED") ? RespondTypeEnum.Verified : RespondTypeEnum.Invalid;
return output;
}
The enumerator and the classes that you will need are the following:
public enum RespondTypeEnum { Verified, Invalid }
public class PayPalRespond
{
public RespondTypeEnum RespondType { get; set; }
public List<QueryValue> Data { get; set; }
public string JsonData { get; set; }
public string TransactionID { get; set; }
public string OrderID { get; set; }
public Decimal AmountPaid { get; set; }
}
public class QueryValue
{
public string Name { get; set; }
public string Value { get; set; }
}
I have a window service service1. service1 is running a process.I have called this window service inside a webservice. But it will not allow to start the process while calling the service1 from webservice.
Please find the code below.
Service1
namespace ContinuousIntegration
{
[ServiceContract]
public interface IContIntegService
{
[OperationContract]
string Execute(string Component);
}
// Use a data contract as illustrated in the sample below to add composite types to service operations.
// You can add XSD files into the project. After building the project, you can directly use the data types defined there, with the namespace "ContinuousIntegration.ContractType".
[DataContract]
public class CompositeType
{
bool boolValue = true;
string stringValue = "Hello ";
[DataMember]
public bool BoolValue
{
get { return boolValue; }
set { boolValue = value; }
}
[DataMember]
public string StringValue
{
get { return stringValue; }
set { stringValue = value; }
}
}
}
public class ContIntegService : IContIntegService
{
public string Execute(string component)
{
if(component == "Verification")
{
string exeFileName = string.Empty;
string workingDirectory = string.Empty;
string ServerArguments = string.Empty;
string moderatorPath = string.Empty;
string output = string.Empty;
workingDirectory = Path.GetDirectoryName(moderatorPath); ;
exeFileName = Path.GetFileName(moderatorPath);
ServerArguments = string.Concat("-c", " ", "TC", " ", "-r", " ", reportpath);
P = new Process();
try
{
if (P != null)
{
P.StartInfo.WorkingDirectory = workingDirectory;
P.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Normal;
P.StartInfo.FileName = exeFileName;
P.StartInfo.Arguments = ServerArguments;
P.StartInfo.CreateNoWindow = true;
runStatus = P.Start();
P.WaitForExit(5000);
while (!File.Exists(reportpath + "\\" + "TestCaseAll.html"))
{
Thread.Sleep(1000);
}
output = "Completed";
}
}
catch (Exception ex)
{
string msg = ex.Message;
runStatus = false;
}
runStatus = true;
return output;
}
}
}
WebService
public string ExecuteWindowService(string component)
{
string result = string.Empty;
ServiceReference1.ContIntegServiceClient myService = new ServiceReference1.ContIntegServiceClient("NetTcpBinding_IContIntegService");
myService.Endpoint.Binding.SendTimeout = TimeSpan.MaxValue;
myService.Endpoint.Binding.ReceiveTimeout = TimeSpan.MaxValue;
myService.Endpoint.Binding.OpenTimeout = TimeSpan.MaxValue;
myService.Endpoint.Binding.CloseTimeout = TimeSpan.MaxValue;
result = myService.Execute(component);
myService.Close();
return result;
}
Since Wcf REST do not support Out parameter .Then how can I get the value from the method that is retuning the value.
[OperationContract(IsOneWay = false)]
[WebInvoke
(Method = "GET",
ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped,
UriTemplate = "xml/getcommentsforvideopage?vid={videoID}&psize={pageSize}&pnum={pageNumber}")]
IEnumerable<DTOUserComment> GetCommentsForVideoPage(
// out int resultCount,
int videoID, string pageSize,
string pageNumber);
Implemented as this below------------------
public IEnumerable<DTOUserComment> GetCommentsForVideoPage(
int videoID, string pageSize,
string pageNumber)
{
int? pageSizeInt = pageSize.ParseNullableInt();
int? pageNumberInt = pageNumber.ParseNullableInt();
IEnumerable<DTOUserComment> results = null;
// TODO
int resultCount;
try
{
results = searcher.GetCommentsForVideoPage(**out resultCount,** videoID, pageSizeInt, pageNumberInt);
}
catch (UnauthorizedAccessException ex)
{
Logger.Write(ex);
}
catch (Exception ex)
{
Logger.Write(ex);
}
return results;
}
Client Side I am consuming using HttpClient--
public IEnumerable<UserComment> CommentsPage(int videoId, int startRowIndex, int maximumRows)
{
int pageSize = maximumRows;
if (maximumRows < 1) { maximumRows = 1; }
int startingPage = (startRowIndex / maximumRows) + 1;
using (var client = new HttpClient())
{
HttpResponseMessage response = client.GetAsync("http://localhost:25252/MyRESTService.svc/xml/getcommentsforvideopage?vid=" + videoId + "&psize=" + pageSize + "&pnum=" + startingPage).Result;
string strJson = response.Content.ReadAsStringAsync().Result;
JsonSerializerSettings settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
var data = JObject.Parse(strJson)["GetCommentsForVideoPageResult"];//JSON array starts with "GetCommentsForVideoPageResult"
IEnumerable<UserComment> comments = JsonConvert.DeserializeObject<IEnumerable<UserComment>>(data.ToString(),settings);
return comments;
}
}
Do these things.
1.Create a wrapper class like this
public class WrappedDTOUserComment
{
public IEnumerable<DTOUserComment> Comments { get; set; }
public int CommentCount { get; set; }
}
2.Then change the method like this
public WrappedDTOUserComment GetCommentsForVideoPage(
int videoID, string pageSize,
string pageNumber)
{
int? pageSizeInt = pageSize.ParseNullableInt();
int? pageNumberInt = pageNumber.ParseNullableInt();
WrappedDTOUserComment data = null;
IEnumerable<DTOUserComment> results = null;
// TODO
int resultCount;
try
{
results = searcher.GetCommentsForVideoPage(**out resultCount,** videoID, pageSizeInt, pageNumberInt);
}
catch (UnauthorizedAccessException ex)
{
Logger.Write(ex);
}
catch (Exception ex)
{
Logger.Write(ex);
}
data.Comments = results;
data.CommentCount = resultCount;
return data;
}
3.The interface will be
[OperationContract(IsOneWay = false)]
[WebInvoke
(Method = "GET",
ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped,
UriTemplate = "xml/getcommentsforvideopage?vid={videoID}&psize={pageSize}&pnum={pageNumber}")]
WrappedDTOUserComment GetCommentsForVideoPage(
// out int resultCount,
int videoID, string pageSize,
string pageNumber);
How can I get the email from microsoft account? I'm doing the following:
public ActionResult ExternalLoginCallback(string returnUrl)
{
AuthenticationResult result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
//...
string email = null;
if (result.Provider.ToLower() == "google")
{
email = result.ExtraData["email"];
}
else if (result.Provider.ToLower() == "facebook")
{
email = result.ExtraData["username"];
}
else if (result.Provider.ToLower() == "microsoft")
{
email = result.ExtraData["????"];
}
}
For google and facebook I'm able to get the email but I can't with microsoft? What kew should I use?
Solution:
public class MicrosoftScopedClient : IAuthenticationClient
{
private string clientId;
private string clientSecret;
private string scope;
private const string baseUrl = "https://login.live.com/oauth20_authorize.srf";
private const string tokenUrl = "https://login.live.com/oauth20_token.srf";
public MicrosoftScopedClient(string clientId, string clientSecret, string scope)
{
this.clientId = clientId;
this.clientSecret = clientSecret;
this.scope = scope;
}
public string ProviderName
{
get { return "Microsoft"; }
}
public void RequestAuthentication(HttpContextBase context, Uri returnUrl)
{
string url = baseUrl + "?client_id=" + clientId + "&redirect_uri=" + HttpUtility.UrlEncode(returnUrl.ToString()) + "&scope=" + HttpUtility.UrlEncode(scope) + "&response_type=code";
context.Response.Redirect(url);
}
public AuthenticationResult VerifyAuthentication(HttpContextBase context)
{
string code = context.Request.QueryString["code"];
string rawUrl = context.Request.Url.ToString();
//From this we need to remove code portion
rawUrl = Regex.Replace(rawUrl, "&code=[^&]*", "");
IDictionary<string, string> userData = GetUserData(code, rawUrl);
if (userData == null)
return new AuthenticationResult(false, ProviderName, null, null, null);
string id = userData["id"];
string username = userData["email"];
userData.Remove("id");
userData.Remove("email");
AuthenticationResult result = new AuthenticationResult(true, ProviderName, id, username, userData);
return result;
}
private IDictionary<string, string> GetUserData(string accessCode, string redirectURI)
{
string token = QueryAccessToken(redirectURI, accessCode);
if (token == null || token == "")
{
return null;
}
var userData = GetUserData(token);
return userData;
}
private IDictionary<string, string> GetUserData(string accessToken)
{
ExtendedMicrosoftClientUserData graph;
var request =
WebRequest.Create(
"https://apis.live.net/v5.0/me?access_token=" + EscapeUriDataStringRfc3986(accessToken));
using (var response = request.GetResponse())
{
using (var responseStream = response.GetResponseStream())
{
using (StreamReader sr = new StreamReader(responseStream))
{
string data = sr.ReadToEnd();
graph = JsonConvert.DeserializeObject<ExtendedMicrosoftClientUserData>(data);
}
}
}
var userData = new Dictionary<string, string>();
userData.Add("id", graph.Id);
userData.Add("username", graph.Name);
userData.Add("name", graph.Name);
userData.Add("link", graph.Link == null ? null : graph.Link.AbsoluteUri);
userData.Add("gender", graph.Gender);
userData.Add("firstname", graph.FirstName);
userData.Add("lastname", graph.LastName);
userData.Add("email", graph.Emails.Preferred);
return userData;
}
private string QueryAccessToken(string returnUrl, string authorizationCode)
{
var entity =
CreateQueryString(
new Dictionary<string, string> {
{ "client_id", this.clientId },
{ "redirect_uri", returnUrl },
{ "client_secret", this.clientSecret},
{ "code", authorizationCode },
{ "grant_type", "authorization_code" },
});
WebRequest tokenRequest = WebRequest.Create(tokenUrl);
tokenRequest.ContentType = "application/x-www-form-urlencoded";
tokenRequest.ContentLength = entity.Length;
tokenRequest.Method = "POST";
using (Stream requestStream = tokenRequest.GetRequestStream())
{
var writer = new StreamWriter(requestStream);
writer.Write(entity);
writer.Flush();
}
HttpWebResponse tokenResponse = (HttpWebResponse)tokenRequest.GetResponse();
if (tokenResponse.StatusCode == HttpStatusCode.OK)
{
using (Stream responseStream = tokenResponse.GetResponseStream())
{
using (StreamReader sr = new StreamReader(responseStream))
{
string data = sr.ReadToEnd();
var tokenData = JsonConvert.DeserializeObject<OAuth2AccessTokenData>(data);
if (tokenData != null)
{
return tokenData.AccessToken;
}
}
}
}
return null;
}
private static readonly string[] UriRfc3986CharsToEscape = new[] { "!", "*", "'", "(", ")" };
private static string EscapeUriDataStringRfc3986(string value)
{
StringBuilder escaped = new StringBuilder(Uri.EscapeDataString(value));
// Upgrade the escaping to RFC 3986, if necessary.
for (int i = 0; i < UriRfc3986CharsToEscape.Length; i++)
{
escaped.Replace(UriRfc3986CharsToEscape[i], Uri.HexEscape(UriRfc3986CharsToEscape[i][0]));
}
// Return the fully-RFC3986-escaped string.
return escaped.ToString();
}
private static string CreateQueryString(IEnumerable<KeyValuePair<string, string>> args)
{
if (!args.Any())
{
return string.Empty;
}
StringBuilder sb = new StringBuilder(args.Count() * 10);
foreach (var p in args)
{
sb.Append(EscapeUriDataStringRfc3986(p.Key));
sb.Append('=');
sb.Append(EscapeUriDataStringRfc3986(p.Value));
sb.Append('&');
}
sb.Length--; // remove trailing &
return sb.ToString();
}
protected class ExtendedMicrosoftClientUserData
{
public string FirstName { get; set; }
public string Gender { get; set; }
public string Id { get; set; }
public string LastName { get; set; }
public Uri Link { get; set; }
public string Name { get; set; }
public Emails Emails { get; set; }
}
protected class Emails
{
public string Preferred { get; set; }
public string Account { get; set; }
public string Personal { get; set; }
public string Business { get; set; }
}
}
AuthConfig.cs
public static class AuthConfig
{
public static void RegisterAuth()
{
Dictionary<string, object> MicrosoftsocialData = new Dictionary<string, object>();
MicrosoftsocialData.Add("Icon", "../Content/icons/microsoft.png");
OAuthWebSecurity.RegisterClient(new MicrosoftScopedClient("XXXXXXXX", "YYYYYYYYYYYYY",
"wl.basic wl.emails"), "Microsoft", MicrosoftsocialData);
//......
}
}
Usage:
public ActionResult ExternalLoginCallback(string returnUrl)
{
AuthenticationResult result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
//...
string email = null;
if (result.Provider.ToLower() == "google")
{
email = result.ExtraData["email"];
}
else if (result.Provider.ToLower() == "facebook")
{
email = result.ExtraData["username"];
}
else if (result.Provider.ToLower() == "microsoft")
{
email = result.UserName;
}
}
Based on: How OAuthWebSecurity to obtain emails for different oauth clients, but Microsoft Client doesn’t return email, it didn’t include scope “wl.emails”
or even simpler: https://stackoverflow.com/a/22723713/1586498
var mo =
new Microsoft.Owin.Security.MicrosoftAccount.MicrosoftAccountAuthenticationOptions
{
CallbackPath = new Microsoft.Owin.PathString("/Callbacks/External"),//register at oAuth provider
ClientId = "<<yourclientid>>",
ClientSecret = "<<yourclientsecret>>",
Provider = new Microsoft.Owin.Security.MicrosoftAccount.MicrosoftAccountAuthenticationProvider
{
OnAuthenticated = (context) =>
{
context.Identity.AddClaim(new Claim(providerKey, context.Identity.AuthenticationType));
context.Identity.AddClaim(new Claim(ClaimTypes.Name, context.Identity.FindFirstValue(ClaimTypes.Name)));
return System.Threading.Tasks.Task.FromResult(0);
}
}
};
mo.Scope.Add("wl.basic");
mo.Scope.Add("wl.emails"); //HERE IS THE GOLD
app.UseMicrosoftAccountAuthentication(mo);
and my way of grabbing them:
var externalIdentity = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
externalIdentity.Claims.FirstOrDefault(c => c.Type.Equals(ClaimTypes.Email));
amp's answer really helped me out.
Also want to mention that you have to check the 'Live SDK support' checkbox when you register your application (https://apps.dev.microsoft.com/) - otherwise the OAuth service complains that you don't have a client secret (even if you do).
Just wanted to add how to do this without using the AuthConfig.cs stuff in case anyone is interested (a bit more manual, but it makes it easier to understand if you're not familiar with the framework):
public ActionResult LoginWithMicrosoftAccount(CancellationToken cancellationToken)
{
var client = new MicrosoftScopedClient(appID, appsecret, "wl.basic wl.emails");
var urlNoQueryString = Request.Url.GetLeftPart(UriPartial.Path);
AuthenticationResult result = null;
if(Request.QueryString["error"]!= null)
{//Microsoft service returns error
return View();
}
if (Request.QueryString["code"] != null)
{
result = client.VerifyAuthentication(this.HttpContext);
//at this point, you should get the username from result.UserName
}
if(Request.QueryString["code"]==null || result.UserName == null)
{//will do the redirection
client.RequestAuthentication(this.HttpContext, new Uri(urlNoQueryString));
}
return View();
}