4

This has been a long process that I set aside to resolve "later". Well, it's "later" and the clients wants to launch asap.

Only registered users can use this site. There are 2 groups: Manager and Employee. Users in Manager group have permission to create new users and put them in Employee group. It all works in the back end.

Using the craft front end User Registration template I added a hidden field for the group id <input type="hidden" name="groups[]" value="2"> and when I submit I get "HTTP 403 – Forbidden, This action may only be performed with an elevated session."

Some googling lead me to this craftcms github comment. I get what it's saying about the elevated session and needing to send the password again but am having issues doing it. First step is testing with hard coded values on my form submit:

$.ajax({
    method: 'POST',
    url: 'https://www.myurl.ca/',
    data: { action: 'users/start-elevated-session', password: 'xxx'}
})
.done(function( msg ) {
    alert( "Data: " + msg );
});

But nothing is alerted. So something is failing.

When I try https://www.myurl.ca/?action=users/start-elevated-session&password=xxx in a new browser tab I get "TypeError, Argument 1 passed to craft\web\User::startElevatedSession() must be of the type string, null given, called in /.../vendor/craftcms/cms/src/controllers/UsersController.php on line 234".

So it can't find password value. It's just getting null. I feel like maybe I don't know what "body params" means in the github comment. Can someone explain it like I'm 5?

Thanks Amanda

3 Answers 3

5

Elevated sessions are basically user sessions where the user has proven to Craft that it’s really them within the last 5 minutes. It’s used whenever Craft is about to increase permissions on a user (either directly or via a user group).

The point of the concept is to reduce the chances of malicious permission escalation.

(In English, that means if a user with certain permissions/groups walks away from their computer, elevated sessions reduces the likelihood that another user could walk up to it, find their own user account, and increase their account permissions. They’d think they’re going to get away with it right up until the point where Craft asks them to re-enter their password, which kicks off the elevated session.)

I’m explaining this so that you understand the risk in what I’m about to say.

Maybe the easiest way for you to work around this issue is to just disable elevated sessions on your site. You can do that by opening config/general.php and adding this:

return [
    'elevatedSessionDuration' => 0,
    // ...
];

If you do that, Craft will no longer require elevated sessions anywhere.

Otherwise, you need to have the user enter their current password as part of the user form, and send that off to Craft’s users/start-elevated-session action.

You can get that action URL, and the CSRF token needed to make any POST requests to Craft, and send them to JavaScript like this:

<script>
    window.startElevatedSessionUrl = "{{ actionUrl('users/start-elevated-session')|e('js')|raw }}";
    window.csrfTokenValue = "{{ craft.app.request.csrfToken|e('js')|raw }}";
</script>

Then in JavaScript (assuming you’re using jQuery),

$.ajax({
    url: window.startElevatedSessionUrl, {
    type: 'POST',
    dataType: 'json',
    headers: {
        'X-CSRF-Token': window.csrfTokenValue
    },
    data: {
        password: $('#current-password-input').val()
    },
    success: function(response, textStatus) {
        // submit the user form...
    }),
    error: function(jqXHR, textStatus) {
        // error handling...
    })
});

If you want to get even fancier you can check to make sure the user doesn’t already have an elevated session by running a POST request to users/get-elevated-session-timeout. If response.timeout is greater than 5 (seconds) or so, then it’s safe to submit the form without prompting them for their current password.

1
  • Thank you for this!
    – Meintjes
    Mar 11, 2022 at 22:24
2

When you enable public registration in Craft’s user settings, you can select a user group that new users should be added to. Once a group is selected you can override the group programatically from a Users::EVENT_BEFORE_ASSIGN_USER_TO_DEFAULT_GROUP event handler.

Register the handler from your modules/Module.php module’s init() method. Add the following code to it.

use Craft;
use craft\events\UserAssignGroupEvent;
use craft\services\Users;
use yii\base\Event;

Event::on(Users::class, Users::EVENT_BEFORE_ASSIGN_USER_TO_DEFAULT_GROUP, function(UserAssignGroupEvent $event) {
    $user = $event->user;
    $form = Craft::$app->getRequest()->getBodyParam('form');

    $groupMap = [
        'form-a' => [2],
        'form-b' => [2, 3],
    ];

    if (($groups = $groupMap[$form] ?? false) !== false) {
        // Don’t assign the default group and assign groups manually
        $event->isValid = false;
        Craft::$app->getUsers()->assignUserToGroups($user->id, $groups);
    }
});

Enable bootstrapping of the module in config/app.php, simply un-comment the 'bootstrap' => ['my-module'] line.

Last step left is to add a hidden input to your registration forms to set a form param. This param is used in the event handler to lookup the user group IDs the user should be added to when registering with this form.

<input type="hidden" name="form" value="form-b">

This is a much more secure than passing group IDs with the form, and you don’t have to deal with elevated user sessions at all.

2
  • I like what you're saying but I don't know where to do it. I think put your above code example in /modules/my-module.php? And then ...? I don't know how to register the handler. I assume thing under '// Custom initialization code goes here...' that calls the file? Sep 19, 2018 at 17:29
  • Or I guess, I really have to get into it like docs.craftcms.com/v3/extend/module-guide.html, don't I? Sep 19, 2018 at 17:36
0

You can avoid the elevated session problem with the EVENT_AFTER_ASSIGN_USER_TO_DEFAULT_GROUP event. Just register the user as normal, letting them get added to the default group, and then use this event in your custom plugin's primary class init() function.

I have an example of my code in this ticket, although I'm doing a standard form post instead of with AJAX, but you could post to the standard Craft save-user controller and catch it in the event: https://craftcms.stackexchange.com/a/27926/4433

4
  • No need to answer this 3 times especially if someone else (carlcs) wrote exactly the same Oct 3, 2018 at 17:37
  • Yeah, I didn't realize his answer was similar to mine until after I posted. Feel free to delete if you want.
    – Ryan
    Oct 3, 2018 at 18:20
  • Nono, I didn't mean to discourage you in any way. It's a good solution and it's good to share it. Every piece of knowledge is highly appreciated, so well done. Just read other answers before and everything is fine Oct 3, 2018 at 19:30
  • Yeah I had read this answer a week back while trying to sort out the problem, didn't fully understand carlcs' answer, eventually solved my problem using a similar approach coincidentally, kept a reminder to come back here and to other tickets to post a link to my answer, then realized after posting that my answer was similar. I figured it's better to have two similar answers for others to cross check than to delete mine, and since I just posted a link instead of my code instead of the code itself, I decided to just leave it. All good though.
    – Ryan
    Oct 4, 2018 at 13:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.