Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sanctum package not switching the database connection #51

Closed
redonosmanollaj opened this issue Jun 17, 2020 · 11 comments
Closed

Sanctum package not switching the database connection #51

redonosmanollaj opened this issue Jun 17, 2020 · 11 comments

Comments

@redonosmanollaj
Copy link

I am using sanctum package for authorizing users in both tenant database connection and landlord database connection. In my case I'm using example.test domain for authorizing landlord users, and subdomains ex. subdomain.example.test for authorizing tenant users. I am using landlord connection as default and works good when trying to login as landlord user, but when trying to login in subdomain.example.test it looks like sanctum cannot change database connection to 'tenant'.

@masterix21
Copy link
Collaborator

Hi @redonosmanollaj,

are you using the SwitchTenantDatabaseTask class?

I have tried now and it seems ok:

tinker

@redonosmanollaj
Copy link
Author

redonosmanollaj commented Jun 17, 2020

Hi @redonosmanollaj,

are you using the SwitchTenantDatabaseTask class?

I have tried now and it seems ok:

tinker

Yes tokens generated good, but when trying to call routes that have sanctum middleware, landlord works well, but tenantToken doesnt work because sanctum is trying to find token in landlord database, and getting Unauthenticated error message.

@redonosmanollaj
Copy link
Author

Or vice versa, if I set tenant as default connection, tenant routes works well, but when I try to call landlord routes I get this error:

Illuminate\Database\QueryException: SQLSTATE[3D000]: Invalid catalog name: 1046 No database selected (SQL: select * from personal_access_tokens where personal_access_tokens.id = 1 limit 1) in file C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Database\Connection.php on line 671

#0 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Database\Connection.php(631): Illuminate\Database\Connection->runQueryCallback('select * from ...', Array, Object(Closure)) #1 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Database\Connection.php(339): Illuminate\Database\Connection->run('select * from ...', Array, Object(Closure))
#2 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Database\Query\Builder.php(2202): Illuminate\Database\Connection->select('select * from `...', Array, true)
#3 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Database\Query\Builder.php(2190): Illuminate\Database\Query\Builder->runSelect()
#4 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Database\Query\Builder.php(2685): Illuminate\Database\Query\Builder->Illuminate\Database\Query{closure}()
#5 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Database\Query\Builder.php(2191): Illuminate\Database\Query\Builder->onceWithColumns(Array, Object(Closure))
#6 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Builder.php(539): Illuminate\Database\Query\Builder->get(Array)
#7 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Builder.php(523): Illuminate\Database\Eloquent\Builder->getModels(Array)
#8 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Database\Concerns\BuildsQueries.php(143): Illuminate\Database\Eloquent\Builder->get(Array)
#9 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Builder.php(345): Illuminate\Database\Eloquent\Builder->first(Array)
#10 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Support\Traits\ForwardsCalls.php(23): Illuminate\Database\Eloquent\Builder->find('1')
#11 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Model.php(1728): Illuminate\Database\Eloquent\Model->forwardCallTo(Object(Illuminate\Database\Eloquent\Builder), 'find', Array)
#12 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Model.php(1740): Illuminate\Database\Eloquent\Model->__call('find', Array)
#13 C:\laragon\www\strike-backend\vendor\laravel\sanctum\src\PersonalAccessToken.php(64): Illuminate\Database\Eloquent\Model::__callStatic('find', Array)
#14 C:\laragon\www\strike-backend\vendor\laravel\sanctum\src\Guard.php(66): Laravel\Sanctum\PersonalAccessToken::findToken('vsBMIh82FWlq8ID...')
#15 [internal function]: Laravel\Sanctum\Guard->__invoke(Object(Illuminate\Http\Request), NULL)
#16 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Auth\RequestGuard.php(58): call_user_func(Object(Laravel\Sanctum\Guard), Object(Illuminate\Http\Request), NULL)
#17 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Auth\GuardHelpers.php(60): Illuminate\Auth\RequestGuard->user()
#18 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Auth\Middleware\Authenticate.php(63): Illuminate\Auth\RequestGuard->check()
#19 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Auth\Middleware\Authenticate.php(42): Illuminate\Auth\Middleware\Authenticate->authenticate(Object(Illuminate\Http\Request), Array)
#20 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(167): Illuminate\Auth\Middleware\Authenticate->handle(Object(Illuminate\Http\Request), Object(Closure), 'sanctum')
#21 C:\laragon\www\strike-backend\vendor\laravel\sanctum\src\Http\Middleware\EnsureFrontendRequestsAreStateful.php(33): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline{closure}(Object(Illuminate\Http\Request))
#22 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(128): Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful->Laravel\Sanctum\Http\Middleware{closure}(Object(Illuminate\Http\Request))
#23 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(103): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline{closure}(Object(Illuminate\Http\Request))
#24 C:\laragon\www\strike-backend\vendor\laravel\sanctum\src\Http\Middleware\EnsureFrontendRequestsAreStateful.php(34): Illuminate\Pipeline\Pipeline->then(Object(Closure))
#25 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(167): Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful->handle(Object(Illuminate\Http\Request), Object(Closure))
#26 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(103): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline{closure}(Object(Illuminate\Http\Request))
#27 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Routing\Router.php(687): Illuminate\Pipeline\Pipeline->then(Object(Closure))
#28 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Routing\Router.php(662): Illuminate\Routing\Router->runRouteWithinStack(Object(Illuminate\Routing\Route), Object(Illuminate\Http\Request))
#29 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Routing\Router.php(628): Illuminate\Routing\Router->runRoute(Object(Illuminate\Http\Request), Object(Illuminate\Routing\Route))
#30 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Routing\Router.php(617): Illuminate\Routing\Router->dispatchToRoute(Object(Illuminate\Http\Request))
#31 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php(165): Illuminate\Routing\Router->dispatch(Object(Illuminate\Http\Request))
#32 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(128): Illuminate\Foundation\Http\Kernel->Illuminate\Foundation\Http{closure}(Object(Illuminate\Http\Request))
#33 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Foundation\Http\Middleware\TransformsRequest.php(21): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline{closure}(Object(Illuminate\Http\Request))
#34 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(167): Illuminate\Foundation\Http\Middleware\TransformsRequest->handle(Object(Illuminate\Http\Request), Object(Closure))
#35 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Foundation\Http\Middleware\TransformsRequest.php(21): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline{closure}(Object(Illuminate\Http\Request))
#36 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(167): Illuminate\Foundation\Http\Middleware\TransformsRequest->handle(Object(Illuminate\Http\Request), Object(Closure))
#37 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Foundation\Http\Middleware\ValidatePostSize.php(27): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline{closure}(Object(Illuminate\Http\Request))
#38 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(167): Illuminate\Foundation\Http\Middleware\ValidatePostSize->handle(Object(Illuminate\Http\Request), Object(Closure))
#39 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode.php(63): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline{closure}(Object(Illuminate\Http\Request))
#40 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(167): Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode->handle(Object(Illuminate\Http\Request), Object(Closure))
#41 C:\laragon\www\strike-backend\vendor\fruitcake\laravel-cors\src\HandleCors.php(37): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline{closure}(Object(Illuminate\Http\Request))
#42 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(167): Fruitcake\Cors\HandleCors->handle(Object(Illuminate\Http\Request), Object(Closure))
#43 C:\laragon\www\strike-backend\vendor\fideloper\proxy\src\TrustProxies.php(57): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline{closure}(Object(Illuminate\Http\Request))
#44 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(167): Fideloper\Proxy\TrustProxies->handle(Object(Illuminate\Http\Request), Object(Closure))
#45 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php(103): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline{closure}(Object(Illuminate\Http\Request))
#46 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php(140): Illuminate\Pipeline\Pipeline->then(Object(Closure))
#47 C:\laragon\www\strike-backend\vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php(109): Illuminate\Foundation\Http\Kernel->sendRequestThroughRouter(Object(Illuminate\Http\Request))
#48 C:\laragon\www\strike-backend\public\index.php(55): Illuminate\Foundation\Http\Kernel->handle(Object(Illuminate\Http\Request))
#49 {main}

