U4-10752 - Membership snippets not working at all

Created by Roger Davies 12 Dec 2017, 12:25:34 Updated by Rune Antonsen 19 Apr 2018, 11:13:09

Hi all,

First just want to say I do appreciate everyone's hard work, it's easy to criticise when we find bugs, so I wanted to start by thanking the core dev team and saying that I have every faith that any troubles can be worked through!

That said, you're going to love me because I have found quite a vast number of issues with 7.7.6 and membership provider. So many, in fact, that I don't believe this is a functioning feature as it stands (without lots of extra code to assist the Umbraco Core and using a custom membership provider etc etc). These tests were done using a vanilla install of Umbraco 7.7.6 installed on MS SQL 2012 with no code whatsoever besides UmbracoCms itself (all these code tests were done in the (Umbraco native) views:

  • Membership Provider Cannot Be Used 'Out of the Box' at all without very specific configuration

Umbraco is currently shipped with a Web.config that has allowManuallyChangingPassword="false" in the "UmbracoMembershipProvider" key of the "membership" node. This means this key must be changed before Members can be created or used in code at all. This took only 10 minutes of Googling to find - via Stack overflow, as this isn't documentated anywhere within the community that I could find. A small one for Umbraco devs I suspect, but I do suggest either (1) shipping Umbraco in a way that the membership feature can be used or (2) clear instructions on how someone might setup the membership provider to be able to actually use it.

  • None of the membership scripts (provided as part of the 'New partial view from snippet' feature) work
  • The login script, when atteempting to login gives:

Server Error in '/' Application. The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters. Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: System.FormatException: The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters.

Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace:

[FormatException: The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters. ] System.Convert.FromBase64_Decode(Char* startInputPtr, Int32 inputLength, Byte* startDestPtr, Int32 destLength) +733 System.Convert.FromBase64CharPtr(Char* inputPtr, Int32 inputLength) +152 System.Convert.FromBase64String(String s) +49 Umbraco.Core.Security.MembershipProviderBase.EncryptOrHashPassword(String pass, String salt) +137 Umbraco.Core.Security.MembershipProviderBase.CheckPassword(String password, String dbPassword) +157 Umbraco.Web.Security.Providers.UmbracoMembershipProvider2.PerformValidateUser(String username, String password) +985 Umbraco.Web.Security.Providers.UmbracoMembershipProvider2.ValidateUser(String username, String password) +14 Umbraco.Web.Security.MembershipHelper.Login(String username, String password) +29 Umbraco.Web.Controllers.UmbLoginController.HandleLogin(LoginModel model) +147 lambda_method(Closure , ControllerBase , Object[] ) +91 System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary2 parameters) +229 System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary2 parameters) +35 System.Web.Mvc.Async.AsyncControllerActionInvoker.b__39(IAsyncResult asyncResult, ActionInvocation innerInvokeState) +39 System.Web.Mvc.Async.WrappedAsyncResult2.CallEndDelegate(IAsyncResult asyncResult) +71 System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethod(IAsyncResult asyncResult) +42 System.Web.Mvc.Async.AsyncInvocationWithFilters.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3d() +72 System.Web.Mvc.Async.<>c__DisplayClass46.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3f() +386 System.Web.Mvc.Async.<>c__DisplayClass46.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3f() +386 System.Web.Mvc.Async.<>c__DisplayClass46.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3f() +386 System.Web.Mvc.Async.<>c__DisplayClass46.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3f() +386 System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeActionMethodWithFilters(IAsyncResult asyncResult) +42 System.Web.Mvc.Async.<>c__DisplayClass2b.<BeginInvokeAction>b__1c() +38 System.Web.Mvc.Async.<>c__DisplayClass21.<BeginInvokeAction>b__1e(IAsyncResult asyncResult) +186 System.Web.Mvc.Async.AsyncControllerActionInvoker.EndInvokeAction(IAsyncResult asyncResult) +38 System.Web.Mvc.Controller.<BeginExecuteCore>b__1d(IAsyncResult asyncResult, ExecuteCoreState innerState) +29 System.Web.Mvc.Async.WrappedAsyncVoid1.CallEndDelegate(IAsyncResult asyncResult) +67 System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +53 System.Web.Mvc.Async.WrappedAsyncVoid1.CallEndDelegate(IAsyncResult asyncResult) +36 System.Web.Mvc.Controller.EndExecute(IAsyncResult asyncResult) +38 System.Web.Mvc.MvcHandler.<BeginProcessRequest>b__5(IAsyncResult asyncResult, ProcessRequestState innerState) +44 System.Web.Mvc.Async.WrappedAsyncVoid1.CallEndDelegate(IAsyncResult asyncResult) +67 System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +38 System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +399 System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +137

Again, this one is also compounded by the fact that it's not documented. If I knew what the encoding/hashing was supposed to be (based on this error I guessed in might be base64 as a quick stab in the dark - but doing this threw a different kind of YSOD further down the Umbraco core and at this point I gave up.

  • Member.ValidateUser(...) throws the same YSOD ( The input is not a valid Base-64 string as it contains a non-base 64 character)

I then noticed a very different type of code than has ever been used in Umbraco to validate member (as provided in the partial templates) and this too throws a YSOD about 50% of the time.

