Answers.Body: <p>Elevated sessions are basically user sessions where the user has proven to Craft that it’s really <em>them</em> 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).</p>
  
  <p>The point of the concept is to reduce the chances of malicious permission escalation.</p>
  
  <p>(<em><strong>In English,</strong></em> 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.)</p>
  
  <p><strong>I’m explaining this so that you understand the risk in what I’m about to say.</strong></p>
  
  <p>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 <code>config/general.php</code> and adding this:</p>
  
  <pre class="lang-php prettyprint-override"><code>return [
      'elevatedSessionDuration' =&gt; 0,
      // ...
  ];
  </code></pre>
  
  <p>If you do that, Craft will no longer require elevated sessions anywhere.</p>
  
  <p>Otherwise, you need to have the user enter their current password as part of the user form, and send that off to Craft’s <code>users/start-elevated-session</code> action.</p>
  
  <p>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:</p>
  
  <pre class="lang-twig prettyprint-override"><code>&lt;script&gt;
      window.startElevatedSessionUrl = "{{ actionUrl('users/start-elevated-session')|e('js')|raw }}";
      window.csrfTokenValue = "{{ craft.app.request.csrfToken|e('js')|raw }}";
  &lt;/script&gt;
  </code></pre>
  
  <p>Then in JavaScript (assuming you’re using jQuery),</p>
  
  <pre class="lang-js prettyprint-override"><code>$.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...
      })
  });
  </code></pre>
  
  <p>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 <code>users/get-elevated-session-timeout</code>. If <code>response.timeout</code> is greater than <code>5</code> (seconds) or so, then it’s safe to submit the form without prompting them for their current password.</p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2022-03-11T22:24:35.587Z
Answers.Text: Thank you for this!
Answers.UserDisplayName: Meintjes
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-09-19T20:03:22.993Z
Answers.LastActivityDate: 2018-09-19T20:31:49.090Z
Answers.LastEditDate: 2018-09-19T20:31:49.090Z
Answers.UserDisplayName: Brandon Kelly
Answers.Body: <p>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 <code>Users::EVENT_BEFORE_ASSIGN_USER_TO_DEFAULT_GROUP</code> event handler.</p>
  
  <p>Register the handler from your modules/Module.php module’s <code>init()</code> method. Add the following code to it.</p>
  
  <pre class="lang-php prettyprint-override"><code>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-&gt;user;
      $form = Craft::$app-&gt;getRequest()-&gt;getBodyParam('form');
  
      $groupMap = [
          'form-a' =&gt; [2],
          'form-b' =&gt; [2, 3],
      ];
  
      if (($groups = $groupMap[$form] ?? false) !== false) {
          // Don’t assign the default group and assign groups manually
          $event-&gt;isValid = false;
          Craft::$app-&gt;getUsers()-&gt;assignUserToGroups($user-&gt;id, $groups);
      }
  });
  </code></pre>
  
  <p>Enable bootstrapping of the module in config/app.php, simply un-comment the <code>'bootstrap' =&gt; ['my-module']</code> line.</p>
  
  <p>Last step left is to add a hidden input to your registration forms to set a <code>form</code> 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.</p>
  
  <pre><code>&lt;input type="hidden" name="form" value="form-b"&gt;
  </code></pre>
  
  <p>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.</p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-09-19T17:29:01.423Z
Answers.Text: 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?
Answers.UserDisplayName: Amanda Lutz
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-09-19T17:36:05.223Z
Answers.Text: Or I guess, I really have to get into it like https://docs.craftcms.com/v3/extend/module-guide.html, don't I?
Answers.UserDisplayName: Amanda Lutz
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-09-19T17:07:34.233Z
Answers.LastActivityDate: 2018-09-19T17:07:34.233Z
Answers.UserDisplayName: carlcs
Answers.Body: <p>You can avoid the elevated session problem with the <code>EVENT_AFTER_ASSIGN_USER_TO_DEFAULT_GROUP</code> 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 <code>init()</code> function.</p>
  
  <p>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 <code>post</code> to the standard Craft <code>save-user</code> controller and catch it in the event: <a href="https://craftcms.stackexchange.com/a/27926/4433">https://craftcms.stackexchange.com/a/27926/4433</a></p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-10-03T17:37:07.210Z
