Validating spam, temporary emails and emails with invalid domains - c#

recently I have noticed a lot of users being registered to my site and they all use disposable emails, temporary email addresses or fake emails to register on the site...
What I have done to partially prevent that is I added mailgun email verification which tells me whether an email is valid or not , disposable one or not...
This filtered out like 95% of the fake emails and users, however there is still a small portion of them which go "unnoticed". What I have figured out to do are following things via code:
public static bool IsValidEmail(string email)
{
RestClient client = new RestClient();
client.BaseUrl = new Uri("https://api.mailgun.net/v3");
client.Authenticator = new HttpBasicAuthenticator("api", "");
RestRequest request = new RestRequest();
request.Resource = "/address/validate";
request.AddParameter("address", email);
var res = new JavaScriptSerializer().Deserialize<RootObject>(client.Execute(request).Content);
if (res.is_disposable_address || res.is_valid==false)
return false;
return true;
}
This is one step via mailgun that I described previously... And second method to prevent spam/fake users is that I have set up manually a table in my DB with blacklisted domains to which registration won't be allowed... And the logic for that is:
public static bool IsValid(string email)
{
using (var ctx = new myEntities())
{
var blacklistedProviders = ctx.BlacklistedDomains.ToList();
foreach (var item in blacklistedProviders)
{
if (email.ToLower().Contains(item.DomainName.ToLower()))
return false;
}
}
return true;
}
However I don't think this will prevent them from doing more malicious stuff on the website...
I have also added google recaptcha to harden the process of registration for spammers, that does the job well too....
So my questions here are:
Are there any other ways that I can validate the email to see whether it is disposable or invalid email
What other way could I implement to prevent users from creating fake accounts?
Can someone help me out with this?

Related

C# Console Application - Parsing Office 365 Inbox

I was able to succeed via a package I found called EAGetMail. Unfortunately, I realized soon after that they have a token system and this is not a free approach.
There are a couple other choices available, like using Outlook Mail REST API, and MimeKit, but I'm lost on how to achieve my end result because no "start to finish" code is available on either of these references that demonstrates how to parse an Inbox for an account.
I've started to write this with the help of Mimekit, but am not sure if this is the proper way at all.
I must imagine it looks something like:
using (var client = new SmtpClient ())
{
client.Connect("outlook.office365.com", 587);
client.Authenticate("myemail#office365account.com", "mypassword");
var message = MimeMessage.Load(stream);
}
I don't know how to setup the stream mentioned above, and I don't know if it's possible to do this with Mimekit and Office 365.
I'm open to seeing a solution for this in any other approach that's not through EAGetMail. If anyone has a lightweight solution ranging from actual establishing a connection, to pulling messages from the inbox, would be great to see!
I've got it using EWS (Exchange Web Services). Here's my code:
private static bool RedirectionUrlValidationCallback(string redirectionUrl)
{
// The default for the validation callback is to reject the URL.
bool result = false;
Uri redirectionUri = new Uri(redirectionUrl);
// Validate the contents of the redirection URL. In this simple validation
// callback, the redirection URL is considered valid if it is using HTTPS
// to encrypt the authentication credentials.
if (redirectionUri.Scheme == "https")
{
result = true;
}
return result;
}
static void Main(string[] args)
{
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013_SP1);
service.Credentials = new WebCredentials("email#myemail.com", "myPassword");
service.AutodiscoverUrl("email#myemail.com", RedirectionUrlValidationCallback);
//creates an object that will represent the desired mailbox
Mailbox mb = new Mailbox(#"email#myemail.com");
//creates a folder object that will point to inbox folder
FolderId fid = new FolderId(WellKnownFolderName.Inbox, mb);
//this will bind the mailbox you're looking for using your service instance
Folder inbox = Folder.Bind(service, fid);
//load items from mailbox inbox folder
if (inbox != null)
{
FindItemsResults<Item> items = inbox.FindItems(new ItemView(100));
foreach (var item in items)
{
item.Load();
Console.WriteLine("Subject: " + item.Subject);
}
}
}

