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

Setup passport db connection #247

Closed
RidgeA opened this issue Jan 24, 2017 · 24 comments
Closed

Setup passport db connection #247

RidgeA opened this issue Jan 24, 2017 · 24 comments

Comments

@RidgeA
Copy link

RidgeA commented Jan 24, 2017

Hello.

Accordint to this laravel/framework#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 ?

@heisian
Copy link

heisian commented Feb 6, 2017

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) { ... });

@RidgeA
Copy link
Author

RidgeA commented Feb 21, 2017

@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

@heisian
Copy link

heisian commented Feb 21, 2017

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.

@heisian
Copy link

heisian commented Feb 21, 2017

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

@RidgeA
Copy link
Author

RidgeA commented Feb 21, 2017

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

@heisian
Copy link

heisian commented Feb 21, 2017

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.

@themsaid
Copy link
Member

themsaid commented Jul 5, 2017

Closing for lack of activity, hope you got the help you needed :)

@themsaid themsaid closed this as completed Jul 5, 2017
@NikhilRadadiya
Copy link

@heisian What change I need for multiple db connection in HasApiCustom trait, I don't know what to do, Can you help me here?

@heisian
Copy link

heisian commented Dec 4, 2017

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.

@sathyasankar
Copy link

Is there any related examples . facing the same issue

@sadhakbj
Copy link

sadhakbj commented Jun 8, 2018

Is there any solution for this ?

@shellprog
Copy link

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;
    }
}

@benbjurstrom
Copy link

benbjurstrom commented Sep 24, 2018

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';
}

@livingos
Copy link
Contributor

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.

@lucasctd
Copy link

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.

image

@lucasctd
Copy link

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'));
});

@richieamir
Copy link

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'
image

@AdrienPoupa
Copy link

AdrienPoupa commented Oct 3, 2019

@benbjurstrom has the good answer, but you should add Passport::useRefreshTokenModel(RefreshToken::class); for 8.x and above :)

@johenel
Copy link

johenel commented Oct 17, 2019

@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

image

Please help thanks

@AdrienPoupa
Copy link

It's in the future 8.x that is there https://github.com/laravel/passport/tree/master/src

@andrefrlima
Copy link

andrefrlima commented May 21, 2020

I was able to change the connection by creating the passport.php file in the config folder.

<?php

return [
    'storage' => [
        'database' => [
            'connection' => 'tenant',
        ],
    ],
];

@zeetabit
Copy link

zeetabit commented May 26, 2020

@

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 555e02c#diff-021d6ca3cab77674d7a33ed636455154

@wannymiarelli
Copy link

wannymiarelli commented Jul 20, 2020

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?

@eskater
Copy link

eskater commented Dec 20, 2020

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

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