Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I'm not quite sure about what is meant in the Laravel documentation, so I'm asking to be sure.

We have the default authentication of Laravel on one side and Sanctum on the other.

It is stated that Sanctum can either do Tokens or simply implement auth. :

For this feature, Sanctum does not use tokens of any kind. Instead, Sanctum uses Laravel's built-in cookie based session authentication services. This provides the benefits of CSRF protection, session authentication, as well as protects against leakage of the authentication credentials via XSS. Sanctum will only attempt to authenticate using cookies when the incoming request originates from your own SPA frontend (Vue.js).

Therefor if Tokens are nevers used, Sanctum is basically the same as the default Authentication method, am I correct? Basically, does it implement the default authentication and add tokens if needed on top of that? If so, what is the difference between sanctum and passport since they do the same thing but Sanctum is said to be lightweight. What does that actually mean?

Thanks for reading

like image 302
Pouissante Avatar asked Apr 22 '20 19:04

Pouissante


People also ask

What is Auth Sanctum?

Introduction. Laravel Sanctum provides a featherweight authentication system for SPAs (single page applications), mobile applications, and simple, token based APIs. Sanctum allows each user of your application to generate multiple API tokens for their account.

What is the difference between Laravel Passport and Sanctum?

Chatty Cathy @vincent15000 Passport is an OAuth server implementation, and used to offer OAuth authorisation for your application. Sanctum is an authentication library for “simpler” token-based authentication for clients that need it (i.e. mobile apps) but also offers cookie-based authentication for SPAs.

Does Laravel sanctum use JWT?

Laravel JWT authentication vs. Sanctum offers both session-based and token-based authentication and is good for single-page application (SPA) authentications. Passport uses JWT authentication as standard but also implements full OAuth 2.0 authorization.

What is Laravel sanctum Token Authentication and how does it work?

By default, Laravel Sanctum token authentication will either completely block access to protected routes or allow it at the expense of not being able to detect if the user is logged in. However, there are valid scenarios whereby a route should be accessible to guests and at the same time allow bearer token authentication.

Does sanctum use tokens to authenticate?

For this feature, Sanctum does not use tokens of any kind. Instead, Sanctum uses Laravel's built-in cookie based session authentication services. Typically, Sanctum utilizes Laravel's web authentication guard to accomplish this.

Does Laravel use cookies for authentication?

Both Sanctum's "SPA Authentication" and Laravel's default authentication use cookies because they're easy to use and browsers offer extra security features you can't get with tokens. If your SPA and API are on different domains you can't use cookies.

Which authentication Guard does Laravel use for spa?

It uses an authentication guard when performing the SPA authentication. By default, the web guard is used as per the configuration file. The same guard is used in the default Laravel request authentication as well. If your web app has multiple panels (say, admin, manager, customer, etc.), you'd be using multiple authentication guards.


Video Answer


3 Answers

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)

<?php

namespace 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

like image 109
Arun A S Avatar answered Oct 20 '22 13:10

Arun A S


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}.`);
    }
});
like image 30
agm1984 Avatar answered Oct 20 '22 14:10

agm1984


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.

like image 1
CleanCode Avatar answered Oct 20 '22 13:10

CleanCode