Are Sanctum and Laravel's default auth the same if not used for tokens? Are Sanctum and Laravel's default auth the same if not used for tokens? laravel laravel

Are Sanctum and Laravel's default auth the same if not used for tokens?


Therefore if Tokens are never used, Sanctum is basically the same as the default Authentication method, am I correct?

Yes, under the hood it uses laravel's default auth.

Taking a look at the sanctum guard (below code taken fro github. It was last commited on Apr 11, sanctum 2.x)

<?phpnamespace Laravel\Sanctum;use Illuminate\Contracts\Auth\Factory as AuthFactory;use Illuminate\Http\Request;class Guard{    /**     * The authentication factory implementation.     *     * @var \Illuminate\Contracts\Auth\Factory     */    protected $auth;    /**     * The number of minutes tokens should be allowed to remain valid.     *     * @var int     */    protected $expiration;    /**     * Create a new guard instance.     *     * @param  \Illuminate\Contracts\Auth\Factory  $auth     * @param  int  $expiration     * @return void     */    public function __construct(AuthFactory $auth, $expiration = null)    {        $this->auth = $auth;        $this->expiration = $expiration;    }    /**     * Retrieve the authenticated user for the incoming request.     *     * @param  \Illuminate\Http\Request  $request     * @return mixed     */    public function __invoke(Request $request)    {        if ($user = $this->auth->guard(config('sanctum.guard', 'web'))->user()) {            return $this->supportsTokens($user)                        ? $user->withAccessToken(new TransientToken)                        : $user;        }        if ($token = $request->bearerToken()) {            $model = Sanctum::$personalAccessTokenModel;            $accessToken = $model::findToken($token);            if (! $accessToken ||                ($this->expiration &&                 $accessToken->created_at->lte(now()->subMinutes($this->expiration)))) {                return;            }            return $this->supportsTokens($accessToken->tokenable) ? $accessToken->tokenable->withAccessToken(                tap($accessToken->forceFill(['last_used_at' => now()]))->save()            ) : null;        }    }    /**     * Determine if the tokenable model supports API tokens.     *     * @param  mixed  $tokenable     * @return bool     */    protected function supportsTokens($tokenable = null)    {        return $tokenable && in_array(HasApiTokens::class, class_uses_recursive(            get_class($tokenable)        ));    }}

If you check the _invoke() method,

    if ($user = $this->auth->guard(config('sanctum.guard', 'web'))->user()) {        return $this->supportsTokens($user)                    ? $user->withAccessToken(new TransientToken)                    : $user;    }

the authenticated user is found using

$user = $this->auth->guard(config('sanctum.guard', 'web'))->user()

After checking the sanctum config file, there is no sanctum.guard config currently (it's probably meant for some future version), so sanctum checks with the web guard by default, so it's basically doing the same thing as your default web routes.

But you've misunderstood the use of Sanctum. Sanctum is for API authentication and not for web auth (though it can be used web auth as well). Sanctum's non-token auth is for your SPA's to be able to use the same API as mobile applications ( which use token authentication ) without needing tokens and providing the benefits of csrf and session based auth.

To help you understand better, suppose you have build an API which uses tokens (if it's already using sanctum for tokens, that makes things simpler) for authentication. Now you wish to build an SPA ( which may be build inside the laravel project itself, or a seperate project, on same domain or on different domain ) which will use the same API's, but since this will be built by you, it is a trusted site so you don't want it to use tokens but instead use laravel's default session based auth along with csrf protection while also using the same api routes. The SPA will communicate with the server through ajax. You also want to ensure that only your SPA is allowed to use session based auth and not allow other third party sites to use it.

So this is where Sanctum comes in. You would just need to add the Sanctum middleware to your api route group in app/Http/Kernel.php

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

Then configure sanctum to allow your SPA's domain and configure cors (check the docs to learn how to do this). Then just add the auth:sanctum middleware to your route and you're done with the serverside setup.

Now these routes will authenticate users if the request has a token or if it is stateful (session cookie).

Now your SPA can communicate with your API without tokens.

To get csrf protection, call the csrf-cookie request first, this will set up a csrf token in your cookies, and axios will automatically attach it to subsequent requests

axios.get('/sanctum/csrf-cookie').then(response => {    // Login...})

What is the difference between sanctum and passport since they do the same thing but Sanctum is said to be lightweight.

Well it's just like it says, sanctum is lightweight. This is because Passport provides full Oauth functionality while Sanctum only focuses on creating and managing tokens. To explain Oauth in a simple way, you must have seen those Sign in with Google, Sign in with Facebook, Sign in with Github on different sites, and you can then sign it to those sites using your google/facebook/github account. This is possible because Google, Facebook and Github provide Oauth functionality (just a simple example, not going in to too much detail). For most websites, you don't really need Passport as it provides a lot features that you don't need. For simple api authentication Sanctum is more than enough


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}.`);    }});


Sanctum has two separate authentication systems, there is the cookie based session authentication meant to use for single page applications, where you are sending api requests (ajax, fetch etc.) instead of server side rendering (which reads the session cookie on every page load), sanctum makes it possible to use this cookie (the default authentication) without page reloads.

The second authentication system is token based and is meant to use for mobile applications.