Search code examples
laravellaravel-routinglaravel-8laravel-middleware

Subscriber middleware route allowing public to view all restricted pages


I'm having trouble with routes and middleware in my project. I have a list of topics that are locked to subscribers only. However, topics can be marked as public, allowing guest users / the public to view the topic.

The issue I'm facing is I either get all topics visible to the public or none.

I have the following Subscriber middleware.

public function handle(Request $request, Closure $next)
{
    if ( $request->user() && ! $request->user()->subscribed('annual_membership') ) {
        return redirect('profile');
    } 
        
    if( Auth::Guest() ) {
        return redirect('topics')->with('error', 'You need to be a registered memeber to view this topic.');
    }

    return $next($request);
}

This is added to my kernel like this.

protected $routeMiddleware = [
    // ...
    'subscriber' => \App\Http\Middleware\Subscriber::class,
];

I then have the following routes set up.

Route::get('voting-topics', [TopicController::class, 'topics'])->name('topics');
Route::get('voting-topics/{topic}', [TopicController::class, 'topic'])->name('topic');

Route::group(['middleware' => ['subscriber']], function () 
{
    Route::get('profile/billing', function (Request $request) {
        return $request->user()->redirectToBillingPortal();
    });

    Route::get('voting-topics/{topic}', [TopicController::class, 'topic'])->name('topic');
});

And my controller for the topics.

class TopicController extends Controller
{
    public function topics(Request $request) 
    {
        if ( $request->user() && $request->user()->subscribed('annual_membership') ) {
            return $this->subscriberTopics();
        } else {
            return $this->publicTopics();
        }
    }
    
    
    private function publicTopics() 
    {
        $topics = Topic::orderBy('date', 'asc')->where('free_to_view', 1)->paginate(50);

        return view('topics.topics', compact('topics'));
    }
    
    private function subscriberTopics() 
    {
        $topics = Topic::orderBy('date', 'desc')->paginate(50);

        return view('topics.topics', compact('topics'));
    }

    public function topic(Request $request, Topic $topic) 
    {
        $topic = Topic::find($topic)->first();

        return view('topics.topic', compact(['topic',));
    }
}

The Topics directory works fine. It's only showing ones marked as public for guests and all topics for subscribers. My code currently only allows subscribers to view all topics, and guest can see the public ones, which isn't what I want.

If I remove the middleware route, the opposite happens guest can access all topics if they were to type in the URL of the topic.

EDIT

To clarify and explain a little better, I have a list of topics (basically like blog posts). Topics are only available to view for paying subscribers to my website. However, some topics can be marked as free to view, and they will be visible to everyone.

If I go to example.com/voting-topics, then I only see free topics. (this is correct and working) If I log in and go to the same URL, I see all the topics because I'm logged in, and I'm a paying subscriber. (This again is correct and working)

If I remove the duplicate route in my middleware, then go to example.com/voting-topics/123 or any other topic id direct via the URL. I can view the topic even though it should be locked to subscribers only.

If I add the duplicate route back into my middleware, the general public can't access the free topics.

I need the guest / general public / non-subscribers to only access topics that are marked as public but paying subscribers can access all of them. `


Solution

  • So it seems it was a simple fix after all, rather than do checks in my Routes I needed to do them in my controller.

    I removed the Route::get('voting-topics/{topic}', [TopicController::class, 'topic'])->name('topic'); from my middelware and then added the following code in my controller.

    if( $topic->free_to_view === 1 || Auth::check() && auth()->user()->subscribed('annual_membership')) {
        return view('topics.topic', compact('topic'));
    
    } else {
    
        return redirect()->back()->with('error', 'You need to be a paying member to view this topic');
    
    }