markwilliamsweb
markwilliamsweb

Reputation: 490

Laravel Sanctum Tokens - Two Databases

I am having some problems getting Laravel Sanctum authorising two tables in two separate databases.

I am using Laravel Sanctum tokens for authorisation. I have two tables to authorise users (users & contacts) I have setup two separate guards and can get everything to work on a single database with one token table.

However I want to have the contacts table in a separate database. Doing so creates two personal_access_tokens tables, one in the Users database and the other in the Contacts database, which I don't mind. I can create the tokens just fine, however when I try to authorise contacts using a token, Sanctum is trying to look in the Users personal_access_tokens table, not the Contacts personal_access_tokens table. So essentially it's just looking at the wrong database for the personal_access_tokens table and I don't know how to change that.

My setup is as follows:

Guards:

'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],

    /*'api' => [
        'driver' => 'token',
        'provider' => 'users',
        'hash' => false,
    ],*/

    'users' => [
        'driver' => 'sanctum',
        'provider' => 'users',
        'hash' => false,
    ],

    'contacts' => [
        'driver' => 'sanctum',
        'provider' => 'contacts',
        'hash' => false,
    ],
],

Providers

   'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class,
        ],

        'contacts' => [
            'driver' => 'eloquent',
            'model' => App\Models\Contact::class,
        ],
    ],

User Model

namespace App\Models;

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

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name',
        'email',
        'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
}

Contact Model

namespace App\Models;

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

class Contact extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;

    /**
     * The connection name for the model.
     *
     * @var string
     */
    protected $connection = 'puranet_crm';

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'first_name',
        'last_name',
        'email',
        'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
}

My two api routes for autorisation are:

Route::group(['middleware' => 'auth:sanctum'], function() {
    //All secure URL's
    Route::get('test',[UserController::class, 'test']);
});

Route::group(['middleware' => 'auth:contacts'], function() {
    Route::get('test-contacts',[ContactController::class, 'test']);
});

Contact Controller (this is identical to the UserController with exception to the Model it is referencing)

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Contact;
use Illuminate\Support\Facades\Hash;

class ContactController extends Controller
{
    /**
     * @param Request $request
     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
     */
    public function login(Request $request)
    {

        $user = Contact::where('email', $request->email)->first();

        if (!$user || !Hash::check($request->password, $user->password)) {
            return response([
                'message' => ['These credentials do not match our records.']
            ], 404);
        }

        $token = $user->createToken('contacts-app-token')->plainTextToken;

        $response = [
            'user' => $user,
            'token' => $token
        ];

        return response($response, 201);
    }

    /**
     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
     */
    public function test()
    {
        return response(["response" => "Test Contacts"], 201);
    }
}

Upvotes: 5

Views: 3883

Answers (1)

Gustavo Specht
Gustavo Specht

Reputation: 31

You need to overwrite the sanctum model on your project and overwrite the $connection variable inside of it, so you will be able to connect to the database that you would like to, same when you do with normal Models.You can find how to overwrite sanctum model on the Laravel documentation for version 8. Create a this model in one of your projects to overwrite where sanctum will look for the token.

class PersonalAccessToken extends SanctumPersonalAccessToken{

  use HasFactory;

  protected $connection = 'name of your connection in database.php';
}

So both sanctum will use the same DB to auth the User.

I hope I helped you :)

Upvotes: 3

Related Questions