MS Bot Builder: how to set session data to proactive message?

I first send a proactive message to the user via sms channel inside OAuthCallback method
var connector = new ConnectorClient();
Message message = new Message();
message.From = new ChannelAccount { Id = Constants.botId, Address = "+12312311", ChannelId = "sms", IsBot = true };
message.To = new ChannelAccount { Id = newUserId, Address = "+18768763", ChannelId = "sms", IsBot = false };
message.Text = $"How are you doing? ";
message.Language = "en";
connector.Messages.SendMessage(message);
IBotData myDataBag = new JObjectBotData(message);
myDataBag.UserData.SetValue("Username", "Bob");
myDataBag.PerUserInConversationData.SetValue("Newuser", "yes");
Then in my main Dialog.cs I try to access it
public static readonly IDialog<string> dialog = Chain
.PostToChain()
.Switch(new Case<Message, IDialog<string>>((msg) =>
{
var regex = new Regex("hello$", RegexOptions.IgnoreCase);
return regex.IsMatch(msg.Text);
},
(ctx, msg) =>
{
// Clearing user related data upon logout
string isnewuser = ctx.PerUserInConversationData.TryGetValue("Newuser");
string username = ctx.UserData.TryGetValue("Username");
return Chain.Return($"Welcome {username}");
}))
.Unwrap()
.PostToUser();
I receive the message on my phone. However, I am not able to get back the username and newuser session data saved inside OAuthCallback.
I suspect that this is happening because the proactive message does not have conversationId set. And the conversationId must differ somehow.
so how can I get it to set session data to my proactive message in the future conversation?
In proactive's scenarios, the conversation Id for channels change when the user answers your message, it's like a new session, we do this type of features using the channel data, but this solution is only for small data, you also have the option of creating a persistent session using the same table storage that the bot framework is using to save the dialog context, in this solution you can create another table to store your data serialized, and the final one is a persistent session using a distributed cache like Redis, but this type of services are expensive, so you have to analyze which type of solution is the right one for your solution, but as a start, you should try with the Channel Data property and if it works, you can analyze another approach
I hope I have been helpful
Not sure if this is still relevant after four years, but I think I figured this out in Access UserProfile from NotifyBot. Check it out.

Retrieve suggested contacts of Outlook

