Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Ap3twe's avatar

Making a route public for anyone to view

I want to allow a route to be public for anyone who has the URL to view. It is coming from protected routes from the admin. The admin creates the pages and the final page we would send it to a doctor who will also send it to the patients. How can I create a public URL from Route::get('/smiledesign/{project}/doctorfinalform', 'ProjectsController@doctorfinalform');

The view is like this http://blog.test/smiledesign/4/doctorfinalform

Here are my routes

  Auth::routes();

// Users Routes
Route::get('/home', 'HomeController@index')->name('home');
Route::get('/smiledesign/create', 'ProjectsController@create');
Route::post('/store', 'ProjectsController@store');
Route::get('/smiledesign/{project}/show', 'ProjectsController@show');
Route::get('/smiledesign/success', 'ProjectsController@success');
Route::get('/smiledesign/cases', 'ProjectsController@cases');

// Doctor final form
Route::get('/smiledesign/{project}/doctorfinalform', 'ProjectsController@doctorfinalform');

// Admin redirect to dashboard
Route::get('/admin', 'HomeController@admin')->middleware('admin');

// Admin Routes
Route::middleware('forStaff')->group(function () {
Route::get('/smiledesign/adminforms', 'ProjectsController@adminforms');
Route::get('/smiledesign/{project}/records', 'ProjectsController@records');
Route::patch('/smiledesign/{project}', 'ProjectsController@update');
Route::get('/smiledesign/{project}/edit', 'ProjectsController@edit');
Route::delete('/smiledesign/{project}', 'ProjectsController@destroy');
Route::get('/smiledesign/{project}/userproject', 'ProjectsController@userproject');
Route::get('/smiledesign/published', 'ProjectsController@published');
});

 // Published page
    public function published(project $project){
        $project = Project::where('user_id', auth()->id())->find(session('created_id'));
        return view ('/smiledesign.published', compact('project'));

    }

    public function doctorfinalform(Project $project, request $request){

        return view('smiledesign.doctorfinalform', compact('project'));
    }
0 likes
31 replies
tisuchi's avatar

The route seems public already. Make sure, there is no authentication checking in the __construct() method in your ProjectsController.

Ap3twe's avatar

@I need the authentication in the ```__contruct()`` or else all the pages will be public. I want only the doctorfinalform to be public

tisuchi's avatar

@AP3TWE - In that case, you can make an auth middleware group in your routes. No need to use __construct() .

Snapey's avatar

If you want to use middleware in the constructor, you can use

        $this->middleware('auth')->except('doctorfinalform');

But something worries me about your code. What is to stop the patient from just changing the {project} in the URL and seeing other peoples forms?

jlrdw's avatar

If there is a chance of that, I'd have it in an authenticated route as well and verify with Auth::user()->id

1 like
Ap3twe's avatar

@SNAPEY - @snapey That is what I was thinking. I have to make a decision to leave that out. Security is important. I will limit it to the doctor

Ap3twe's avatar

@JLRDW - I created a middleware. Yet all users can see each other project. What condition do I have to provide?

Route::get('/smiledesign/{project}/doctorfinalform', 'ProjectsController@doctorfinalform')->middleware('finalForm');

<?php

namespace App\Http\Middleware;

use Closure;

class FinalForm
{
    
    public function handle($request, Closure $next)
    {

        if(auth()->user()->id){
           
        }
        return $next($request);
    }
}
jlrdw's avatar

When you say doctor and form, you're not letting other patients see another patients information are you.

As I said that's what the authenticated user ID comes in.

If the authenticated users ID doesn't match the id in the record then they don't need to be viewing it..

On mobile now I will go to laptop.

EDIT: On laptop

In your

public function handle($request, Closure $next)
    

As long as an id such as user_id or patient_id has to match the auth()->user()->id.

If no match then access not allowed. Have you tested?

Say you had a url like:

http://localhost/laravel58/pet/edit?petid=47

See the petid? Here a user can only see their pets. What stops me from entering another petid? Like

http://localhost/laravel58/pet/edit?petid=5

Well the Auth::user()->id

if ($user_id === Auth::user()->id) {
            // in my case I actually use ownerid
            //allow this request
        } else {
           // redirect somewhere
        }

See this post and some of the links in it.

https://laracasts.com/discuss/channels/laravel/multi-auth-login-with-single-table-user-using-middleware-in-laravel

RBAC has several phases.

  • Authentication - A login is required
  • Authorization - What can the logged in user do and not do.
  • Enabling user to see and edit their data
  • Blocking others from users data.

Just because a user is logged in with a role of bookkeeper, that doesn't mean they can access all methods.

And if bob can edit his post, that doesn't mean he can edit another persons.

Which is where the Auth::user()->id comes into play.

Auth::user()->id has to match an id either in current table (no relations), or child table if dealing with relations.

I have some custom code in examples (helpers), but derive your own logic. Remember ust examples.

Going back to my pet example:

$pet = Pet::find($petid);
$ownerid = $pet->ownerid;
if ($ownerid != Auth::user()->id) {
            return redirect('somewhere_you_decide');
        } 
//  No redirect, passed so continue the method

Just one example, but there has to be a match. Me changing it to 5 did not match, that's someone else's pet.

Snapey's avatar

you can provide some protection by using uuid for the project id, but really all patients should be authenticated

jlrdw's avatar

I agree you cannot play around with the security when you are talking about patients.

Ap3twe's avatar

@SNAPEY - All doctors are protected. The problem I have is I am taking the form submitted by the doctor and updating it with new contents and sending it back to him. That makes it that the route is coming from protected admin and then releasing access back to the doctor. If I release access as public routes, anybody can see each other project. I am gonna protect it with the suggestion from @jlrdw

Ap3twe's avatar

I get error Trying to get property 'user_id' of non-object

    public function handle($request, Closure $next)
    {
 
        $project = Project::find('id');
        $user_id = $project->user_id;
        if($user_id != auth()->user()->id){
            return redirect('/')->with('error', 'You are not able to see this page');
 
        }
 
 
        return $next($request);
    }
}
Cronix's avatar
$project = Project::find('id');

That should actually be the integer id of the project you're trying to load. The string id won't do anything...

It's basically saying select * from projects where projects.id = id the way you have it.

jlrdw's avatar

@AP3TWE - Good to see you're taking this very seriously please let us know the final solution and if something worked out for you or not.

Usually this stuff just requires a little trial and error and experimentation to make sure it all is correct.

Once you have solution make sure you thoroughly test and make sure someone else cannot get into that patients information.

Make sure you see Cronix answer above he knows laravel very well.

1 like
Ap3twe's avatar

@CRONIX - Replacing the string with integer throws an error Use of undefined constant id - assumed 'id' (this will throw an Error in a future version of PHP)

Cronix's avatar

I'm not sure what you did, but

$project = Project::find(4);

should not cause that error, with 4 being the id of the project you're retrieving.

jlrdw's avatar

You could probably do this check right in the controller, me I like doing it in a query scope.

@snapey is the one who got me hooked on query scopes they work wonderfully.

All you're doing is making sure an ID that you're using in the query matches up with the authorized users ID I imagine this case it would be the patient.

Unfortunately I'm busy it will be a while before I get to laptop.

Ap3twe's avatar

@CRONIX - I want it to be dynamic. Assigning 4 is hard coded. I want to block other users from seeing the form.

Cronix's avatar

Yes, of course. I'm not sure how you're passing it into the middlware. Im just pointing out the reason for your error.

Trying to get property 'user_id' of non-object

That is because your $project query isn't returning the result, and you assume it does because you directly try to use it here $user_id = $project->user_id; which is why you get the error for user_id of non-object. $project is null so you can't do null->some_property

Ap3twe's avatar

This is my middleware

<?php

namespace App\Http\Middleware;
use App\Project;

use Closure;

class FinalForm
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */


    public function handle($request, Closure $next)
    {

        // $project = Project::where('user_id', auth()->id())->get();
        $project = Project::find('id');
        $user_id = $project->user_id;
        if($user_id != auth()->user()->id){
            return redirect('/')->with('error', 'You are not able to see this page');

        }


        return $next($request);
    }
}

Controller

    // Published page
    public function published(project $project){
        $project = Project::where('user_id', auth()->id())->find(session('created_id'));
        return view ('/smiledesign.published', compact('project'));

    }

    public function doctorfinalform(Project $project, request $request){

        // $this->authorize('doctorfinalform', $project);

        return view('smiledesign.doctorfinalform', compact('project'));
    }

Route

Route::get('/smiledesign/{project}/doctorfinalform', 'ProjectsController@doctorfinalform')->middleware('finalForm');
Snapey's avatar

$project = Project::find('id');

Where do you think 'id' comes from (even if you changed it to $project = Project::find($id);

Snapey's avatar
Snapey
Best Answer
Level 122

Is the doctor the user?

Don't mess with middleware. There are better methods using Authorization https://laravel.com/docs/5.8/authorization

But to do simply, in your controller;

    public function published(project $project)
    {
        abort_if(! $project->user_id == auth()->id(), 403);
    
        return view ('/smiledesign.published', compact('project'));

    }
Ap3twe's avatar

@SNAPEY - I tried your solution. The route does not abort. I dump it and it returns null

$bbd  =  abort_if(! $project->user_id == auth()->id(), 403);
        dd($bbd);
Ap3twe's avatar

@SNAPEY - Sorry it is working when I changed abort_if(! $project->user_id == auth()->id(), 403); to abort_if( $project->user_id !== auth()->id(), 403);

Ap3twe's avatar

How do I give access to the admin too? How do I write the condition by adding isAdmin? auth()->user()->isAdmin

jlrdw's avatar

you have to check the role and see if they are admin.

That's why I mentioned scopes because one query for admin, yet another condition if it's the patient.

Edit: A scope works like this, just a basic example. Also I have my own helpers, you need your own logic to determine if user is an admin. If single roles it's easy, role can be put in users table. However multiple roles are more tricky.

The basic scope looks like:

    public function scopegetPets($query, $petsearch = '')
    {
        $petsearch = $petsearch . "%";
        $query->where('petname', 'like', $petsearch);
        if (ChkAuth::userRole('admin') === false) {  // IGNORE this part. Custom code
            $userid = Auth::user()->id;
            $query->where('ownerid', '=', $userid);
        }
        $results = $query->orderBy('petname', 'asc')->paginate(5);
        return $results;
    }

The part to ignore could be something like:

 if (Auth::user()->role === 'admin') {

Or

if (! auth()->user()->isAdmin()) {

It all depends on how you setup your roles.

But the basics of the example scope is this part:

$userid = Auth::user()->id;
$query->where('ownerid', '=', $userid);

is used when the logged in user IS NOT an admin. That is if an admin can see all records.

You may want to look at a good package many laravel users use:

https://github.com/spatie/laravel-permission

Where it's fairly easy to check a role, from one of their example:

Auth::user()->hasRole('admin')

I basically do the same using in_array, just example here to show how I check a role:

    public static function chkRole($role = null)
    {
        $userrole = Auth::user()->role;
        $checkrole = explode(',', $userrole);
        if (in_array($role, $checkrole)) {
            return true;
        }
        return false;
    }

But again that's is just example, you need to first learn laravel authorization well before attempting custom helpers. Or decide to give Spatie a try.

This stuff is a learning curve. Jeffrey states in a video, it is tricky until you start learning it.

And sorry I don't have a better example, as I have helpers to work with authentication, but just take the time to learn.

All RBAC is basically doing the same, just a little different on how it's implemented.

Just remember, a user can or cannot do something is the main goal. Depending on role and permissions.

In my opinion, you should really learn and use scopes. You could even have separate scopes for user vs admin, it's up to you. Just me, I combine in one when I can.

Have you viewed Jeffrey's free videos on Authentication and Authorization. It's in the

https://laracasts.com/series/laravel-from-scratch-2018 series.

Next

Please or to participate in this conversation.