@masterix21
Copy link
Collaborator

masterix21 commented Jun 18, 2020

Try to extend "Spatie\Multitenancy\Tasks\SwitchTenantDatabaseTask::class" with:

config([
    'database.default' => 'YOUR-TENANT-CONNECTION',
]);

@redonosmanollaj
Copy link
Author

Yes it works. Thank you very much.

@aginev
Copy link
Contributor

aginev commented Oct 7, 2020

Sorry about open this one again but not sure that setting default DB connection to "tenant" one is the best choice. Consider this post as another way of getting things working with Sanctum. Still on a early stage on the project using this and not tested entirely...

See:
https://github.com/laravel/sanctum/blob/2.x/src/Guard.php#L61
https://github.com/laravel/sanctum/blob/2.x/src/Sanctum.php#L56-L65

Sanctum gives you the opportunity to overwrite it's default model like so:

<?php

namespace App\Models\Tenant;

use Spatie\Multitenancy\Models\Concerns\UsesTenantConnection;

class PersonalAccessToken extends \Laravel\Sanctum\PersonalAccessToken
{
    use UsesTenantConnection;
}

Then you'll need a task that should set the new model into Sanctum

<?php

namespace App\Multitenancy\Tasks;

use Laravel\Sanctum\Sanctum;
use Spatie\Multitenancy\Models\Tenant;
use App\Models\Tenant\PersonalAccessToken;
use Spatie\Multitenancy\Tasks\SwitchTenantTask;
use Laravel\Sanctum\PersonalAccessToken as BasePersonalAccessToken;

class SetSanctumPersonAccessTokenModelTask implements SwitchTenantTask
{

    public function makeCurrent(Tenant $tenant): void
    {
        Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class);
    }

    public function forgetCurrent(): void
    {
        Sanctum::usePersonalAccessTokenModel(BasePersonalAccessToken::class);
    }
}

Do not forget to add it to package config:

