Redirect to Login then to Referrer Page (link)

I’m working on a site with 3 contexts. Two of the contexts have several resources behind their own login area (using the Login snippet). It all works pretty much as expected except for one hurdle which may have nothing to do with the Login Extra, I’m not sure.

For example, if I were to email a link to a registered user of a page that was behind the login area, but that user was not currently logged in, when they click the link they get a 404 which I guess makes sense because it’s blocked by the login.

What I need to happen is instead of a 404 they are redirected to the login page first and once they have successfully logged-in they would then be redirected to the referring link, which in this case would be the link I included in the email.

I feel like this is all an .htaccess and not a Login snippet issue, but either way I’m unsure how to proceed. I assume it’s possible?

Thanks

At least the “redirect to the login page” part should be fairly simple.
Set the system setting unauthorized_page to the right value and make sure the anonymous user group has an access policy of “Load Only” for the resource group of the protected resources.

The second part (redirected to the referring link) is probably more difficult. The Login snippet has a property loginResourceId that maybe can be filled dynamically from a url-parameter to achieve this, but I’m not sure.

Interestingly I already had the unauthorized_page setting configured as per the instructions but it’s clearly not working. Does this setting apply to all contexts or just the web? Because the login protected resoures are all in non-web contexts if that matters.

As for the rest of it I suspect it will require an .htaccess solution with a whole lot of regex I won’t understand.

When you try to access a protected resource and get forwarded to the login page, the url in the browser doesn’t change. This means that with the default behaviour of [[!Login]] you kind of already get redirected back to the protected resource after a successful login. However, this doesn’t work when you enter wrong credentials and the login form is reloaded.

Maybe I’m missing something simple and obvious, but here are some ideas that may or may not work:

Version 1: Request-parameter 'redirectBack’

Template:

[[!Login? &loginTpl=`myLoginTpl`]]

Chunk “myLoginTpl”: Add a hidden field with name “redirectBack” (important!) to the login form.

<input type="hidden" name="redirectBack" value="[[!getReferrerId]]" />

Snippet “getReferrerId”: Change the values of $fallback_id and $resource_id_login.

<?php
if (isset($_POST['redirectBack'])){
    //login form reload -> don't change value
    return (int)$_POST['redirectBack'];
} else {
    $fallback_id = 20; //default loginResourceId, if it can't be determined from the request parameter
    $resource_id_login = 10; //resource id of the login page
    
    //try to determine original resource id from the request parameter (for friendly urls)
    if (isset($_REQUEST['q'])){
        $req_uri = $_REQUEST['q']; 
        $resource_id = $modx->findResource($req_uri);
        if ($resource_id && $resource_id != $resource_id_login){
            return $resource_id;
        }
    }
    return $fallback_id;
}

Version 2: Redirect in PostHook

Template:

[[!Login? &loginTpl=`myLoginTpl` &postHooks=`myLoginRedirectPostHook`]]

Chunk “myLoginTpl”: Add a hidden field with name “referrer” to the login form.

<input type="hidden" name="referrer" value="[[!getReferrer]]" />

Snippet “getReferrer”:

<?php
if (isset($_POST['referrer'])){
    //form reload, don't change value
    return $_POST['referrer'];
} else {
    $referrer = $_SERVER['REQUEST_URI'];
    //maybe do some checks here
    return $referrer;
}

Snippet “myLoginRedirectPostHook”:

<?php
$modx = $hook->modx;

if (isset($_POST['referrer']) && !empty($_POST['referrer'])){
    $url = $_POST['referrer'];
    //maybe do some checks here to prevent shenanigans
    $modx->sendRedirect($url);
}

return true;

It is probably not a good idea to call sendRedirect() in a posthook, because I think it prevents the rest of the code from being executed. But it would allow to use the request uri directly without the need to convert it into a resource-id.

Give the anonymous user “Load Only” access for the context, in which he tries to access the resource.