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

[9.x] Initial support for multiple providers #1220

Merged
merged 16 commits into from
Apr 28, 2020
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
coverage: none

- name: Install dependencies
run: composer require "illuminate/contracts=${{ matrix.laravel }}" --prefer-dist --no-interaction --no-suggest
run: composer require "illuminate/contracts=${{ matrix.laravel }}" --prefer-dist --no-interaction

- name: Execute tests
run: vendor/bin/phpunit --verbose
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public function up()
$table->unsignedBigInteger('user_id')->nullable()->index();
$table->string('name');
$table->string('secret', 100)->nullable();
$table->string('provider')->nullable();
billriess marked this conversation as resolved.
Show resolved Hide resolved
$table->text('redirect');
$table->boolean('personal_access_client');
$table->boolean('password_client');
Expand Down
11 changes: 10 additions & 1 deletion src/Bridge/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,31 @@ class Client implements ClientEntityInterface
*/
protected $identifier;

/**
* The client's provider.
*
* @var string
*/
public $provider;
billriess marked this conversation as resolved.
Show resolved Hide resolved

/**
* Create a new client instance.
*
* @param string $identifier
* @param string $name
* @param string $redirectUri
* @param bool $isConfidential
* @param string|null $provider
* @return void
*/
public function __construct($identifier, $name, $redirectUri, $isConfidential = false)
public function __construct($identifier, $name, $redirectUri, $isConfidential = false, $provider = null)
billriess marked this conversation as resolved.
Show resolved Hide resolved
{
$this->setIdentifier((string) $identifier);

$this->name = $name;
$this->isConfidential = $isConfidential;
$this->redirectUri = explode(',', $redirectUri);
$this->provider = $provider;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/Bridge/ClientRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public function getClientEntity($clientIdentifier)
}

return new Client(
$clientIdentifier, $record->name, $record->redirect, $record->confidential()
$clientIdentifier, $record->name, $record->redirect, $record->confidential(), $record->provider
);
}