/*
 * These tasks will be performed when switching tenants.
 *
 * A valid task is any class that implements Spatie\Multitenancy\Tasks\SwitchTenantTask
 */
'switch_tenant_tasks' => [
    // Some other tasks in here...
    App\Multitenancy\Tasks\SetSanctumPersonAccessTokenModelTask::class
],

In this way all the database stays the same and just the models that are supposed to work with multi tenancy will use the proper DB connection.

Regards

Nasko

@sys-auditing
Copy link

Hi,
thank you for your solution, but i got this messge error :
Cannot declare class App\Models\PersonalAccessToken because the name is already in use
Do you have any idea ?

Thank you

@sys-auditing
Copy link

I figured out the issue
Thank you

@emadbatwa
Copy link

Sorry about open this one again but not sure that setting default DB connection to "tenant" one is the best choice. Consider this post as another way of getting things working with Sanctum. Still on a early stage on the project using this and not tested entirely...

See: https://github.com/laravel/sanctum/blob/2.x/src/Guard.php#L61 https://github.com/laravel/sanctum/blob/2.x/src/Sanctum.php#L56-L65

Sanctum gives you the opportunity to overwrite it's default model like so:

<?php

namespace App\Models\Tenant;

use Spatie\Multitenancy\Models\Concerns\UsesTenantConnection;

class PersonalAccessToken extends \Laravel\Sanctum\PersonalAccessToken
{
    use UsesTenantConnection;
}

Then you'll need a task that should set the new model into Sanctum

<?php

namespace App\Multitenancy\Tasks;

use Laravel\Sanctum\Sanctum;
use Spatie\Multitenancy\Models\Tenant;
use App\Models\Tenant\PersonalAccessToken;
use Spatie\Multitenancy\Tasks\SwitchTenantTask;
use Laravel\Sanctum\PersonalAccessToken as BasePersonalAccessToken;

class SetSanctumPersonAccessTokenModelTask implements SwitchTenantTask
{

    public function makeCurrent(Tenant $tenant): void
    {
        Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class);
    }

    public function forgetCurrent(): void
    {
        Sanctum::usePersonalAccessTokenModel(BasePersonalAccessToken::class);
    }
}

Do not forget to add it to package config:

/*
 * These tasks will be performed when switching tenants.
 *
 * A valid task is any class that implements Spatie\Multitenancy\Tasks\SwitchTenantTask
 */
'switch_tenant_tasks' => [
    // Some other tasks in here...
    App\Multitenancy\Tasks\SetSanctumPersonAccessTokenModelTask::class
],

In this way all the database stays the same and just the models that are supposed to work with multi tenancy will use the proper DB connection.

Regards

Nasko

Thank you, its work for me

@weirdlooop
Copy link

Sorry about open this one again but not sure that setting default DB connection to "tenant" one is the best choice. Consider this post as another way of getting things working with Sanctum. Still on a early stage on the project using this and not tested entirely...

See: https://github.com/laravel/sanctum/blob/2.x/src/Guard.php#L61 https://github.com/laravel/sanctum/blob/2.x/src/Sanctum.php#L56-L65

Sanctum gives you the opportunity to overwrite it's default model like so:

<?php

namespace App\Models\Tenant;

use Spatie\Multitenancy\Models\Concerns\UsesTenantConnection;

class PersonalAccessToken extends \Laravel\Sanctum\PersonalAccessToken
{
    use UsesTenantConnection;
}

Then you'll need a task that should set the new model into Sanctum

<?php

namespace App\Multitenancy\Tasks;

use Laravel\Sanctum\Sanctum;
use Spatie\Multitenancy\Models\Tenant;
use App\Models\Tenant\PersonalAccessToken;
use Spatie\Multitenancy\Tasks\SwitchTenantTask;
use Laravel\Sanctum\PersonalAccessToken as BasePersonalAccessToken;

class SetSanctumPersonAccessTokenModelTask implements SwitchTenantTask
{

    public function makeCurrent(Tenant $tenant): void
    {
        Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class);
    }

    public function forgetCurrent(): void
    {
        Sanctum::usePersonalAccessTokenModel(BasePersonalAccessToken::class);
    }
}

Do not forget to add it to package config:

/*
 * These tasks will be performed when switching tenants.
 *
 * A valid task is any class that implements Spatie\Multitenancy\Tasks\SwitchTenantTask
 */
'switch_tenant_tasks' => [
    // Some other tasks in here...
    App\Multitenancy\Tasks\SetSanctumPersonAccessTokenModelTask::class
],

In this way all the database stays the same and just the models that are supposed to work with multi tenancy will use the proper DB connection.

Regards

Nasko

This is the best solution and worked for me also in 2023

@adarmanto
Copy link

adarmanto commented Jun 9, 2023

My approach was a bit different with #51 (comment) and getting help from AI :) https://www.phind.com/search?cache=d0a2e8b6-5615-49db-8376-9f05a28e4bff in Laravel v10 version.

Basically, I need to override the default sanctum model and that's it!

use App\Models\Sanctum\PersonalAccessToken;
use Laravel\Sanctum\Sanctum;
 
/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class);
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants