Lifetime access is $100 off. Ends this week.Join here →

Problem with Laravel Sanctum

I have my api using laravel sanctum api for auth and in my api routes i have this

Route::get('/blog/{id}', BlogController::class);

and in my controller i have this

public function __invoke(Request $request)
{
   $blog = Blog::where('id', $request->id)->first()

	return response->json([
		'blog' => $blog
		'user' => $request->user()
	]);
}

The problem is that i do not know why the user is null in my nextjs application even though i have logged in.

also if i add sanctum auth middleware to the route i am not able to access the page in my nextjs application.

any help to solve this will be appreciated

Note: i have also done this auth()->user() and auth()->id() still null

henry75958
henry75958
0
21
200
alex
alex
Moderator

Could you post your .env file with just the Sanctum config you've added (if any)?

Also, is your Nuxt app using the same domain as your API?

henry75958
henry75958

I have thus in my .env

APP_URL=http://localhost:8000
FRONTEND_URL=http://localhost:3000


SESSION_DRIVER=database
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null

i am using laravel 11 so inside config/sanctum.php

i have this


    'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
        '%s%s%s',
        'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
        Sanctum::currentApplicationUrlWithPort(),
        env('FRONTEND_URL') ? ','.parse_url(env('FRONTEND_URL'), PHP_URL_HOST) : ''
    )))
alex
alex
Moderator

Thanks. So from the docs, the following isn't working for you?

also if i add sanctum auth middleware to the route i am not able to access the page in my nextjs application.

->withMiddleware(function (Middleware $middleware) {
    $middleware->statefulApi();
})
henry75958
henry75958

I haven't tried that yet, what i did was just

Route::get('/blog/{id}', BlogController::class)->middleware(['auth:sanctum']);

and that redirects the user even though they are logged in

alex
alex
Moderator

Here's a direct link. Let me know if this doesn't work and I'll set up a local project (but with Vue) and see if there's anything that might be missing.

I need to update the Sanctum course anyway, so will be super helpful.

https://laravel.com/docs/11.x/sanctum#sanctum-middleware

henry75958
henry75958

Hi Alex, so i did that was in the doc

->withMiddleware(function (Middleware $middleware) {
        $middleware->api(prepend: [
            \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
        ]);

        $middleware->statefulApi();

        $middleware->alias([
            'verified' => \App\Http\Middleware\EnsureEmailIsVerified::class,
            'onboarding' => \App\Http\Middleware\OnboardingRedirect::class,
            'subdomainAuthorization' => \App\Http\Middleware\SubdomainAuthorization::class,
        ]);

Controller

class DashboardController extends Controller
{


    public function __invoke(Request $request)
    {

        $blog = Blog::where('id', $request->id)->first();
        return response()->json([
            'blog' => $blog,
            'user' => $request->user(),
            'message' => 'Dashboard'
        ], 200);
    }
}

api route

Route::get('/blog/{id}', DashboardController::class);

and in nextjs i did this

export async function getSubdomain(id) {

    try {
    
        const response = await axios.get(`/api/blog/${id}`);

        console.log(response)
        return response.data.blog;
    } catch (error) {
     console.log(error)
    }
}

i still see null

alex
alex
Moderator

No worries, I’ll get a repo set up today and will send it over, since I’ve not worked for it for a while.

With Sanctum, small things are so easy to miss ha.

henry75958
henry75958

Cheers Alex, looking forward to that

arpit
arpit

Try setting up

SANCTUM_STATEFUL_DOMAINS=localhost:3000 // replace this with front-end side url
SESSION_DOMAIN=localhost

in .env

henry75958
henry75958

I already did that and i posted in my previous response above. :)

alex
alex
Moderator

I've spent some time re-familiarising myself with the Sanctum setup, so here's a full working repo for authentication with Sanctum and Vue.

This will be a course shortly, so you'll be able to follow along there for a full explanation too.

Let me know if you're still struggling!

https://github.com/alexgarrettsmith/laravel-sanctum-vue

Notable parts from the .env file (not commited)

SESSION_DOMAIN=laravel-sanctum-vue-api.test
SANCTUM_STATEFUL_DOMAINS=laravel-sanctum-vue-api.test:5173

The client/api are running on exactly the same domain locally, just with a different port.

henry75958
henry75958

Hi @Alex thanks for this.

I will check it out and update you shortly

Quicky question though i noticed you used Fortify just out of curiosity why didn't you use Breeze??

alex
alex
Moderator

You can use Breeze, but since it just defines some basic controllers I’d always recommend Fortify for the additional functionality.

Are you using Breeze to authenticate using Sanctum?

henry75958
henry75958

Yeah i am using Breeze, but i will check out what you've provided and use Fortify.

I am currently trying to read the Laravel docs to know the difference. :)

alex
alex
Moderator

Awesome. Good luck and let me know how it goes!

henry75958
henry75958

Hi Alex, i tried it last night, the authentication works and all. but i still have the same problem for some weird reason, if i add sanctum middleware to my show route i keep getting redirected even though i have been authenticated.

Makes me wonder if the auth:sanctum middleware does not support show method in controller??

My application is just about user authenticating and creating a blog and i want to attach middleware to stop user that's authenticated from accessing blog page that does not belong to them.

Cheers Alex

alex
alex
Moderator

Would you mind posting what you see in your network tab? e.g. the response codes, response or anything relevant where the redirect happens? Thanks!

henry75958
henry75958

Hi Alex,

in my route i did this

Route::get('/{publication}/dashboard', [DashboardController::class, 'show']);

in my controller i did this

/**
     * Display the specified resource.
     */
    public function show(Request $request, Publication $publication)
    {

        return response()->json([
            'publication' => $publication,
            'user' => $request->user(),
            'message' => 'Dashboard'
        ], 200);
    }

and i see this

{
  "publication": {
    "id": "9c095ff7-8d30-43ea-bfee-f063a27cba07",
    "subdomain": "henry",
    "publication_name": null,
    "about": null,
    "twitter": null,
    "instagram": null,
    "website": null,
    "facebook": null,
    "linkedin": null,
    "header_color": "#ffffff",
    "user_id": 1,
    "created_at": "2024-05-13T22:53:06.000000Z",
    "updated_at": "2024-05-13T22:53:06.000000Z"
  },
  "user": null,
  "message": "Dashboard"
}

if i put auth:sanctum middleware in my routes Route::get('/{publication}/dashboard', [DashboardController::class, 'show'])->middleware('auth:sanctum'); it redirects to /dashboard even though i already been authenticated.

alex
alex
Moderator

Are you using any other middleware in your DashboardController ?

henry75958
henry75958
Solution

Hi Alex, after spending time going through it, i have been able to solve it. The problem was how i was getting the data with Nextjs.

So i used server components to get the data using axios and the problem was that i was using another server component to display the response, which for some reason wasn't working.

When i switched to using client component by adding 'use client' and running the process it works

so here is my final code

i have hooks/fetchPublication

import axios from "@/lib/axios"

export const useDomain = () => {

    const csrf = () => axios.get('/sanctum/csrf-cookie')
    const getPublication = async ( props ) => {
        try{
            await csrf()

            const res = await axios
                .get(`api/${props.id}/dashboard`)
            

            return res.data


        }catch(error){
           console.log(error)

        }
    }
  return {
        getPublication
    }
}

and in my client component i did this

'use client'

import { useAuth } from "@/hooks/auth"
import Header from "./components/Header"
import { useDomain } from "@/hooks/fetchPublication"

export default function Layout({params,  children }) {
    
    const { user } = useAuth({ 
        middleware: 'auth',
    })
    
    const { id } = params
    const { getPublication } = useDomain()
    const  data  =  getPublication({id})

    if (!data) {
        return redirect('/');
    }
 return (
        <div>
            <Header user={user} />

            {children}
        </div>
    )
}

that works.

Thanks for your help mate.

alex
alex
Moderator

Phew, I’m so glad you solved it!

Any time!