I am working on a big project that has a laravel backend for API and a separate SPA (vue-cli scaffolded). they are found on the same top level domain the laravel project runs at the domain(m.m) and the Vue Spa runs at(vue.m.m:8080)
what my problem is:
- auth:sanctum protected routes does not work they return ({ "message" : "Unauthenticated" })
what works:
- route /api/login works perfectly when given the correct password and email (it sends a response ({"message" : "Login successful"}))
my code snippets:
.env (it is only the part that is needed based on the docs I cant show the other because of security issues)
BROADCAST_DRIVER=log
CACHE_DRIVER=file
QUEUE_CONNECTION=sync
SESSION_DRIVER=cookie
SESSION_LIFETIME=120
SESSION_DOMAIN=.m.m
SANCTUM_STATEFUL_DOMAINS=vue.m.m
backend
UserController => for Authenticating
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
class UserController extends Controller
{
public function register(Request $request)
{
$this->validator($request->all())->validate();
$user = $this->create($request->all());
$this->guard()->login($user);
return response()->json(['user'=> $user,
'message'=> 'registration successful'
], 200);
}
/**
* Get a validator for an incoming registration request.
*
* @param array $data
* @return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'string', 'min:4', 'confirmed'],
]);
}
/**
* Create a new user instance after a valid registration.
*
* @param array $data
* @return \App\User
*/
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
]);
}
protected function guard()
{
return Auth::guard();
}
public function login(Request $request)
{
$credentials = $request->only('email', 'password');
if (Auth::attempt($credentials)) {
// Authentication passed...
return response()->json(['message' => 'Login successful'], 200);
}
}
public function logout()
{
Auth::logout();
return response()->json(['message' => 'Logged Out'], 200);
}
}
sactum.php
<?php
return [
'stateful' => explode(',', env(
'SANCTUM_STATEFUL_DOMAINS',
'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1'
)),
'expiration' => null,
'middleware' => [
'verify_csrf_token' => App\Http\Middleware\VerifyCsrfToken::class,
'encrypt_cookies' => \Illuminate\Cookie\Middleware\EncryptCookies::class,
],
];
auth.php
<?php
return [
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false,
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'expire' => 60,
'throttle' => 60,
],
],
'password_timeout' => 10800,
];
User Model
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasFactory, Notifiable,HasApiTokens;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
}
api.php
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return "protected route";
});
Route::post("/login","App\Http\Controllers\UserController@login");
cors.php (for development purposes I allowed all incoming requests to be processed) and (credentials are set to true)
'paths' => ['*'],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => true,
kernel.php
protected $middleware = [
// \App\Http\Middleware\TrustHosts::class,
\Fruitcake\Cors\HandleCors::class,
\App\Http\Middleware\TrustProxies::class,
\Fruitcake\Cors\HandleCors::class,
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
];
'api' => [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
Frontend
axios
axios.defaults.withCredentials = true;
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
axios.defaults.baseURL = 'http://m.m/api';
Vue (this is the method that handles sending login requests)
login(evt) {
evt.preventDefault();
axios.get("/sanctum/csrf-cookie", { baseURL: "http://m.m" }).then(() => {
axios
.post(
"/login",
{ email: "[email protected]", password: "password" },
)
.then(() => {
const res = axios.get("/user");
console.log(res.data);
});
});
};
}
what I get from the browser network tab from chrome
this is what is stored in the browser as cookies (image)