Search code examples
phplaravellaravel-8laravel-sanctumsanctum

Laravel Authentication API Sanctum – With Custom Database


First important information: I’m new to Laravel, so your patience is appreciated.

I’m currently migrating a framework of mine to Laravel and still in the early stages. I know that Laravel has it’s own database construction mechanism that is recommended to use the migrations and the Models, however, for my purpose, I’d like to use my own database that I use in other systems that I’ve built in the past. The idea for this system is to have a shared database, but operable through different tech stacks.

This is my current scenario:

  • Laravel 8
  • Sanctum 2.14

Frontend (Laravel):

  • I’ve built a very simple login page that has a controller and sends data (user and password) to my backend (Laravel). In the backend (different server), I grab the data and check if the data is correct. Being correct, I send a json response with some data, like:
returnStatus = true
loginVerification = true
IDCrypt = asdfasd4fa654sd54a (encrypted ID to grab in the frontend again)

Up till here, it’s working fine, as I wanted and very similar to my legacy systems.

My idea would be to get this response in the frontend, via auth token managed by Sanctum and use a middleware to check the token in order to let the user access some web routes.

I’ve watched some videos, but I’m only finding videos that use all the database mechanism that Laravel provides.

However, my intention would be to generate the token with data from my own table and data objects I created (without any existing Laravel´s models).

  • Is there a way for me to do this?
  • How would I set the token in the backend and include in my response?
  • How would I grab the token in the frontend in a secure way?

Edit – Progress on creating the Legacy User.

I´m following @gavin´s suggestion. So far, my Legacy User looks like this:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Sanctum\HasApiTokens;

class LegacyUser extends Authenticatable
{
    use HasFactory, HasApiTokens;


    /**
     * Build content placeholder body.
     * @return array
     */
    public function cphBodyBuild($oudRecordParameters): array
    {
        // Variables.
        // ----------------------
        $arrReturn = ['returnStatus' => false];
        // ----------------------

        // Logic.
        try {
            // Build object - details.
            if ($this->oudRecordParameters !== null) {
                $oudRecord = new \SyncSystemNS\ObjectUsersDetails($oudRecordParameters);
                $arrReturn['oudRecord'] = $oudRecord->recordDetailsGet(0, 1);

                if ($arrReturn['oudRecord']['returnStatus'] === true) {
                    $arrReturn['returnStatus'] = true;
                }
            }
        } catch (Error $cphBodyBuildError) {
            if ($GLOBALS['configDebug'] === true) {
                throw new Error('cphBodyBuildError: ' . $cphBodyBuildError->message());
            }
        } finally {
            //
        }
        
        return $arrReturn;
    }
}

I´m not sure how to override the methods he mentioned (createToken and tokens). As of now, when I use:

$userLogin = new LegacyUser();
$userLoginData = $userLogin->cphBodyBuild($param);
$userLoginToken = $userLogin->createToken('testing_token')->plainTextToken;

It logs the following error:

[previous exception] [object] (PDOException(code: 23000): SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'tokenable_id' cannot be null at …
[stacktrace]

Edit – Developed createToken method.

After further research on @gavin´s suggestion, I found this post that helped me make some progress on overriding the createToken method: Why does one need to pass a string in the createToken method?

So, in my LagacyUser model, I created the following method:

    /**
     * Override createToken method.

     */
    public function createToken(string $name, array $abilities = ['*'])
    {
        $token = $this->tokens()->create([
            'name' => $name,
            'token' => hash('sha256', $plainTextToken = Str::random(80)),
            'abilities' => $abilities,
        ]);
    
        return new NewAccessToken($token, $token->id.'|'.$plainTextToken);
    }    

It´s still returning me the error:

[previous exception] [object] (PDOException(code: 23000): SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'tokenable_id' cannot be null at …
[stacktrace]

I found another post that also helped me try to figure out how I need to change the tokens method: Laravel Sanctum with uuid column in User model doesn't save tokenable_id

Something like this:

    public function tokens()
    {
        return $this->morphMany(Sanctum::$personalAccessTokenModel, 'tokenable', ‘tokenable_type’, ‘tokenable_id’);
    }

However, I still have no idea how to convert tokenable_id into my model’s ID.

Remember: I´m not using Laravel migrations and all their wired up classes. Note: Opened to Laravel Passport solution.


Solution

  • Turned out that I didn’t need to override the createToken() and tokens() methods. As my Laravel experience is low, I didn’t know how to set the Laravel native ID attribute in my custom model. (That’s also part of the reason I wanted to build this without the wired up Laravel models – to deepen my learnings)

    In order to learn how to set the ID attribute, I found this post: https://stackoverflow.com/a/17236014

    And the code ended up looking like this:

    <?php
    
    namespace App\Models;
    
    use Illuminate\Database\Eloquent\Factories\HasFactory;
    use Illuminate\Foundation\Auth\User as Authenticatable;
    use Laravel\Sanctum\HasApiTokens;
    
    class LegacyUser extends Authenticatable
    {
        use HasFactory, HasApiTokens;
    
    
        /**
         * Build content placeholder body.
         * @return array
         */
        public function cphBodyBuild($oudRecordParameters): array
        {
            // Variables.
            // ----------------------
            $arrReturn = ['returnStatus' => false];
            $this->attributes['id'] = $oudRecordParameters['_idTbUsers']; // Override Laravel protected attribute (necessary for creating Sanctum token).
            // ----------------------
    
            // Logic.
            try {
                // Build object - details.
                if ($this->oudRecordParameters !== null) {
                    $oudRecord = new \SyncSystemNS\ObjectUsersDetails($oudRecordParameters);
                    $arrReturn['oudRecord'] = $oudRecord->recordDetailsGet(0, 1);
    
                    if ($arrReturn['oudRecord']['returnStatus'] === true) {
                        $arrReturn['returnStatus'] = true;
                    }
                }
            } catch (Error $cphBodyBuildError) {
                if ($GLOBALS['configDebug'] === true) {
                    throw new Error('cphBodyBuildError: ' . $cphBodyBuildError->message());
                }
            } finally {
                //
            }
            
            return $arrReturn;
        }
    }
    

    And creating the token looks like this:

    $userLogin = new LegacyUser();
    $userLoginData = $userLogin->cphBodyBuild($param);
    $userLoginToken = $userLogin->createToken('testing_token')->plainTextToken;
    

    After the createToken method is called, a new record in personal_access_tokens is created.