Hello.
Accordint to this https://github.com/laravel/framework/issues/17511 I need to setup clearly connection name for passport models.
But I can't find any convenient way to do this.
Can somebody suggest any way to do this with minimal code changes ?
You'd need to create an additional database connection, and then publish the passport migrations manually and use
Schema::connection('PassportDB')->create('oauth_clients', function (Blueprint $t) { ... });
@heisian thanks, but I can't understand how it will help me.
I'll try to explain more detailed.
I have User model. It is stored in one database, e.g. db2 .
Also I have Token model (actually i didn't create it - it is inside passport's package)
Token model is stored in database e.g. db1 (default connection).
In HasApiTokens trait (provided by passport) defined relation between User an Token model's
public function tokens()
{
return $this->hasMany(Token::class, 'user_id')->orderBy('created_at', 'desc');
}
In Laravel 5.3 related models are used default db connection (which is db1 in my example).
But in Laravel 5.4 this behavior was changed, and by default (if db connection not specified for model directly) related models are used parent's models connection.
So, when I try to access Token model through relation in User model, it uses db2 connection instead of db1
Yes actually I tried following through with using Passport in a secondary database, but was unable to get it to work correctly.
So what I did end up doing was having the entire app default to an oauth db, which only had the passport tables. Then for every other model, I explicitly designated it to use the myApp db. Then for retrieving tokens, I had to write my own method:
public function getTokens()
{
$tokens = \Laravel\Passport\Token::where('user_id', $this->id)->orderBy('created_at', 'desc')->get();
return $tokens;
}
Or something to that effect. Basically you can't use the stock getters.... unfortunately.
Might be worth us investigating some pull requests around allowing for multi-db relations, though we'd probably need to get really involved in their Slack channels as it sounds and I don't currently have the time to get that deep :P
I had to write my own method
Yes, it works in your code.
But, for example, in AuthorizedAccessTokenController->forUser in passport it will use default behavior, which will use parent's db connection
public function forUser(Request $request)
{
return $request->user()->tokens->load('client')->filter(function ($token) {
return ! $token->client->firstParty() && ! $token->revoked;
})->values();
}
Might be worth us investigating some pull requests ... I don't currently have the time to get that deep
so do I :-(
What you want to do here is take the HasApiTokens trait and override it. So create your own App\User\HasApiTokensCustom class, modify the methods, and within your User model, use App\User\HasApiTokensCustom; instead of Passport's.
That way when user()->tokens is called, it doesn't matter where it was called from, it's using your custom method for getting the tokens.
Closing for lack of activity, hope you got the help you needed :)
@heisian What change I need for multiple db connection in HasApiCustom trait, I don't know what to do, Can you help me here?
You need to override the default tokens() method with your own, using your DB connection. If that is not clear enough I would advise trying to learn more about how PHP and the Laravel framework works in general.
Is there any related examples . facing the same issue
Is there any solution for this ?
Duplicate your HasApiTokens trait and use your new HasApiTokens in User class
<?php
namespace App\KodeInfo;
use Illuminate\Container\Container;
use App\KodeInfo\Client;
use App\KodeInfo\Token;
use Laravel\Passport\PersonalAccessTokenFactory;
trait HasApiTokens
{
/**
* The current access token for the authentication user.
*
* @var \Laravel\Passport\Token
*/
protected $accessToken;
/**
* Get all of the user's registered OAuth clients.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function clients()
{
return $this->hasMany(Client::class, 'user_id');
}
/**
* Get all of the access tokens for the user.
*
* @return \Illuminate\Database\Eloquent\Collection
*/
public function tokens()
{
return $this->hasMany(Token::class, 'user_id')->orderBy('created_at', 'desc');
}
/**
* Get the current access token being used by the user.
*
* @return \Laravel\Passport\Token|null
*/
public function token()
{
return $this->accessToken;
}
/**
* Determine if the current API token has a given scope.
*
* @param string $scope
* @return bool
*/
public function tokenCan($scope)
{
return $this->accessToken ? $this->accessToken->can($scope) : false;
}
/**
* Create a new personal access token for the user.
*
* @param string $name
* @param array $scopes
* @return \Laravel\Passport\PersonalAccessTokenResult
*/
public function createToken($name, array $scopes = [])
{
return Container::getInstance()->make(PersonalAccessTokenFactory::class)->make(
$this->getKey(), $name, $scopes
);
}
/**
* Set the current access token for the user.
*
* @param \Laravel\Passport\Token $accessToken
* @return $this
*/
public function withAccessToken($accessToken)
{
$this->accessToken = $accessToken;
return $this;
}
}
Then make a copy of Client and Token class from passport/src and replace the class names in your new HasApiTokens class, now you can define the $connection in Client and Token class like below
<?php
namespace App\KodeInfo;
use Illuminate\Database\Eloquent\Model;
class Client extends Model
{
/**
* The database table used by the model.
*
* @var string
*/
protected $table = 'oauth_clients';
protected $connection = 'admin';
/**
* The guarded attributes on the model.
*
* @var array
*/
protected $guarded = [];
/**
* The attributes excluded from the model's JSON form.
*
* @var array
*/
protected $hidden = [
'secret',
];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'personal_access_client' => 'bool',
'password_client' => 'bool',
'revoked' => 'bool',
];
/**
* Get all of the authentication codes for the client.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function authCodes()
{
return $this->hasMany(AuthCode::class, 'client_id');
}
/**
* Get all of the tokens that belong to the client.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function tokens()
{
return $this->hasMany(Token::class, 'client_id');
}
/**
* Determine if the client is a "first party" client.
*
* @return bool
*/
public function firstParty()
{
return $this->personal_access_client || $this->password_client;
}
}
Instead of overwriting HasApiTokens you can also set your custom models in the boot method of AuthServiceProvider
namespace App\Providers;
use Laravel\Passport\Passport;
use App\PassportClient as Client;
use App\PassportToken as Token;
use App\PassportAuthCode as AuthCode;
use App\PassportPersonalAccessClient as PersonalAccessClient;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
*
* @var array
*/
protected $policies = [
//
];
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
Passport::routes();
Passport::useTokenModel(Token::class);
Passport::useClientModel(Client::class);
Passport::useAuthCodeModel(AuthCode::class);
Passport::usePersonalAccessClientModel(PersonalAccessClient::class);
}
}
Custom client model
namespace App;
use Laravel\Passport\Client;
class PassportClient extends Client
{
protected $connection = 'my_custom_connection';
protected $table = 'passport_oauth_clients';
}
Was trying something similar myself, but while this works for clients and tokens. It fails for auth codes when trying to use the authorization code flow.
I guess I will need to keep passport tables in the default database. Even after doing what @benbjurstrom said. Laravel still makes queries using the default database (see below). It is not using eloquent so, it is using the default DB instead of the one I want to.