Expand Down
2 changes: 1 addition & 1 deletion src/Bridge/UserRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public function __construct(HashManager $hasher)
*/
public function getUserEntityByUserCredentials($username, $password, $grantType, ClientEntityInterface $clientEntity)
{
$provider = config('auth.guards.api.provider');
$provider = $clientEntity->provider ?: config('auth.guards.api.provider');

if (is_null($model = config('auth.providers.'.$provider.'.model'))) {
throw new RuntimeException('Unable to determine authentication model from configuration.');
Expand Down
4 changes: 3 additions & 1 deletion src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,10 @@ class Client extends Model
*/
public function user()
{
$provider = $this->provider ?: config('auth.guards.api.provider');

return $this->belongsTo(
config('auth.providers.'.config('auth.guards.api.provider').'.model')
config("auth.providers.$provider.model")
);
}

Expand Down
11 changes: 7 additions & 4 deletions src/ClientRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -104,18 +104,20 @@ public function personalAccessClient()
* @param int $userId
* @param string $name
* @param string $redirect
* @param string|null $provider
* @param bool $personalAccess
* @param bool $password
* @param bool $confidential
* @return \Laravel\Passport\Client
*/
public function create($userId, $name, $redirect, $personalAccess = false, $password = false, $confidential = true)
public function create($userId, $name, $redirect, $provider = null, $personalAccess = false, $password = false, $confidential = true)
billriess marked this conversation as resolved.
Show resolved Hide resolved
{
$client = Passport::client()->forceFill([
'user_id' => $userId,
'name' => $name,
'secret' => ($confidential || $personalAccess) ? Str::random(40) : null,
'redirect' => $redirect,
'provider' => $provider,
'personal_access_client' => $personalAccess,
'password_client' => $password,
'revoked' => false,
Expand All @@ -136,7 +138,7 @@ public function create($userId, $name, $redirect, $personalAccess = false, $pass
*/
public function createPersonalAccessClient($userId, $name, $redirect)
{
return tap($this->create($userId, $name, $redirect, true), function ($client) {
return tap($this->create($userId, $name, $redirect, null, true), function ($client) {
$accessClient = Passport::personalAccessClient();
$accessClient->client_id = $client->id;
$accessClient->save();
Expand All @@ -149,11 +151,12 @@ public function createPersonalAccessClient($userId, $name, $redirect)
* @param int $userId
* @param string $name
* @param string $redirect
* @param string|null $provider
* @return \Laravel\Passport\Client
*/
public function createPasswordGrantClient($userId, $name, $redirect)
public function createPasswordGrantClient($userId, $name, $redirect, $provider = null)
billriess marked this conversation as resolved.
Show resolved Hide resolved
{
return $this->create($userId, $name, $redirect, false, true);
return $this->create($userId, $name, $redirect, $provider, false, true);
}

/**
Expand Down
13 changes: 11 additions & 2 deletions src/Console/ClientCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class ClientCommand extends Command
{--password : Create a password grant client}
{--client : Create a client credentials grant client}
{--name= : The name of the client}
{--provider= : The name of the provider}
{--redirect_uri= : The URI to redirect to after authorization }
{--user_id= : The user ID the client should be assigned to }
{--public : Create a public client (Auth code grant type only) }';
Expand Down Expand Up @@ -83,8 +84,16 @@ protected function createPasswordClient(ClientRepository $clients)
config('app.name').' Password Grant Client'
);

$providers = array_keys(config('auth.providers'));

$provider = $this->option('provider') ?: $this->choice(
'What provider should be used?',
$providers,
in_array('users', $providers) ? 'users' : null
);

$client = $clients->createPasswordGrantClient(
null, $name, 'http://localhost'
null, $name, 'http://localhost', $provider
);

$this->info('Password grant client created successfully.');
Expand Down Expand Up @@ -136,7 +145,7 @@ protected function createAuthCodeClient(ClientRepository $clients)
);

$client = $clients->create(
$userId, $name, $redirect, false, false, ! $this->option('public')
$userId, $name, $redirect, null, false, false, ! $this->option('public')
);

$this->info('New client created successfully.');
Expand Down
4 changes: 3 additions & 1 deletion src/Console/InstallCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ class InstallCommand extends Command
*/
public function handle()
{
$provider = in_array('users', array_keys(config('auth.providers'))) ? 'users' : null;
billriess marked this conversation as resolved.
Show resolved Hide resolved

$this->call('passport:keys', ['--force' => $this->option('force'), '--length' => $this->option('length')]);
$this->call('passport:client', ['--personal' => true, '--name' => config('app.name').' Personal Access Client']);
$this->call('passport:client', ['--password' => true, '--name' => config('app.name').' Password Grant Client']);
$this->call('passport:client', ['--password' => true, '--name' => config('app.name').' Password Grant Client', '--provider' => $provider]);
}
}
41 changes: 32 additions & 9 deletions src/Guards/TokenGuard.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
use Exception;
use Firebase\JWT\JWT;
use Illuminate\Container\Container;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Contracts\Debug\ExceptionHandler;
use Illuminate\Contracts\Encryption\Encrypter;
use Illuminate\Cookie\Middleware\EncryptCookies;
Expand All @@ -16,6 +15,7 @@
use Laminas\Diactoros\UploadedFileFactory;
use Laravel\Passport\ClientRepository;
use Laravel\Passport\Passport;
use Laravel\Passport\PassportUserProvider;
use Laravel\Passport\TokenRepository;
use Laravel\Passport\TransientToken;
use League\OAuth2\Server\Exception\OAuthServerException;
Expand All @@ -34,7 +34,7 @@ class TokenGuard
/**
* The user provider implementation.
*
* @var \Illuminate\Contracts\Auth\UserProvider
* @var \Laravel\Passport\PassportUserProvider
*/
protected $provider;

Expand Down Expand Up @@ -63,25 +63,44 @@ class TokenGuard
* Create a new token guard instance.
*
* @param \League\OAuth2\Server\ResourceServer $server
* @param \Illuminate\Contracts\Auth\UserProvider $provider
* @param \Laravel\Passport\PassportUserProvider $provider
* @param \Laravel\Passport\TokenRepository $tokens
* @param \Laravel\Passport\ClientRepository $clients
* @param \Illuminate\Contracts\Encryption\Encrypter $encrypter
* @return void
*/
public function __construct(ResourceServer $server,
UserProvider $provider,
TokenRepository $tokens,
ClientRepository $clients,
Encrypter $encrypter)
{
public function __construct(
ResourceServer $server,
PassportUserProvider $provider,
TokenRepository $tokens,
ClientRepository $clients,
Encrypter $encrypter
) {
$this->server = $server;
$this->tokens = $tokens;
$this->clients = $clients;
$this->provider = $provider;
$this->encrypter = $encrypter;
}

/**
* Determine if the requested provider matches the client's provider.
*
* @param \Illuminate\Http\Request $request
* @return bool
*/
protected function hasValidProvider(Request $request)
{
$client = $this->client($request);

// If no client provider is defined, fallback to old behavior.
if ($client && ! $client->provider) {
return true;
}

return $client && $client->provider === $this->provider->getProviderName();
}

/**
* Get the user for the incoming request.
*
Expand All @@ -90,6 +109,10 @@ public function __construct(ResourceServer $server,
*/
public function user(Request $request)
{
if (! $this->hasValidProvider($request)) {
return;
}

if ($request->bearerToken()) {
return $this->authenticateViaBearerToken($request);
} elseif ($request->cookie(Passport::cookie())) {
Expand Down
2 changes: 1 addition & 1 deletion src/Http/Middleware/CheckCredentials.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ protected function validate($psr, $scopes)
abstract protected function validateCredentials($token);

/**
* Validate token credentials.
* Validate token scopes.
*
* @param \Laravel\Passport\Token $token
* @param array $scopes
Expand Down
2 changes: 1 addition & 1 deletion src/PassportServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ protected function makeGuard(array $config)
return new RequestGuard(function ($request) use ($config) {
return (new TokenGuard(
$this->app->make(ResourceServer::class),
Auth::createUserProvider($config['provider']),
new PassportUserProvider(Auth::createUserProvider($config['provider']), $config['provider']),
$this->app->make(TokenRepository::class),
$this->app->make(ClientRepository::class),
$this->app->make('encrypter')
Expand Down
86 changes: 86 additions & 0 deletions src/PassportUserProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php

namespace Laravel\Passport;

use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider;

class PassportUserProvider implements UserProvider
{
/**
* The Application UserProvider instance.
*
* @var \Illuminate\Contracts\Auth\UserProvider
*/
protected $provider;

/**
* The Application UserProvider name.
*
* @var string
*/
protected $providerName;

/**
* Create a new passport user provider.
*
* @param \Illuminate\Contracts\Auth\UserProvider $provider
* @param string $providerName
* @return void
*/
public function __construct(UserProvider $provider, $providerName)
{
$this->provider = $provider;
$this->providerName = $providerName;
}

/**
* Get the UserProvider name.
*
* @return string
*/
public function getProviderName()
{
return $this->providerName;
}

/**
* {@inheritdoc}
*/
public function retrieveById($identifier)
{
return $this->provider->retrieveById($identifier);
}

/**
* {@inheritdoc}
*/
public function retrieveByToken($identifier, $token)
{
return $this->provider->retrieveByToken($identifier, $token);
}

/**
* {@inheritdoc}
*/
public function updateRememberToken(Authenticatable $user, $token)
{
$this->provider->updateRememberToken($user, $token);
}

/**
* {@inheritdoc}
*/
public function retrieveByCredentials(array $credentials)
{
return $this->provider->retrieveByCredentials($credentials);
}

/**
* {@inheritdoc}
*/
public function validateCredentials(Authenticatable $user, array $credentials)
{
return $this->provider->validateCredentials($user, $credentials);
}
}
2 changes: 2 additions & 0 deletions tests/BridgeClientRepositoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,8 @@ class BridgeClientRepositoryTestClientStub

public $password_client = false;

public $provider = null;

public $grant_types;

public function firstParty()
Expand Down
Loading