Protect non-XenForo directory with XenForo login

peteGSX

New member
I'm not sure of the best forum to ask this in, and I suspect my lack of understanding of terminology may be limiting my ability to find what I'm looking for as I suspect my question is likely already answered (but I can't find it).

I have some resources on our cPanel server in a separate directory that I only want accessible to registered XenForo users.

This directory (downloads) is at the same folder level as our forum installation (xenforo).

public_html/xenforo
public_html/downloads

After some searching around, I've tried adding this to the .htaccess file in the downloads directory:

RewriteEngine On
RewriteCond %{HTTP_COOKIE} !xf_user=([^;]+)
RewriteRule ^ /xenforo/index.php?login/&redirect=%{REQUEST_URI} [L,R=302]
My testing shows this works in so much as a user who has not authenticated will be redirected to the XenForo login page, but once logged in, they are not redirected to the downloads URL, but instead continue to see the message in XenForo that they are already logged in.

Can anyone shed some light on the right way to go about this or at least correct my terminology etc. so I can start looking in the right places?

I'm more than happy to be taught how to fish!
 
If you are using XenForo 2.x the redirect code should be _xfRedirect instead of redirect. So I suspect that changing the code from
Code:
RewriteRule ^ /xenforo/index.php?login/&redirect=%{REQUEST_URI} [L,R=302]
to
Code:
RewriteRule ^ /xenforo/index.php?login/?_xfRedirect=%{REQUEST_URI} [L,R=302]
might make it work.
 
I feel this is a bad way to do it since the user can simply create the cookie with any value, and they have access.

Xenforo sessions should actually be tracked, with single application to application bridge (like Word<-->Xenforo), or something like Amember which can bridge several apps and control access to directories as well.
 
If you are using XenForo 2.x the redirect code should be _xfRedirect instead of redirect. So I suspect that changing the code from
Code:
RewriteRule ^ /xenforo/index.php?login/&redirect=%{REQUEST_URI} [L,R=302]
to
Code:
RewriteRule ^ /xenforo/index.php?login/?_xfRedirect=%{REQUEST_URI} [L,R=302]
might make it work.
Ah thanks! Yes, sorry I should've mentioned this is XenForo 2.

That doesn't make it work, but where can I find info about using things like _xfRedirect? I looked through the user manual and couldn't, where else should I be looking?
 
I feel this is a bad way to do it since the user can simply create the cookie with any value, and they have access.

Xenforo sessions should actually be tracked, with single application to application bridge (like Word<-->Xenforo), or something like Amember which can bridge several apps and control access to directories as well.
If this was sensitive information or similar, then yes, 100% agree.

In this instance though, it's just basic protection to discourage randoms from downloading the PDFs etc.
 
After all that today, I can't get the "simple" .htaccess redirect based on the cookie working, and further searching around on here has led me to implement a PHP script to properly use XenForo authentication to protect this directory.

However, I'm still having trouble getting the redirect back from the login to the original file download working.

This is all in my (not publicly available) test environment which lives in the xenforo_test directory.

This is the check_user.php file:
<?php

// Base XenForo directory
$dir = '<my XenForo directory>';

// Include autoloader and start
require($dir . '/src/XF.php');
\XF::start($dir);

$app = \XF::app();
$visitor = \XF::visitor();

// Check if the user is a guest
if ($visitor->user_id === 0) {
// Redirect to the login page if not authenticated
header('Location: /xenforo_test/index.php?login&_xfRedirect=' . urlencode($_SERVER['REQUEST_URI']));
exit;
}

// If authenticated, allow access to the requested file
vardump($file);
$file = $_GET['file'];
$filePath = '/downloads/' . basename($file);

if (file_exists($filePath)) {
header('Content-Type: application/pdf'); // Change to appropriate MIME type if needed
header('Content-Disposition: inline; filename="' . basename($filePath) . '"');
readfile($filePath);
} else {
http_response_code(404);
echo "File not found.";
}
?>
This is the .htaccess file:

RewriteEngine On

# Prevent rewriting the check_user.php script itself
RewriteCond %{REQUEST_URI} !^/downloads/check_user.php$

# Rewrite all other requests to check_user.php
RewriteRule ^(.*)$ check_user.php?file=$1 [L]

The behaviour I see is that trying to access one of the PDFs in the downloads directory when not logged in to XenForo correctly redirects me to the XenForo login page.

However, when logged in, I just keep seeing the login page that tells me I'm already logged in, and it never redirects me back to the original PDF I'm trying to access.

Any tips on where I'm going wrong here?
 
I haven't solved this yet, but I think I can now see where this is all going wrong, which is how I'm validating a user is actually logged in which determines if they are redirected to login or not.

I don't believe this check is valid:

if ($visitor->user_id === 0) {
Looking through the developer docs etc. now to see if I can figure the correct method out.
 
Ok, some more searching and fiddling and I'm now onto a winner, so for completeness here is the final check_user.php code:

<?php

// Base XenForo directory
$base_dir = '<base cPanel directory>';
$xf_dir = $base_dir . '/xenforo';
$dl_dir = $base_dir . '/downloads/';

// Include autoloader and start
require($xf_dir . '/src/XF.php');
\XF::start($xf_dir);

$app = \XF::setupApp('XF\Pub\App');
$app->start();
$visitor = \XF::visitor();

// Check if the user is a guest
if ($visitor->user_id === 0) {
// Redirect to the login page if not authenticated
header('Location: /xenforo/index.php?login&_xfRedirect=' . urlencode($_SERVER['REQUEST_URI']));
exit;
}

// If authenticated, allow access to the requested file

$file = $_GET['file'];
$filePath = $dl_dir . basename($file);

if (file_exists($filePath)) {
header('Content-Type: application/pdf'); // Change to appropriate MIME type if needed
header('Content-Disposition: inline; filename="' . basename($filePath) . '"');
readfile($filePath);
} else {
http_response_code(404);
echo "File not found.";
}
?>
The final .htaccess file:

RewriteEngine On

# Prevent rewriting the check_user.php script itself
RewriteCond %{REQUEST_URI} !^/downloads/check_user.php$

# Rewrite all other requests to check_user.php
RewriteRule ^(.*)$ check_user.php?file=$1 [L]

This now does exactly what I need.
 
Back
Top Bottom