My company is under Office 365.
My goal is to retrieve Outlook's suggested contacts of the user in my asp .net MVC app (the contacts displayed in the autocomplete list).
The website is configured for automatic logon with Windows Authentication and I don't want to ask the user his credentials.
I have to tried to retrieve the suggested contacts by using Exchange Web Service but I only succeed to retrieve "real" contacts by using this code :
public List<Contact> GetContacts()
{
ContactsFolder.Bind(this._service, WellKnownFolderName.Contacts);
ItemView itemView = new ItemView(1000);
itemView.PropertySet = new PropertySet(BasePropertySet.IdOnly, new PropertyDefinitionBase[4]
{
(PropertyDefinitionBase) ContactSchema.DisplayName,
(PropertyDefinitionBase) ContactSchema.Surname,
(PropertyDefinitionBase) ContactSchema.GivenName,
(PropertyDefinitionBase) ContactSchema.EmailAddress1
});
FindItemsResults<Item> items = this._service.FindItems(WellKnownFolderName.Contacts, (ViewBase) itemView);
List<Contact> list = new List<Contact>();
foreach (Item obj in items)
{
if (obj is Contact)
list.Add(obj as Contact);
}
return list;
}
Then, I tried by using the People Api of Office 365 REST API but I don't know how to make the call without asking for the user login/password. This is a sample of a try (if I don't use the proxy, I receive an HTTP 407 Error) :
public async Task Try()
{
var proxy = WebRequest.GetSystemWebProxy();
proxy.Credentials = new NetworkCredential("foo", "1234");
// Now create a client handler which uses that proxy
HttpClient client = null;
HttpClientHandler httpClientHandler = new HttpClientHandler()
{
Proxy = proxy,
PreAuthenticate = true,
UseDefaultCredentials = false,
Credentials = new NetworkCredential("foo#foo.com", "1234")
};
var httpClient = new HttpClient(httpClientHandler);
var result = await httpClient.GetAsync("https://outlook.office.com/api/beta/me/people");
var stringContent = await result.Content.ReadAsStringAsync();
}
About the Suggested Contact problem
What I am thinking is that you are not looking in the proper folder. From what I have seen by googling the suggested contacts are not in the Contacts directory but in Suggested Contacts. In your EWS sample you are looking in Contacts...
See this discussion. Look also at this guy post, he manages to have access to the Suggested Contacts folder with EWS and powershell so there is no doubt this is feasible with C# and EWS .NET SDK. My advice is to continue trying with your sample 1.
About the Authentication problem
Let me emphasize the fact that your requests should be authorized to access both Exchange Web Services (code sample 1) or the outlook REST API (code sample 2).
In sample 1 we do not see how the _service field is instantiated but I bet there is a piece of code that looks more or less the lines below so your allowed to request EWS.
ExchangeService service = new ExchangeService();
service.Credentials = new OAuthCredentials(token);
service.Url = new Uri(ewsUrl);
The token can be probably reuse for the Outlook REST API, try to set it in the bearer in the httpClient
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "Your Oauth token");
Now your request should be authorized but you still have your proxy problem. I bet that this happen only within your organisation because your IT set up a proxy. You probably won't need it in production. You can use a debug statement to make it work while your developing locally.
#if DEBUG
IWebProxy webProxy = System.Net.WebRequest.DefaultWebProxy;
if (webProxy!=null && !webProxy.IsBypassed(new Uri(endpoint)))
{
client.Proxy = webProxy;
}
#endif
I never found the "Suggested Contacts" folder of the post.
I ended by using the folder "AllContacts" that seems to do the job.
public List<Contact> GetSuggestedContacts()
{
// Instantiate the item view with the number of items to retrieve from the Contacts folder.
ItemView view = new ItemView(1000);
// To keep the request smaller, request only the display name property.
view.PropertySet = new PropertySet(BasePropertySet.FirstClassProperties, ContactSchema.DisplayName, ContactSchema.Surname, ContactSchema.GivenName, ContactSchema.EmailAddress1);
// Retrieve the RecipientCache folder in the Contacts folder that have the properties that you selected.
var contactFolders = _service.FindFolders(new FolderId(WellKnownFolderName.Root), new FolderView(500));
var folder = contactFolders.Folders.SingleOrDefault(x => x.DisplayName == "AllContacts");
if(folder == null) return new List<Contact>();
//Cast Item in Contact and filtered only real adresses
var cacheContacts = folder.FindItems(view).Items.OfType<Contact>().Where(x => x.EmailAddresses.Contains(0) && x.EmailAddresses[0].Address != null && x.EmailAddresses[0].Address.Contains('#')).ToList();
return cacheContacts;
}
I also found the service ResolveName of Exchange that I could have use for the autocomplete.

ASP.NET Identity 2.0 Invalid Token Randomly