nvm, I wal able to set the right connection in one of my services providers =D
$this->app->singleton(Connection::class, function () {
return DB::connection(env('DB_CONNECTION_CFG'));
});
you go to passport directory in your project. passpoty->src:
-Authcode.php
-client.php
-HasApiToken.php
-PersonalAccessClient.php
-token.php
you can add "protected $connection = '[database-name]'"
[database-name] is based on .env and database.php in config folder.
example
protected $connection = 'sqlsrv'

@benbjurstrom has the good answer, but you should add Passport::useRefreshTokenModel(RefreshToken::class); for 8.x and above :)
@benbjurstrom has the good answer, but you should add
Passport::useRefreshTokenModel(RefreshToken::class);for recent releases :)
Could not find the Refresh token model alongside the others from the latest release

Please help thanks
It's in the future 8.x that is there https://github.com/laravel/passport/tree/master/src
I was able to change the connection by creating the passport.php file in the config folder.
<?php
return [
'storage' => [
'database' => [
'connection' => 'tenant',
],
],
];
@
I was able to change the connection by creating the passport.php file in the config folder.
<?php return [ 'storage' => [ 'database' => [ 'connection' => 'tenant', ], ], ];
Only on versions after May 9 2020 ;)
solved details here https://github.com/laravel/passport/commit/555e02c212022393888cbd4e902b327e9ec9ad7a#diff-021d6ca3cab77674d7a33ed636455154
Lost the past 2 hours with this :D I have a multi-tenant & multi-database laravel application. I used to create my own Passport Models setting the connection variable to the tenant one
namespace App;
use Laravel\Passport\Client;
class PassportClient extends Client
{
protected $connection = 'tenant';
}
For some reason (after upgrade from 7 -> 9), the connection variable is ignored. To fix this I had to publish the config file changing the connection value to 'tenant'. Is this excepted behaviour?
Additional to https://github.com/laravel/passport/issues/247#issuecomment-424095961
Older version passport, you can override PassportServiceProvider
In App\Providers\PassportServiceProvider.php:
namespace App\Providers;
use DateInterval;
use Laravel\Passport;
use Laravel\Passport\Bridge;
use League\OAuth2\Server\Grant;
use DB;
class PassportServiceProvider extends Passport\PassportServiceProvider
{
/**
* Build the Auth Code grant instance.
*
* @return \League\OAuth2\Server\Grant\AuthCodeGrant
*/
protected function buildAuthCodeGrant()
{
return new Grant\AuthCodeGrant(
$this->app->make(Bridge\AuthCodeRepository::class),
$this->app->makeWith(Bridge\RefreshTokenRepository::class, ['database' => DB::connection('your-connection')]),
new DateInterval('PT10M')
);
}
/**
* Create and configure a Refresh Token grant instance.
*
* @return \League\OAuth2\Server\Grant\RefreshTokenGrant
*/
protected function makeRefreshTokenGrant()
{
$repository = $this->app->makeWith(Bridge\RefreshTokenRepository::class, ['database' => DB::connection('your-connection')]);
return tap(new Grant\RefreshTokenGrant($repository), function ($grant) {
$grant->setRefreshTokenTTL(Passport\Passport::refreshTokensExpireIn());
});
}
/**
* Create and configure a Password grant instance.
*
* @return \League\OAuth2\Server\Grant\PasswordGrant
*/
protected function makePasswordGrant()
{
$grant = new Grant\PasswordGrant(
$this->app->make(Bridge\UserRepository::class),
$this->app->makeWith(Bridge\RefreshTokenRepository::class, ['database' => DB::connection('your-connection')])
);
$grant->setRefreshTokenTTL(Passport\Passport::refreshTokensExpireIn());
return $grant;
}
}
In config/app.php add to providers section
/*
* Application Service Providers...
*/
...
App\Providers\PassportServiceProvider::class,
...
Finally in composer.json, disable discovering original PassportServiceProvider:
"extra": {
"laravel": {
"dont-discover": [
"Laravel\\Passport\\PassportServiceProvider"
]
}
},
And:
composer dump-autoload
Most helpful comment
Instead of overwriting HasApiTokens you can also set your custom models in the boot method of AuthServiceProvider
Custom client model