Help with website (not CMS) login

Silverstripe Version: 4.1

Appreciate this is a huge question… I have this requirement for user authentication on a project.

The form is displayed in a modal and is to be sent via Ajax. If it fails, I’d inject some meaningful errors into the modal. Success would reload the page and specific areas (like the login button) change state by checking if the user is logged in.

So, quite different how it’s handled normally but pretty standard I thought.

I’m struggling to find much useful info that’s not years old on how to get this done.

The docs Authentication – SilverStripe Documentation make it seem like it can be done.

So far I wrote the modal and the form and sent it via ajax to a controller. Then tried to use SilverStripe\Security\MemberAuthenticator\MemberAuthenticator like:

$MemberAuthenticator = new MemberAuthenticator;
$result = $MemberAuthenticator->authenticate($data, $request) 

And that does work but it feels like I’m misusing/hacking it.

The end goal is:

  • Have a custom login form available in all pages
  • Send login data from custom form to process via ajax
  • Save these user records in their own table, not the CMS Members/Groups.

I’d love some direction on how to go about that the right way.

Seems I should extend the Member DataObject and point it at a different table.

Do I then need my own extended versions of MemberLoginForm, LoginHandler and MemberAuthenticator to get this done?

Not saving these members in the provided Member object and table, is creating a big problem if you want to use the MemberAuthenticator. MemberAuthenticator relies on the object and records to be there. Moving it in to it’s own object, will require you to write or override part of the MemberAuthenticator.

The Members object already contains everything a member would need to exist, things like the encryption type and the password are safely stored there, as well as the encryption of the password etc.

Your custom login form should be just fine. However, I don’t see much value in doing it via ajax. Why not a normal submission of the form. You want to redirect the user after submission anyway, so there’s no point in using ajax there.

Your handling of the logging in is fine, it’s exactly what the login handler does. However, the Authenticator does not actively log the member in. After authentication, you’ll have to log the member in to the IdentityStore:

        /** @var IdentityStore $identityStore */
        $identityStore = Injector::inst()->get(IdentityStore::class);
        $identityStore->logIn($member, $rememberMe, $request);

Hi thanks for the reply.

Yea man you’re right. After digging through the code I’ve seen just how big of a problem this is. Like the base Authenticator interface class checkPassword function expecting a Member pretty much kills it at the start of the inheritance…

I’d wrongly assumed how far I could get with extending Member and what Authenticator does.

The ajax is because the form is in a modal not a page because, designer… On failure, it wouldn’t be a great experience to be sent to another page to be told that didn’t work, try again - in another login form.

If it was one project I’d push back but I’ve got at least 6 existing project’s I need to migrate that use a modal of sorts to display the form. An example is this site: https://www.coffeelab.co.nz click the user icon (top right, second in) and see it shows a form. Click the “Forgot your info” and back link.

At the moment it feels like if I really really had to have them in a seperate table I would need to duplicate a good chunk of the Member and relating code and make a few changes (not implement the Authenticator interface for e.g could remove the CMS centric stuff like actAs etc. Not sure that’s a good use of time…

Let’s say I can use the member table/object, two groups of Admins and “Site Users” and have foreign keys to it in related tables (Data Extensions don’t look like a great idea here). Would I be able to make *Admin files to split the /admin CMS view?
So Under Security it just lists the Admin group users and another “Site Users” under the Security nav link on the left of the CMS to show the “Site Users” group?

Using a modal dos not explicitly mean it has to be an ajax call. As on the example, if you’d give each form it’s own route to post to, instead of letting the javascript decide, you’d pretty much achieve the same result I’d think?
As an example, you can see how https://summer.co.nz does it, if you login with wrong credentials, the whole page is loaded and the overlay is immediately shown.
But yeah, doesn’t really matter much, I just asked because I would like to know reasoning :slight_smile:

If you want to split out the two members, you can either use the existing functionality of viewing members via viewing the groups, or customise the Security admin via a dataextension.

As for the DataExtension vs. separate DataObject, I think it’s a matter of preference, but unless you expect to have a lot of fields extra on the Member, sticking to just the Member and a DataExtension would sound more logical to me.

No sweat.

Not too sure what you by letting Javascript decide the route. It’ll be sending where it’s told, either by hard coding it into the XHR or could pull it out of the form attribute if you wanted. I don’t see much difference there.

The reason for Ajax is basically the same reason why Angular etc exists. To not reload an entire page to just show a string of text. Pretty much all my projects are 50/50 mobile or more, some are also rural so it matters, I think.

Plus, we devs go to great lengths to avoid a FOUC-esque experience like that. It’s not immediate. It’s great on my Fibre connection here though, granted.

Plus, it gives you an opportunity if you wanted to do something “cool” interface-wise with the response, positive or negative.

Some of the sites do have a lot of extra fields stuck to a Member.

What gets my goat about it is the purpose/context of the data isn’t the same. Like Oil and water, both liquids but don’t mix. CMS and site users aren’t the same.

Your javascript with hard coded endpoints should work just fine, like I said, I was just curious, not saying it’s wrong or anything :slight_smile:

What you seem to misunderstand a bit though, is the Members. Members, in the concept of SilverStripe are not specifically CMS users, they can be anything. The Member model is just a container that holds Member Information, which coincidentally is also used for the CMS and not the other way around :wink:

Hah, nah I get it mate. I understand the what and why. You’re underselling it a bit, there’s things baked in for CMS stuff like actAs and DefaultAdmin etc. The dev’s obviously needed to log CMS users in at some point.

I just don’t like it. It could be quite useful if it wasn’t so tightly coupled to Member. Be pretty sweet if it used instanceof or is_subclass of and Member started off as an Interface that could accept models/dataobjects. That way you could keep it practically identical to now but if a site required CMS admins, (and say…) public event submitters and public event managers that never needed CMS access you could split their data out but use the SS brains to run the login/logout/register/forgot/reset etc.

Just an idea with beer in hand, ignore.

Anyway it’s clear I’m way outside what I’m meant/expected to be doing with SS, that’s cool.

Would you be able to tell me how I can re-template the login form? I have a Security_login page for the CMS login but I’m not sure what file I need to put in place to override the Member form.

Cheers for taking the time too, appreciate it.