laravellaravel-5eloquent

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 authentication:

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).

Therefore if Tokens are never 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?


Solution

  • 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