Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Email Address as UserName BUG

I'm developing a MVC4 Website with a SQL Server database. I want to register people with their email address.

However if email address contains the character i, WebSecurity.CreateUserAndAccount method throws an exception says :

The authentication provider returned an error. Please verify your entry and try again. If the problem persists, please contact your system administrator.

I researched a lot and find a few posts about it but there is no solution.

http://forums.asp.net/t/1862233.aspx/1?How+can+i+use+email+address+for+username+in+MVC4+Membership

http://aspnetwebstack.codeplex.com/workitem/714

like image 295
ergunysr Avatar asked Dec 22 '12 17:12

ergunysr


1 Answers

Your fundamental problem is that you are trying to input non-ASCII characters as an e-mail address.

The "ASCII character set" limits you to the US-English alphabet, which happens to be the latin alphabet.

I don't speak turkish, but as pointed out in http://aspnetwebstack.codeplex.com/workitem/714

In Turkish, 'i' is lowercase, while 'İ' is upper of "i".
In the latin alphabet, which you (and me) happen to not have IT-wise, 'i' is lowercase, while 'I' is uppercase of i.

So you have a character which is not a member of the EN-US ASCII character set.
Therefore, the collation setting prevents you from inputting non-ASCII characters into your database, hence the error.
And hence the comment that you shall NOT change the collation setting (which would allow for invalid mail addresses).

As pointed out, ToUpper (which should be ToUpperInvariant) is to blame, because it behind the scenes changes the 'i' to 'İ', which is not a valid ASCII character.

This is a common problem with the ToUpper method of a string/character.
For example the German alphabet contains the letter ß (Unicode U+00DF), also known as "double s", which has no corresponding uppercase character in the first place, therefore if you try to compare strings using toUpper, it will always fail, which is why you should always use ToLower() to compare strings - another Microsoft fail.

A similar thing happens here with ToUpper.

What you need to do in the first place is to ensure your users input ASCII characters.
This is not possible since they have a turkish keyboard and locale, and while the small i will look similar to the ASCII small i, it has a different numerical representation, and therefore a different uppercase character (and lowercase as well btw).

So what you need to do is to "latinize/romanize" your input string, prior to calling the membership method.

I had similar problems with ASCII-only capable software from a third party, which failed on umlauts and French accent characters, and used the below method to resolve this.
You might want to check if the Turkish äöü doesn't have a different numerical representation than the (Swiss) German äöü that I used here.

For risks and side-effects, read the package leaflets and ask your doctor or pharmacist.

    // string str = ApertureSucks.Latinize("(æøå âôû?aè");
    public static string Latinize(string stIn)
    {
        // Special treatment for German Umlauts
        stIn = stIn.Replace("ä", "ae");
        stIn = stIn.Replace("ö", "oe");
        stIn = stIn.Replace("ü", "ue");

        stIn = stIn.Replace("Ä", "Ae");
        stIn = stIn.Replace("Ö", "Oe");
        stIn = stIn.Replace("Ü", "Ue");
        // End special treatment for German Umlauts

        string stFormD = stIn.Normalize(System.Text.NormalizationForm.FormD);
        System.Text.StringBuilder sb = new System.Text.StringBuilder();

        for (int ich = 0; ich < stFormD.Length; ich++)
        {
            System.Globalization.UnicodeCategory uc = System.Globalization.CharUnicodeInfo.GetUnicodeCategory(stFormD[ich]);

            if (uc != System.Globalization.UnicodeCategory.NonSpacingMark)
            {
                sb.Append(stFormD[ich]);
            } // End if (uc != System.Globalization.UnicodeCategory.NonSpacingMark)

        } // Next ich


        //return (sb.ToString().Normalize(System.Text.NormalizationForm.FormC));
        return (sb.ToString().Normalize(System.Text.NormalizationForm.FormKC));
    } // End Function Latinize

And last but not least, I would not use the built-in ASP.NET membership provider, because it joins tables via username + applicationname to rolename, instead of using a unique id. This means you will not be able to change a user or group/role name without changing all the mapping tables. I think doing that this way is highly undesirable, and definitely stupid and careless, if not outright hazardous from Microsoft's part.
I would go as far as calling it impudent to release such garbage into the wild.

The below "thing" demonstrates the problem's utter stupidity and should not ever be used

CREATE TABLE [dbo].[UsersInRoles](
    [Username] [varchar](255) NOT NULL,
    [Rolename] [varchar](255) NOT NULL,
    [ApplicationName] [varchar](255) NOT NULL,
 CONSTRAINT [usersinroles_pkey] PRIMARY KEY CLUSTERED 
(
    [Username] ASC,
    [Rolename] ASC,
    [ApplicationName] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

for the following reasons:

  • Problem 1: string join will make user to role join very slow - very bad for performance
  • Problem 2: varchar - should be nvarchar, unless you live in an English-only universe
  • Problem 3: length of field username & rolename must conform to minimum specs of latest active directory, otherwise there will be problems syncing with activedirectory. Plus Active directory allows for unicode characters.
  • Problem 4: No foreign key reference to the user and roles table - ends up with data garbage sooner or later (usually sooner)
  • Problem 5: if now you change the username or the groupname, UserInRoles will not be updated and the users's group mapping will orphan - ends up with data garbage, left joins will bring empty columns, programs might crash because of an unhandled NullReferenceException.
  • Problem 6: because the foreign key reference is missing, it is possible to change a username/groupname
  • Problem 7: data garbage will sooner or later lead to muliple rows when reporting/displaying data
  • Problem 8: The primary key used here should instead be a unique constraint
  • Problem 9: There is ONE group name, but for example group "administrators" needs to be localized to many languages, which the membership provider doesn't support. Additionaly, the groupname belongs into a mapping table, because one group can have N names for N languages, and then it must be ensured that the groupname is unique within a specific language.
  • Problem 10: Not visible here, but the user table contains a field e-mail. This is an utter fail because one user can have n email addresses, and a smart membership provider should take this into account, instead of stupidly limiting the user to ONE email address.
  • Problem 11: Plus additionally, the mentioned email field is limited to 128 character, which is smaller than the maximum allowed number of characters allowed in an email address as per rfc. FAIL - sooner or later somebody will be unable to input his mail address - even if he has only one. What is the maximum length of a valid email address?

and I am sure there are a lot more problems with the MS-supplied membership-provider fiasco. For example using a fast hash algorithm (MD5), which is an antipattern for this case, because that allows for rainbow table attack, especially if the hash is not salted. If the MS membership provider demonstrates one thing, then this is how NOT to design a membership provider.

like image 56
Stefan Steiger Avatar answered Sep 21 '22 04:09

Stefan Steiger