At the time of writing however, this status one is actually behaving - I saw it throw a YSOD last night with the same vanilla Umbraco 7.7.6 on a different server - but I believe this will all be related to whatever changes have been made to the membership provider.

  • MembershipService.SetPassword(...) also throws YSOD

I suspect there's loads more, but too much to log in this one ticket.

Based on the fact, Umbraco 7.7.6 cannot (1) create a user in code (2) validate a user in code (3) log a user in - I am struggling to imagine how this feature would work at all?

1 Attachments

Comments

Roger Davies 12 Dec 2017, 12:26:26

I've attached the one exception thrown in about 3 of these instances - if I can accurately reproduce the others I will attach stacktraces of these too!


Sebastiaan Janssen 12 Dec 2017, 12:34:20

I've updated the title and want to assure you that membership APIs all work and all of these problems can be worked around. But you point out the biggest problems: the built-in templates were never reviewed and updated for 7.7+ and there's no good documentation for this version. That's something we definitely need to work on!


Roger Davies 12 Dec 2017, 12:38:43

Hi @sebastiaan I think you've misunderstood, this has nothing to do with the templates - they are an obvious symptom that nothing about the memberhsip provider currently works, as the Umbraco installation is shipped. Obviously these need updating (if indeed this is possible) but please don't deliberately misunderstand the focus of this ticket for political reasons.

What I'm asking here (to be clear) is (1) can you ship Umbraco in a manner that these features work 'out of the box' or (2) document what necessary configuration is required to have it work.

Without either of these, I can't help but feel my original headline was much more accurate


Sebastiaan Janssen 12 Dec 2017, 14:51:48

I am not misunderstanding anything for political reasons :-) It's totally possible to use the membership provider as it is now, it just needs a bit of explaining.


Roger Davies 12 Dec 2017, 15:02:56

If this turns out to be the case then I will apologise and retract my point.

IF


Roger Davies 12 Dec 2017, 18:28:48

Hi Sabastiaan,

Okay I believe I've found the first problem, in umbraco-cms-release-7.7.6\src\umbraco.core\security\membershipproviderbase.cs line 689 theres:

        if (UseLegacyEncoding)
        {
            return LegacyEncodePassword(pass);
        }

and new Umbraco installations are shipped with useLegacyEncoding=false which is what causes the exception which begins in Umbraco.Core.Security from what I can tell. Obviously I'm not clued up as to the reasoning of these names or full context of what they mean, but setting this to true seems to allow me to actually login without Umbraco core throwing an exception, not had a chance to check the rest of the bits yet.

I will do some further testing and let you know if I find anything else


Roger Davies 12 Dec 2017, 18:34:33

But I hope you might be able to see why I felt renaming this ticket to just "Membership templates not working at all" is a bit of a trivialisation of a much deeper-seated issue


Sebastiaan Janssen 12 Dec 2017, 19:23:47

Please don't waste too much time on this, I need to look up some code for you and you will need to use a few different APIs.


Sebastiaan Janssen 12 Dec 2017, 19:26:18

But I hope you might be able to see why I felt renaming this ticket to just "Membership templates not working at all" is a bit of a trivialisation of a much deeper-seated issue

I really understand you but please understand that making things secure is not trivial and we can't allow for certain API calls if we want to make them secure. If you want to keep useLegacyEncoding set to false then you need to provider a properly encrypted password to the membershipprovider. We have helpers for that somewhere (I will need to look them up though and that will take some time).


Sebastiaan Janssen 13 Dec 2017, 18:27:02

I've checked it out now and as far as I can tell, the snippets the come with macro partials work (which I called templates before are called Snippets) and custom code will work too, here's the basics of registering a member:

https://our.umbraco.org/forum/umbraco-7/using-umbraco-7/55455-Create-member-with-MemberServiceCreateWithIdentity#comment-195379

This uses memberService.SavePassword to circumvent the fact that passwords cannot be updated when you have allowManuallyChangingPassword set to false (the default). It also helps convert the password to the correct hash, so you don't have to do that yourself and you can leave useLegacyEncoding set to false.

I suspect you're trying to use the MembershipProvider directly, which is not possible, you have to go through Umbraco's MemberService to do these things. Again, this keeps everything secure and the API is nice and simple.

More info on the MemberService: https://our.umbraco.org/documentation/reference/management/services/memberservice

If you have any more questions, make sure to head over to the forum to get some help from our friendly community: https://our.umbraco.org


Sebastiaan Janssen 13 Dec 2017, 18:39:31

Here's a little Visual Studio solution which uses both the macro snippets and custom code to register and login members. I didn't changes the snippets, they were created from Umbraco directly: https://drive.google.com/file/d/1JUr3jmHkG5gzNUkWL8QLlUvz-GrvIDCx/view?usp=sharing

Backoffice login: test@test.com / test123456


Rune Antonsen 19 Apr 2018, 11:13:09

Or this might be an issue with CultureInfo again, since I'm getting this on my weird Culture dev VM.


Priority: Normal

Type: Bug

State: Can't Reproduce

Assignee:

Difficulty: Normal

Category:

Backwards Compatible: True

Fix Submitted:

Affected versions:

Due in version:

Sprint:

Story Points:

Cycle: