Laravel sanctum unauthenticated issue on Next js SPA, how to solve it?

1.3k Views Asked by At

I have read too many solutions but still not solved my issue. I am using Laravel (sanctum) in backend and Next js in front-end.

Laravel version: 9 Backend Url: http://127.0.0.1:8000 Front-End url: http://localhost:3000

Cors.php

    'paths' => ['api/*', 'sanctum/csrf-cookie','login','logout',],

    'allowed_methods' => ['*'],

    'allowed_origins' => ['http://127.0.0.1:3000'],

    'allowed_origins_patterns' => [],

    'allowed_headers' => ['*'],

    'exposed_headers' => [],

    'max_age' => 0,

    'supports_credentials' => true,

Karnel.php

  */
    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'api' => [
             \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
            'throttle:api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
    ];

config/sanctum.php


    'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
        '%s%s',
        'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
        Sanctum::currentApplicationUrlWithPort()
    ))),

    /*
    |--------------------------------------------------------------------------
    | Sanctum Guards
    |--------------------------------------------------------------------------
    |
    | This array contains the authentication guards that will be checked when
    | Sanctum is trying to authenticate a request. If none of these guards
    | are able to authenticate the request, Sanctum will use the bearer
    | token that's present on an incoming request for authentication.
    |
    */

    'guard' => ['web'],

.env

SESSION_DRIVER=cookie
SANCTUM_STATEFUL_DOMAINS=localhost:3000
SESSION_DOMAIN=localhost

.htaccess file on public folder

<IfModule mod_rewrite.c>
    <IfModule mod_negotiation.c>
        Options -MultiViews -Indexes
    </IfModule>

    RewriteEngine On

    # Handle Authorization Header
    RewriteCond %{HTTP:Authorization} .
    RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

    # Redirect Trailing Slashes If Not A Folder...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_URI} (.+)/$
    RewriteRule ^ %1 [L,R=301]

    # Send Requests To Front Controller...
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ index.php [L]
</IfModule>

Route/Api.php


Route::get('/user', [AuthController::class, 'me'])->middleware('auth:sanctum');

AuthController

public function me(Request $request)
{
  return response()->json([
    'status' => 'success',
    'message' => 'Data Fetch Success',
    'data' => [
        "name" => "Zahid Hasan Raju",
    ],
  ]);
}

Axios Request from

import Head from "next/head";
import axios from "axios";

export default function Home() {
 
  const getConfig = {
    method: "get",
    url: "http://localhost:8000/api/user",
    withCredentials: true,
  };



  const handleClick = async () => {
    await axios(getConfig).then((res) => console.log(res.data));
    alert("success");
  };

  return (
    <>
      <Head>
        <title>Home Page</title>
      </Head>
      <h1>Home Page</h1>
      <button className='btn btn-success' onClick={handleClick}>
        Fetch user
      </button>
    </>
  );
}

Result

result

My expectation SPA is authenticated using Sanctum from backend, so i need to show some data in frontend without login.

when i logged in , then i can get session cookie / Barer token for an user. Using this Barer token or session i can get data and do everything.

Without login it's saying ""Unauthenticated.""

suppose i have a list of product in my database, and i want to show the product list from backend to front-end without login, then how can i get the data list?

1

There are 1 best solutions below

2
On

I'm not sure if this is the best approach, but I always just create another middleware that can handle both authenticated and un-authenticated request with working with sanctum since the auth:sanctum would just redirect to login or return un-authenticated message if call via api and you use it.

create your middle first php artisan make:middleware GuestOrAuth

then use Auth facades to handle the authentication if the request contains bearer token

something like below

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

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

        if ($request->bearerToken()) 
            Auth::setUser( Auth::guard('sanctum')->user() );
            
        return $next($request);
    }
}

then on Kernel.php add that middleware under $routeMiddleware

protected $routeMiddleware = [
    .
    .
    .
    'guest.or.auth' => \App\Http\Middleware\GuestOrAuth::class
];

Then you'd be able to use like below which return the user if they are logged-in and return anything you want if not logged-in

Route::middleware(['guest.or.auth'])->get('/test', function() {
    return auth()->user() ?? 'Not logged-in!';
});