NOTE: This answer is for Laravel Sanctum + same-domain SPA
To add to these answers, the default Laravel auth uses the web guard, so you must use that for your auth routes (for same-domain SPA app).
For example, you can create your own routes that point to Laravel's RegistersUsers trait and AuthenticatesUsers trait:
web.php
Route::group(['middleware' => ['guest', 'throttle:10,5']], function () {
Route::post('register', 'Auth\RegisterController@register')->name('register');
Route::post('login', 'Auth\LoginController@login')->name('login');
Route::post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail');
Route::post('password/reset', 'Auth\ResetPasswordController@reset');
Route::post('email/verify/{user}', 'Auth\VerificationController@verify')->name('verification.verify');
Route::post('email/resend', 'Auth\VerificationController@resend');
Route::post('oauth/{driver}', 'Auth\OAuthController@redirectToProvider')->name('oauth.redirect');
Route::get('oauth/{driver}/callback', 'Auth\OAuthController@handleProviderCallback')->name('oauth.callback');
});
Route::post('logout', 'Auth\LoginController@logout')->name('logout');
But make sure they are in the web.php file. For example if they are in the api.php file, I saw some weird errors about session store not on request and RequestGuard::logout() is not a function. I believe this has something to do with the default auth guard via $this->guard() in the auth traits, and something to do with api.php's /api prefix.
The /api prefix seemed related because if you use the composer package Ziggy to achieve route('login') and route('logout'), they actually resolve to /api/login and /api/logout.
I suspect that caused an issue with Sanctum. The fix was to make sure the routes were in web.php. A person may reproduce this error if their config is similar, or for example, if they have Auth::routes() declared in api.php.
Double check your Kernel.php (it should be like this):
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
EnsureFrontendRequestsAreStateful::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
'throttle:60,1',
],
];
If you have StartSession in your api middleware group, your config is incorrect or unnecessarily-convoluted.
Here is my working ./config/auth.php file for your comparison:
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
...
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false,
],
],
Then, you can use the guest middleware for login/registration routes, and very importantly, you should then declare all your JSON-serving endpoints in api.php and use the auth:sanctum middleware on those routes.
Once you think you have it working, I have two test/debug steps for you:
One:
- open Chrome > dev tools pane
- goto the Applications tab
- check to ensure there are two cookies:
<app_name>_session, and XSRF-TOKEN
- with the remember me checkbox and
remember: true in login payload, ensure there is a third cookie for remember_web_<hash>
- make sure the session cookie is
httpOnly, and make sure the CSRF cookie is not (so your JavaScript can access it)
Two, in your unit tests, ensure that after $this->postJson(route('login'), $credentials), you see this:
Auth::check() should return true
Auth::user() should return the user object
Auth::logout() should log the user out, and immediately following that, $this->assertGuest('web'); should return true
Don't get too excited until you verify those two steps, and do get excited once you successfully verify those steps. That will mean you are using Laravel's default auth logic.
For good measure, here is an example of attaching the CSRF token via JavaScript:
import Cookies from 'js-cookie';
axios.interceptors.request.use((request) => {
try {
const csrf = Cookies.get('XSRF-TOKEN');
request.withCredentials = true;
if (csrf) {
request.headers.common['XSRF-TOKEN'] = csrf;
}
return request;
} catch (err) {
throw new Error(`axios# Problem with request during pre-flight phase: ${err}.`);
}
});
Sign In with GoogleandSign in with Facebookright? Those are possible because google and facebook support oauth), sanctum does not provide Oauth. If you don't know what Oauth is, you probably don't need it and just use sanctum - Arun A S