Answers.Text: No need to answer this 3 times especially if someone else (carlcs) wrote exactly the same
Answers.UserDisplayName: Robin Schambach
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-10-03T18:20:38.060Z
Answers.Text: Yeah, I didn't realize his answer was similar to mine until after I posted.  Feel free to delete if you want.
Answers.UserDisplayName: Ryan
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-10-03T19:30:14.900Z
Answers.Text: 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
Answers.UserDisplayName: Robin Schambach
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-10-04T13:57:17.140Z
Answers.Text: 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.
Answers.UserDisplayName: Ryan
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-10-03T16:45:13.160Z
Answers.LastActivityDate: 2018-10-03T16:45:13.160Z
Answers.UserDisplayName: Ryan
Body: <p>This has been a long process that I set aside to resolve "later". Well, it's "later" and the clients wants to launch asap.</p>
  
  <p>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.</p>
  
  <p>Using the <a href="https://docs.craftcms.com/v3/dev/examples/user-registration-form.html" rel="nofollow noreferrer">craft front end User Registration template</a> I added a hidden field for the group id <code>&lt;input type="hidden" name="groups[]" value="2"&gt;</code> and when I submit I get "HTTP 403 – Forbidden, This action may only be performed with an elevated session."</p>
  
  <p>Some googling lead me to <a href="https://github.com/craftcms/cms/issues/2311#issuecomment-358087548" rel="nofollow noreferrer">this craftcms github comment</a>. 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:</p>
  
  <pre><code>$.ajax({
      method: 'POST',
      url: 'https://www.myurl.ca/',
      data: { action: 'users/start-elevated-session', password: 'xxx'}
  })
  .done(function( msg ) {
      alert( "Data: " + msg );
  });
  </code></pre>
  
  <p>But nothing is alerted. So something is failing. </p>
  
  <p>When I try <code>https://www.myurl.ca/?action=users/start-elevated-session&amp;password=xxx</code> 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".</p>
  
  <p>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?</p>
  
  <p>Thanks
  Amanda</p>
  
Tags: <craft3><elevated-session>
Title: Create new users with front end form and put them in a group, need help with start-elevated-session
ThreadText: # Create new users with front end form and put them in a group, need help with start-elevated-session
  
  (Asked by: username_0 on 2018-09-19)
  
  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](https://docs.craftcms.com/v3/dev/examples/user-registration-form.html) 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](https://github.com/craftcms/cms/issues/2311#issuecomment-358087548). 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
  username_0
  
  **Tags:** craft3, elevated-session
  
  
  ---
  
  ## 3 answers
  
  ### Answer by username_1 on 2018-09-19. Score: 5 | Accepted answer
  
  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:
  
  ```php
  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:
  
  ```twig
  <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),
  
  ```js
  $.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.
  
  > **Comments:**
  > **username_2**: Thank you for this!  
  
  ---
  
  ### Answer by username_3 on 2018-09-19. Score: 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.
  
  ```php
  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.
  
  > **Comments:**
  > **username_0**: 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?  
  > **username_0**: Or I guess, I really have to get into it like https://docs.craftcms.com/v3/extend/module-guide.html, don't I?  
  
  ---
  
  ### Answer by username_4 on 2018-10-03. Score: 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>
  
  > **Comments:**
  > **username_5**: No need to answer this 3 times especially if someone else (username_3) wrote exactly the same  
  > **username_4**: Yeah, I didn't realize his answer was similar to mine until after I posted.  Feel free to delete if you want.  
  > **username_5**: 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  
  > **username_4**: Yeah I had read this answer a week back while trying to sort out the problem, didn't fully understand username_3' 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.  
  
  ---
  
  