-
Notifications
You must be signed in to change notification settings - Fork 87
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add configuration support for custom persistence
- Loading branch information
1 parent
21a5fae
commit 81a32a0
Showing
11 changed files
with
483 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
# Using custom persistence managers | ||
|
||
Implement the 4 interfaces from the `League\Bundle\OAuth2ServerBundle\Manager` namespace: | ||
- [AccessTokenManagerInterface](../src/Manager/AccessTokenManagerInterface.php) | ||
- [AuthorizationCodeManagerInterface](../src/Manager/AuthorizationCodeManagerInterface.php) | ||
- [ClientManagerInterface](../src/Manager/ClientManagerInterface.php) | ||
- [RefreshTokenManagerInterface](../src/Manager/RefreshTokenManagerInterface.php) | ||
And the interface for `CredentialsRevokerInterface`: | ||
- [CredentialsRevokerInterface](../src/Service/CredentialsRevokerInterface.php) | ||
|
||
```php | ||
|
||
Example: | ||
|
||
```php | ||
class MyAccessTokenManager implements AccessTokenManagerInterface | ||
{ | ||
} | ||
|
||
class MyAuthorizationCodeManager implements AuthorizationCodeManagerInterface | ||
{ | ||
} | ||
|
||
class MyClientManager implements ClientManagerInterface | ||
{ | ||
} | ||
|
||
class MyRefreshTokenManager implements RefreshTokenManagerInterface | ||
{ | ||
} | ||
|
||
class MyCredentialsRevoker implements CredentialsRevokerInterface | ||
{ | ||
} | ||
``` | ||
|
||
Then register the services in the container: | ||
|
||
```yaml | ||
services: | ||
_defaults: | ||
autoconfigure: true | ||
|
||
App\Manager\MyAccessTokenManager: ~ | ||
App\Manager\MyAuthorizationCodeManager: ~ | ||
App\Manager\MyClientManager: ~ | ||
App\Manager\MyRefreshTokenManager: ~ | ||
App\Service\MyCredentialsRevoker: ~ | ||
``` | ||
Finally, configure the bundle to use the new managers: | ||
```yaml | ||
league_oauth2_server: | ||
persistence: | ||
custom: | ||
access_token_manager: App\Manager\MyAccessTokenManager | ||
authorization_code_manager: App\Manager\MyAuthorizationCodeManager | ||
client_manager: App\Manager\MyClientManager | ||
refresh_token_manager: App\Manager\MyRefreshTokenManager | ||
credentials_revoker: App\Service\MyCredentialsRevoker | ||
``` | ||
## Optional | ||
Example MySql table schema for custom persistence managers implementation: | ||
```sql | ||
CREATE TABLE `oauth2_access_token` ( | ||
`identifier` char(80) NOT NULL, | ||
`client` varchar(32) NOT NULL, | ||
`expiry` datetime NOT NULL, | ||
`userIdentifier` varchar(128) DEFAULT NULL, | ||
`scopes` text, | ||
`revoked` tinyint(1) NOT NULL, | ||
PRIMARY KEY (`identifier`), | ||
KEY `client` (`client`) | ||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; | ||
|
||
CREATE TABLE `oauth2_authorization_code` ( | ||
`identifier` char(80) NOT NULL, | ||
`client` varchar(32) NOT NULL, | ||
`expiry` datetime NOT NULL, | ||
`userIdentifier` varchar(128) DEFAULT NULL, | ||
`scopes` text, | ||
`revoked` tinyint(1) NOT NULL, | ||
PRIMARY KEY (`identifier`), | ||
KEY `client` (`client`) | ||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; | ||
|
||
CREATE TABLE `oauth2_client` ( | ||
`identifier` varchar(32) NOT NULL, | ||
`name` varchar(128) NOT NULL, | ||
`secret` varchar(128) DEFAULT NULL, | ||
`redirectUris` text, | ||
`grants` text, | ||
`scopes` text, | ||
`active` tinyint(1) NOT NULL, | ||
`allowPlainTextPkce` tinyint(1) NOT NULL DEFAULT '0', | ||
PRIMARY KEY (`identifier`) | ||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; | ||
|
||
CREATE TABLE `oauth2_refresh_token` ( | ||
`identifier` char(80) NOT NULL, | ||
`access_token` char(80) DEFAULT NULL, | ||
`expiry` datetime NOT NULL, | ||
`revoked` tinyint(1) NOT NULL, | ||
PRIMARY KEY (`identifier`), | ||
KEY `access_token` (`access_token`) | ||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace League\Bundle\OAuth2ServerBundle\Tests\Acceptance; | ||
|
||
use League\Bundle\OAuth2ServerBundle\Event\UserResolveEvent; | ||
use League\Bundle\OAuth2ServerBundle\Manager\AccessTokenManagerInterface; | ||
use League\Bundle\OAuth2ServerBundle\Manager\AuthorizationCodeManagerInterface; | ||
use League\Bundle\OAuth2ServerBundle\Manager\ClientManagerInterface; | ||
use League\Bundle\OAuth2ServerBundle\Manager\RefreshTokenManagerInterface; | ||
use League\Bundle\OAuth2ServerBundle\Model\AccessToken; | ||
use League\Bundle\OAuth2ServerBundle\Model\AuthorizationCode; | ||
use League\Bundle\OAuth2ServerBundle\Model\Client; | ||
use League\Bundle\OAuth2ServerBundle\Model\RefreshToken; | ||
use League\Bundle\OAuth2ServerBundle\OAuth2Events; | ||
use League\Bundle\OAuth2ServerBundle\Service\CredentialsRevokerInterface; | ||
use League\Bundle\OAuth2ServerBundle\Tests\Fixtures\FakeAccessTokenManager; | ||
use League\Bundle\OAuth2ServerBundle\Tests\Fixtures\FakeAuthorizationCodeManager; | ||
use League\Bundle\OAuth2ServerBundle\Tests\Fixtures\FakeClientManager; | ||
use League\Bundle\OAuth2ServerBundle\Tests\Fixtures\FakeCredentialsRevoker; | ||
use League\Bundle\OAuth2ServerBundle\Tests\Fixtures\FakeRefreshTokenManager; | ||
use League\Bundle\OAuth2ServerBundle\Tests\Fixtures\FixtureFactory; | ||
use League\Bundle\OAuth2ServerBundle\Tests\TestHelper; | ||
use League\Bundle\OAuth2ServerBundle\Tests\TestKernel; | ||
use League\Bundle\OAuth2ServerBundle\ValueObject\RedirectUri; | ||
use PHPUnit\Framework\MockObject\MockObject; | ||
use Symfony\Bundle\FrameworkBundle\Console\Application; | ||
use Symfony\Component\HttpKernel\KernelInterface; | ||
|
||
class CustomPersistenceManagerTest extends AbstractAcceptanceTest | ||
{ | ||
private AccessTokenManagerInterface&MockObject $accessTokenManager; | ||
private ClientManagerInterface&MockObject $clientManager; | ||
private RefreshTokenManagerInterface&MockObject $refreshTokenManager; | ||
private AuthorizationCodeManagerInterface&MockObject $authCodeManager; | ||
|
||
protected function setUp(): void | ||
{ | ||
$this->client = self::createClient(); | ||
$this->accessTokenManager = $this->createMock(AccessTokenManagerInterface::class); | ||
$this->clientManager = $this->createMock(ClientManagerInterface::class); | ||
$this->refreshTokenManager = $this->createMock(RefreshTokenManagerInterface::class); | ||
$this->authCodeManager = $this->createMock(AuthorizationCodeManagerInterface::class); | ||
$this->application = new Application($this->client->getKernel()); | ||
} | ||
|
||
public function testRegisteredServices(): void | ||
{ | ||
static::assertInstanceOf(FakeAccessTokenManager::class, $this->client->getContainer()->get(AccessTokenManagerInterface::class)); | ||
static::assertInstanceOf(FakeAuthorizationCodeManager::class, $this->client->getContainer()->get(AuthorizationCodeManagerInterface::class)); | ||
static::assertInstanceOf(FakeClientManager::class, $this->client->getContainer()->get(ClientManagerInterface::class)); | ||
static::assertInstanceOf(FakeRefreshTokenManager::class, $this->client->getContainer()->get(RefreshTokenManagerInterface::class)); | ||
static::assertInstanceOf(FakeCredentialsRevoker::class, $this->client->getContainer()->get(CredentialsRevokerInterface::class)); | ||
} | ||
|
||
public function testSuccessfulClientCredentialsRequest(): void | ||
{ | ||
$this->accessTokenManager->expects(self::atLeastOnce())->method('find')->willReturn(null); | ||
$this->accessTokenManager->expects(self::atLeastOnce())->method('save'); | ||
$this->client->getContainer()->set('test.access_token_manager', $this->accessTokenManager); | ||
|
||
$this->clientManager->expects(self::atLeastOnce())->method('find')->with('foo')->willReturn(new Client('name', 'foo', 'secret')); | ||
$this->client->getContainer()->set('test.client_manager', $this->clientManager); | ||
|
||
$this->client->request('POST', '/token', [ | ||
'client_id' => 'foo', | ||
'client_secret' => 'secret', | ||
'grant_type' => 'client_credentials', | ||
]); | ||
|
||
$this->client->getResponse(); | ||
static::assertResponseIsSuccessful(); | ||
} | ||
|
||
public function testSuccessfulPasswordRequest(): void | ||
{ | ||
$this->accessTokenManager->expects(self::atLeastOnce())->method('find')->willReturn(null); | ||
$this->accessTokenManager->expects(self::atLeastOnce())->method('save'); | ||
$this->client->getContainer()->set('test.access_token_manager', $this->accessTokenManager); | ||
|
||
$this->clientManager->expects(self::atLeastOnce())->method('find')->with('foo')->willReturn(new Client('name', 'foo', 'secret')); | ||
$this->client->getContainer()->set('test.client_manager', $this->clientManager); | ||
|
||
$eventDispatcher = $this->client->getContainer()->get('event_dispatcher'); | ||
$eventDispatcher->addListener(OAuth2Events::USER_RESOLVE, static function (UserResolveEvent $event): void { | ||
$event->setUser(FixtureFactory::createUser()); | ||
}); | ||
|
||
$this->client->request('POST', '/token', [ | ||
'client_id' => 'foo', | ||
'client_secret' => 'secret', | ||
'grant_type' => 'password', | ||
'username' => 'user', | ||
'password' => 'pass', | ||
]); | ||
|
||
$this->client->getResponse(); | ||
static::assertResponseIsSuccessful(); | ||
} | ||
|
||
public function testSuccessfulRefreshTokenRequest(): void | ||
{ | ||
$client = new Client('name', 'foo', 'secret'); | ||
$accessToken = new AccessToken('access_token', new \DateTimeImmutable('+1 hour'), $client, 'user', []); | ||
$refreshToken = new RefreshToken('refresh_token', new \DateTimeImmutable('+1 month'), $accessToken); | ||
|
||
$this->refreshTokenManager->expects(self::atLeastOnce())->method('find')->willReturn($refreshToken, null); | ||
$this->client->getContainer()->set('test.refresh_token_manager', $this->refreshTokenManager); | ||
|
||
$this->accessTokenManager->expects(self::atLeastOnce())->method('find')->willReturn($accessToken, null); | ||
$this->accessTokenManager->expects(self::atLeastOnce())->method('save'); | ||
$this->client->getContainer()->set('test.access_token_manager', $this->accessTokenManager); | ||
|
||
$this->clientManager->expects(self::atLeastOnce())->method('find')->with('foo')->willReturn($client); | ||
$this->client->getContainer()->set('test.client_manager', $this->clientManager); | ||
|
||
$this->client->request('POST', '/token', [ | ||
'client_id' => 'foo', | ||
'client_secret' => 'secret', | ||
'grant_type' => 'refresh_token', | ||
'refresh_token' => TestHelper::generateEncryptedPayload($refreshToken), | ||
]); | ||
|
||
$this->client->getResponse(); | ||
static::assertResponseIsSuccessful(); | ||
} | ||
|
||
public function testSuccessfulAuthorizationCodeRequest(): void | ||
{ | ||
$client = new Client('name', 'foo', 'secret'); | ||
$client->setRedirectUris(new RedirectUri('https://example.org/oauth2/redirect-uri')); | ||
$authCode = new AuthorizationCode('authorization_code', new \DateTimeImmutable('+2 minute'), $client, 'user', []); | ||
|
||
$this->authCodeManager->expects(self::atLeastOnce())->method('find')->willReturn($authCode, null); | ||
$this->client->getContainer()->set('test.authorization_code_manager', $this->authCodeManager); | ||
|
||
$this->accessTokenManager->expects(self::atLeastOnce())->method('find')->willReturn(null); | ||
$this->accessTokenManager->expects(self::atLeastOnce())->method('save'); | ||
$this->client->getContainer()->set('test.access_token_manager', $this->accessTokenManager); | ||
|
||
$this->clientManager->expects(self::atLeastOnce())->method('find')->with('foo')->willReturn($client); | ||
$this->client->getContainer()->set('test.client_manager', $this->clientManager); | ||
|
||
$this->client->request('POST', '/token', [ | ||
'client_id' => 'foo', | ||
'client_secret' => 'secret', | ||
'grant_type' => 'authorization_code', | ||
'redirect_uri' => 'https://example.org/oauth2/redirect-uri', | ||
'code' => TestHelper::generateEncryptedAuthCodePayload($authCode), | ||
]); | ||
|
||
$this->client->getResponse(); | ||
static::assertResponseIsSuccessful(); | ||
} | ||
|
||
protected static function createKernel(array $options = []): KernelInterface | ||
{ | ||
return new TestKernel( | ||
'test', | ||
false, | ||
[ | ||
'custom' => [ | ||
'access_token_manager' => 'test.access_token_manager', | ||
'authorization_code_manager' => 'test.authorization_code_manager', | ||
'client_manager' => 'test.client_manager', | ||
'refresh_token_manager' => 'test.refresh_token_manager', | ||
'credentials_revoker' => 'test.credentials_revoker', | ||
], | ||
] | ||
); | ||
} | ||
} |
Oops, something went wrong.