Sometimes users get Invalid Token when clicking on their email confirmation link. I can't figure out why, it's purely random.
Here is the code that creates the user:
IdentityResult result = manager.Create(user, "Password134567");
if (result.Succeeded)
{
var provider = new DpapiDataProtectionProvider("WebApp2015");
UserManager<User> userManager = new UserManager<User>(new UserStore<User>());
userManager.UserTokenProvider = new DataProtectorTokenProvider<User>(provider.Create(user.Id));
manager.UserTokenProvider = new DataProtectorTokenProvider<User>(provider.Create("ConfirmUser"));
var emailInfo = new Email();
string code = HttpUtility.UrlEncode(Context.GetOwinContext().GetUserManager<ApplicationUserManager>().GenerateEmailConfirmationToken(user.Id));
string callbackUrl = IdentityHelper.GetUserConfirmationRedirectUrl(code, user.Id, Request);
if (email.IndexOf("#") != -1)
{
if (assignedId == 0)
{
lblError.Text = "There was an error adding this user";
return;
}
string emailcontent = emailInfo.GetActivationEmailContent(assignedId, callbackUrl, userRole);
string subject = emailInfo.Subject;
if (string.IsNullOrEmpty(subject))
{
subject = "Your Membership";
}
Context.GetOwinContext()
.GetUserManager<ApplicationUserManager>()
.SendEmail(user.Id, subject, emailcontent);
if (user.EmailConfirmed)
{
IdentityModels.IdentityHelper.SignIn(manager, user, isPersistent: false);
IdentityHelper.RedirectToReturnUrl(Request.QueryString["ReturnUrl"], Response);
}
else
{
ErrorMessage.ForeColor = Color.Green;
ErrorMessage.Text = "An email has been sent to the user, once they verify their email they are ready to login.";
}
}
else
{
ErrorMessage.ForeColor = System.Drawing.Color.Green;
ErrorMessage.Text = "User has been created.";
}
var ra = new RoleActions();
ra.AddUserToRoll(txtEmail.Text, txtEmail.Text, userRole);
}
else
{
ErrorMessage.Text = result.Errors.FirstOrDefault();
}
Here is the confirmation page that gives the 'invalid token' error
protected void Page_Load(object sender, EventArgs e)
{
var code = IdentityHelper.GetCodeFromRequest(Request);
var userId = IdentityHelper.GetUserIdFromRequest(Request);
if (code != null && userId != null)
{
var manager = Context.GetOwinContext()
.GetUserManager<ApplicationUserManager>();
var confirmId = manager.FindById(userId);
if (confirmId != null)
{
var result = manager.ConfirmEmail(userId, HttpUtility.UrlDecode(code));
if (result.Succeeded)
{
return;
}
else
{
lblError.Text = result.Errors.FirstOrDefault();
txtNewPassword.TextMode= TextBoxMode.SingleLine;
txtNewPassword.Text = "Error contact support";
txtNewPassword2.TextMode= TextBoxMode.SingleLine;
txtNewPassword2.Text = result.Errors.FirstOrDefault();
txtNewPassword.Enabled = false;
txtNewPassword2.Enabled = false;
imageButton1.Enabled = false;
}
}
else
{
lblError.Text = "Account Does Not Exist";
imageButton1.Enabled = false;
}
}
}
Live Demo Project
I've created a pared-down demo project for you. It's hosted on GitHub here and is live on Azure here. It works as designed (see edits about Azure Websites) and uses a similar but not identical approach as you used.
It started with this tutorial, and then I removed the cruft that came with this NuGet demo code:
Install-Package -Prerelease Microsoft.AspNet.Identity.Samples
For your purposes, my demo code is more relevant than the NuGet sample is, because it focuses just on token creation and validation. In particular, take a look at these two files:
Startup.Auth.cs.
We're instantiating the IDataProtectionProvider only once per application start.
public partial class Startup
{
public static IDataProtectionProvider DataProtectionProvider
{
get;
private set;
}
public void ConfigureAuth(IAppBuilder app)
{
DataProtectionProvider =
new DpapiDataProtectionProvider("WebApp2015");
// other code removed
}
}
AccountController.cs.
Then within the AccountController, we're using the static provider instead of creating a new one.
userManager.UserTokenProvider =
new DataProtectorTokenProvider<User>(
Startup.DataProtectionProvider.Create("UserToken"));
Just doing that might remove the bug you're seeing. Here are some questions for you to consider while troubleshooting further.
Are you using two different UserTokenProvider purposes?
The DataProtectorTokenProvider.Create(string[] purposes) method takes a purposes argument. Here is what MSDN has to say about that:
purposes. Additional entropy used to ensure protected data may only be unprotected for the correct purposes.
When you create the user code, you're using (at least) two different purposes:
user.Id
"ConfirmUser" and
the purpose of the ApplicationUserManager that you retrieve with GetOwinContext()....
Here's your code as a snippet.
userManager.UserTokenProvider =
new DataProtectorTokenProvider<User>(provider.Create(user.Id));
manager.UserTokenProvider =
new DataProtectorTokenProvider<User>(provider.Create("ConfirmUser"));
string code = Context
.GetOwinContext()
.GetUserManager<ApplicationUserManager ()
.GenerateEmailConfirmationToken(user.Id)
When you validate the code, you might be using the wrong purpose. Where do you assign the UserTokenProvider for the ApplicationUserManager that you use to confirm the email? It's purposes argument must be the same!
var manager = Context.GetOwinContext()
.GetUserManager<ApplicationUserManager>();
var result = manager.ConfirmEmail(userId, HttpUtility.UrlDecode(code));
There is a strong chance that the token is invalid because you're sometimes using a different UserTokenProvider purpose for creation than you're using for validation.
Why would this be sometimes? Do a thorough search of your code to find all the places that assign to UserTokenProvider. Maybe you override it somewhere unexpected (such as in a property or in the IdentityConfig.cs file) so that it seems random.
Has the TokenLifespan expired?
You've mentioned that the Invalid Token message occurs randomly. It might be that the token has expired. This tutorial notes that the default lifespan is one day. You can change it like this:
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser>
(dataProtectionProvider.Create("WebApp2015"))
{
TokenLifespan = TimeSpan.FromHours(3000)
};
Why three UserManager instances?
Here are some comments on the code that creates the confirmation token. It seems that you're using a three separate UserManager instances including a derived ApplicationUserManager type. What's that about?
What is the type of manager here?
Why create a userManager instead of using the existing manager?
Why use manager.UserTokenProvider not userManager.UserTokenProvider?
Why are you getting a third UserManager instance from the Context?
Note that I have removed a lot of code to focus on just your token creation.
// 1.
IdentityResult result = manager.Create(user, "Password134567");
if (result.Succeeded)
{
var provider = new DpapiDataProtectionProvider("WebApp2015");
// 2.
UserManager<User> userManager =
new UserManager<User>(new UserStore<User>());
userManager.UserTokenProvider =
new DataProtectorTokenProvider<User>(provider.Create(user.Id));
// 3.
manager.UserTokenProvider =
new DataProtectorTokenProvider<User>(provider.Create("ConfirmUser"));
// 4.
string raw = Context.GetOwinContext()
.GetUserManager<ApplicationUserManager>()
.GenerateEmailConfirmationToken(user.Id)
// remaining code removed
}
I wonder whether we could simplify the above to use just one UserManager instance as follows.
// 1.
IdentityResult result = manager.Create(user, "Password134567");
if (result.Succeeded)
{
var provider = new DpapiDataProtectionProvider("WebApp2015");
manager.UserTokenProvider =
new DataProtectorTokenProvider<User>(provider.Create(user.Id));
// 3.
var provider = provider.Create("ConfirmUser");
manager.UserTokenProvider =
new DataProtectorTokenProvider<User>(provider);
// 4.
string raw = manager.GenerateEmailConfirmationToken(user.Id);
// remaining code removed
}
If you use this approach, be sure to use the same "ConfirmUser" purposes argument during confirmation of the email.
What's inside IdentityHelper?
Since the error is happening randomly, it occurs to me that the IdentityHelper methods might be doing something funky to the code that mucks up things. What's inside each of these methods?
IdentityHelper.GetUserConfirmationRedirectUrl()
IdentityHelper.RedirectToReturnUrl()
IdentityHelper.GetCodeFromRequest()
IdentityHelper.GetUserIdFromRequest()
I might write some tests to ensure that the raw code that your process creates always matches the raw code that your process retrieves from the Request. In pseudo-code:
var code01 = CreateCode();
var code02 = UrlEncode(code01);
var request = CreateTheRequest(code02);
var response = GetTheResponse();
var code03 = GetTheCode(response);
var code04 = UrlDecode(code03);
Assert.AreEquals(code01, code04);
Run the above 10,000 times to ensure that no problems exist.
Conclusion
It's my strong suspicion that the problem lies in using one purposes argument during token creation and another during confirmation. Use one purpose only and you might be fine.
Making this work on Azure Websites
Use SqlCompact instead of localdb.
Use app.GetDataProtectionProvider() not DpapiDataProtectionProvider because Dpapi does not work with web farms.
Is the site hosted on multiple web servers?
If so, you cannot use DPAPI here. It is machine-specific.
You'll need to use another data protection provider.
There might be some url-invalid characters in the code (token). Thus we need to use HttpUtility.UrlEncode(token) and HttpUtility.UrlDecode(token) when it appears in any url.
See something details here:
Identity password reset token is invalid
Whilst I think Shaun has provided great feedback to resolve such issues. There is one comment you made that makes me think it just might be an Alternate token issue. See also comments about multiple servers and Tokens.
... it's purely random
I dont think its random ;-). But what could make it work much of the time then for some users not.
A token was generated for a different page or APP, or is from the same APP and has just expired. Eg token valid for a short period. Is present in the browser. The token is from a similar app or from the same app on a different form/page.
The browser present the token since the token was generated for the Domain.
But when analyzed the token didnt match, or just expired.
Consider the lifecycle of the tokens.

Determine if mailbox exists using Exchange Web Services API

Using the Exchange Web Services API, is it possible to determine whether a mailbox/e-mail address such as someone#mydomain.com exists within an organization?
If so, which is the simplest way to do this and is it possible without the use of impersonation?
Case: A Windows Service regularly sends e-mails to people within the organization. It does not have any explicit knowledge about their e-mail adresses. It only knows their username and assumes that their e-mail address is username#mydomain.com. This is true for all users except for a few that do not have mailboxes. In these cases, it should not attempt to send the e-mail in the first place.
Solution:
As suggested by mathieu: look for user and e-mail address in Active Directory instead. This function gets the job done:
using System.DirectoryServices.AccountManagement;
// ...
public static bool TryGetUserEmailAddress(string userName, out string email)
{
using (PrincipalContext domainContext =
new PrincipalContext(ContextType.Domain, Environment.UserDomainName))
using (UserPrincipal user =
UserPrincipal.FindByIdentity(domainContext, userName))
{
if (user != null && !string.IsNullOrWhiteSpace(user.EmailAddress))
{
email = user.EmailAddress;
return true;
}
}
email = null;
return false; // user not found or no e-mail address specified
}
Determining if an user has a mailbox with EWS only could be more complicated than expected, especially without impersonation.
If you're in an Active Directory domain, you should rely on the DirectoryEntry information to determine the mailbox of an user, and send email accordingly. If you got your user login, it's really easy to get the associated DirectoryEntry.
there is an easy way to do it by checking the user availability like the following code.
I tried this and it is working for me.
I am not sure about other cases when availability result returns error but for sure when the email is not right it does
to define your exchange service refer to this: https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/get-started-with-ews-managed-api-client-applications
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2007_SP1);//You version
service.Credentials = new WebCredentials("user1#contoso.com", "password");
service.AutodiscoverUrl("user1#contoso.com", RedirectionUrlValidationCallback);
string email = "TEST#YOUR.COM";
// Get User Availability after 6 months
AttendeeInfo attendee = new AttendeeInfo(email);
var attnds = new List<AttendeeInfo>();
attnds.Add(attendee);
var freeTime = service.GetUserAvailability(attnds, new
TimeWindow(DateTime.Now.AddMonths(6), DateTime.Now.AddMonths(6).AddDays(1)), AvailabilityData.FreeBusyAndSuggestions);
//if you receive result with error then there is a big possibility that the email is not right
if(freetimes.AttendeesAvailability.OverallResult == ServiceResult.Error)
{
return false;
}
return true;

Categories

Resources