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

[pull] master from thephpleague:master #5

Merged
merged 43 commits into from
Jun 16, 2024
Merged
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
5ddd3c4
fix client credentials example
Sephster Mar 25, 2024
f9f73bf
Fix password grant example
Sephster Mar 26, 2024
58cb760
fix device code examples
Sephster Mar 26, 2024
a22f2b0
Fix styling issues
Sephster Mar 26, 2024
a7dfa4b
Fix phpstan errors
Sephster Mar 26, 2024
1d0c6e2
Update scrutinizer PHP version
Sephster Mar 26, 2024
496df40
Be more specific with Scrutinizer PHP version
Sephster Mar 26, 2024
c845561
downgrade scrutinizer PHP version
Sephster Mar 26, 2024
0fe1fe7
Fix reference to $this
Sephster Mar 27, 2024
f4974e6
Remove unusued import
Sephster Mar 27, 2024
38a67b7
Make autoload run earlier
Sephster Mar 27, 2024
2e5b055
Make autoload run earlier
Sephster Mar 27, 2024
6e29082
Remove unneccessary if check
Sephster Mar 27, 2024
0858a2c
Update changelog for v9 RC1
Sephster Mar 27, 2024
ca511c1
Add PR # for toString change
Sephster Mar 27, 2024
db79663
Change identifier types to int or string
Sephster Apr 9, 2024
bd459d7
Change getUserIdentifier to return an int
Sephster Apr 14, 2024
39d3cd3
Change method signatures for ClientId to accept ints
Sephster Apr 14, 2024
43922ff
Make basic auth case insensitive
Sephster Apr 14, 2024
a53f144
Update changelog PR number
Sephster Apr 14, 2024
f7a19cd
Merge pull request #1403 from thephpleague/case-insensitive-basic-auth
Sephster Apr 14, 2024
6dbbc9f
Fix docblock return type
Sephster Apr 22, 2024
8264a40
Revert identifiers to strings
Sephster Apr 22, 2024
e4dfdd6
Make identifiers strings
Sephster Apr 22, 2024
d9d061d
Removed mixed statements and made getRequestParameter stricter typed
Sephster Apr 29, 2024
fc15889
Revert type to mixed for makeRedirectUri return
Sephster Apr 29, 2024
689b2c7
Revert type changes to getRequestParameter and co
Sephster May 5, 2024
74f8abc
Revert mixed types
Sephster May 5, 2024
d1dc453
Fix CS issues
Sephster May 5, 2024
efd742b
Parse request params
eugene-borovov May 6, 2024
df39bf6
Fix style
eugene-borovov May 6, 2024
7596935
Merge pull request #1407 from eugene-borovov/fix-passport-compatibility
Sephster May 10, 2024
16bd02c
Fix styling
Sephster May 10, 2024
02a85f2
Merge master into this branch
Sephster May 13, 2024
669debf
revert scopeId to scope
Sephster May 13, 2024
c64d95e
revert formatting changes
Sephster May 13, 2024
26b33a6
Update changelog
Sephster May 13, 2024
7b65618
Merge pull request #1402 from thephpleague/fix-passport-compatibility
Sephster May 13, 2024
00a2ce5
Update changelog to include typing changes
Sephster May 13, 2024
7795ca2
Update changelog for v9
Sephster May 13, 2024
92c6519
Fix styleci issues
Sephster May 13, 2024
42ba1f8
Fix style CI issues
Sephster May 13, 2024
2ed9e5f
Fix style issue
Sephster May 13, 2024
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 .scrutinizer.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
build:
environment:
php:
version: 7.4
version: 8.3.3
nodes:
analysis:
tests:
31 changes: 28 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [Unreleased]
## [9.0.0] - released 2024-05-13
### Added
- Device Authorization Grant added (PR #1074)
- GrantTypeInterface has a new function, `revokeRefreshTokens()` for enabling or disabling refresh tokens after use (PR #1375)
@@ -14,12 +14,35 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Added function `getKeyContents()` to the `CryptKeyInterface` (PR #1375)

### Fixed
- Basic authorization is now case insensitive (PR #1403)
- If a refresh token has expired, been revoked, cannot be decrypted, or does not belong to the correct client, the server will now issue an `invalid_grant` error and a HTTP 400 response. In previous versions the server incorrectly issued an `invalid_request` and HTTP 401 response (PR #1042) (PR #1082)

### Changed
- All interfaces now specify types for all params and return values. Strict typing enforced (PR #1074)
- Request parameters are now parsed into strings to use internally in the library (PR #1402)
- Authorization Request objects are now created through the factory method, `createAuthorizationRequest()` (PR #1111)
- Changed parameters for `finalizeScopes()` to allow a reference to an auth code ID (PR #1112)
- AccessTokenEntityInterface now requires the implementation of `toString()` instead of the magic method `__toString()` (PR #XXXX)
- AccessTokenEntityInterface now requires the implementation of `toString()` instead of the magic method `__toString()` (PR #1395)

### Removed
- Removed message property from OAuthException HTTP response. Now just use error_description as per the OAuth 2 spec (PR #1375)

## [9.0.0-RC1] - released 2024-03-27
### Added
- Device Authorization Grant added (PR #1074)
- GrantTypeInterface has a new function, `revokeRefreshTokens()` for enabling or disabling refresh tokens after use (PR #1375)
- A CryptKeyInterface to allow developers to change the CryptKey implementation with greater ease (PR #1044)
- The authorization server can now finalize scopes when a client uses a refresh token (PR #1094)
- An AuthorizationRequestInterface to make it easier to extend the AuthorizationRequest (PR #1110)
- Added function `getKeyContents()` to the `CryptKeyInterface` (PR #1375)

### Fixed
- If a refresh token has expired, been revoked, cannot be decrypted, or does not belong to the correct client, the server will now issue an `invalid_grant` error and a HTTP 400 response. In previous versions the server incorrectly issued an `invalid_request` and HTTP 401 response (PR #1042) (PR #1082)

### Changed
- Authorization Request objects are now created through the factory method, `createAuthorizationRequest()` (PR #1111)
- Changed parameters for `finalizeScopes()` to allow a reference to an auth code ID (PR #1112)
- AccessTokenEntityInterface now requires the implementation of `toString()` instead of the magic method `__toString()` (PR #1395)

### Removed
- Removed message property from OAuthException HTTP response. Now just use error_description as per the OAuth 2 spec (PR #1375)
@@ -623,7 +646,9 @@ Version 5 is a complete code rewrite.

- First major release

[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/8.5.4...HEAD
[Unreleased]: https://github.com/thephpleague/oauth2-server/compare/9.0.0...HEAD
[9.0.0]: https://github.com/thephpleague/oauth2-server/compare/9.0.0-RC1...9.0.0
[9.0.0-RC1]: https://github.com/thephpleague/oauth2-server/compare/8.5.4...9.0.0-RC1
[8.5.4]: https://github.com/thephpleague/oauth2-server/compare/8.5.3...8.5.4
[8.5.3]: https://github.com/thephpleague/oauth2-server/compare/8.5.2...8.5.3
[8.5.2]: https://github.com/thephpleague/oauth2-server/compare/8.5.1...8.5.2
6 changes: 3 additions & 3 deletions examples/public/api.php
Original file line number Diff line number Diff line change
@@ -2,15 +2,15 @@

declare(strict_types=1);

include __DIR__ . '/../vendor/autoload.php';

use League\OAuth2\Server\Middleware\ResourceServerMiddleware;
use League\OAuth2\Server\ResourceServer;
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\App;

include __DIR__ . '/../vendor/autoload.php';

$app = new App([
// Add the resource server to the DI container
ResourceServer::class => function () {
@@ -33,7 +33,7 @@
// An example endpoint secured with OAuth 2.0
$app->get(
'/users',
function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
function (ServerRequestInterface $request, ResponseInterface $response) {
$users = [
[
'id' => 123,
4 changes: 2 additions & 2 deletions examples/public/client_credentials.php
Original file line number Diff line number Diff line change
@@ -10,6 +10,8 @@

declare(strict_types=1);

include __DIR__ . '/../vendor/autoload.php';

use Laminas\Diactoros\Stream;
use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Exception\OAuthServerException;
@@ -21,8 +23,6 @@
use Psr\Http\Message\ServerRequestInterface;
use Slim\App;

include __DIR__ . '/../vendor/autoload.php';

$app = new App([
'settings' => [
'displayErrorDetails' => true,
6 changes: 3 additions & 3 deletions examples/public/middleware_use.php
Original file line number Diff line number Diff line change
@@ -10,6 +10,8 @@

declare(strict_types=1);

include __DIR__ . '/../vendor/autoload.php';

use Laminas\Diactoros\Stream;
use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Grant\AuthCodeGrant;
@@ -26,8 +28,6 @@
use Psr\Http\Message\ServerRequestInterface;
use Slim\App;

include __DIR__ . '/../vendor/autoload.php';

$app = new App([
'settings' => [
'displayErrorDetails' => true,
@@ -87,7 +87,7 @@

// Secured API
$app->group('/api', function (): void {
$this->get('/user', function (ServerRequestInterface $request, ResponseInterface $response) {
$app->get('/user', function (ServerRequestInterface $request, ResponseInterface $response) {
$params = [];

if (in_array('basic', $request->getAttribute('oauth_scopes', []))) {
5 changes: 2 additions & 3 deletions examples/src/Entities/UserEntity.php
Original file line number Diff line number Diff line change
@@ -18,10 +18,9 @@ class UserEntity implements UserEntityInterface
{
/**
* Return the user's identifier.
*
*/
public function getIdentifier(): mixed
public function getIdentifier(): string
{
return 1;
return '1';
}
}
7 changes: 6 additions & 1 deletion examples/src/Repositories/AccessTokenRepository.php
Original file line number Diff line number Diff line change
@@ -49,11 +49,16 @@ public function isAccessTokenRevoked($tokenId): bool
public function getNewToken(ClientEntityInterface $clientEntity, array $scopes, $userIdentifier = null): AccessTokenEntityInterface
{
$accessToken = new AccessTokenEntity();

$accessToken->setClient($clientEntity);

foreach ($scopes as $scope) {
$accessToken->addScope($scope);
}
$accessToken->setUserIdentifier($userIdentifier);

if ($userIdentifier !== null) {
$accessToken->setUserIdentifier((string) $userIdentifier);
}

return $accessToken;
}
5 changes: 1 addition & 4 deletions examples/src/Repositories/ClientRepository.php
Original file line number Diff line number Diff line change
@@ -59,10 +59,7 @@ public function validateClient($clientIdentifier, $clientSecret, $grantType): bo
return false;
}

if (
$clients[$clientIdentifier]['is_confidential'] === true
&& password_verify($clientSecret, $clients[$clientIdentifier]['secret']) === false
) {
if (password_verify($clientSecret, $clients[$clientIdentifier]['secret']) === false) {
return false;
}

2 changes: 1 addition & 1 deletion examples/src/Repositories/DeviceCodeRepository.php
Original file line number Diff line number Diff line change
@@ -53,7 +53,7 @@ public function getDeviceCodeEntityByDeviceCode($deviceCode): ?DeviceCodeEntityI
// The user identifier should be set when the user authenticates on the
// OAuth server, along with whether they approved the request
$deviceCodeEntity->setUserApproved(true);
$deviceCodeEntity->setUserIdentifier(1);
$deviceCodeEntity->setUserIdentifier('1');

return $deviceCodeEntity;
}
5 changes: 3 additions & 2 deletions examples/src/Repositories/UserRepository.php
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@
namespace OAuth2ServerExamples\Repositories;

use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Entities\UserEntityInterface;
use League\OAuth2\Server\Repositories\UserRepositoryInterface;
use OAuth2ServerExamples\Entities\UserEntity;

@@ -26,11 +27,11 @@ public function getUserEntityByUserCredentials(
$password,
$grantType,
ClientEntityInterface $clientEntity
) {
): ?UserEntityInterface {
if ($username === 'alex' && $password === 'whisky') {
return new UserEntity();
}

return;
return null;
}
}
2 changes: 2 additions & 0 deletions phpcs.xml.dist
Original file line number Diff line number Diff line change
@@ -14,6 +14,8 @@
<file>tests</file>
<file>examples</file>

<exclude-pattern>examples/vendor/*</exclude-pattern>

<rule ref="PSR12">
<exclude name="Generic.Files.LineLength.TooLong" />
</rule>
6 changes: 1 addition & 5 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
@@ -2,8 +2,4 @@ parameters:
level: 8
paths:
- src
- tests
ignoreErrors:
-
message: '#Call to an undefined method League\\OAuth2\\Server\\ResponseTypes\\ResponseTypeInterface::getAccessToken\(\)\.#'
path: tests/Grant/ClientCredentialsGrantTest.php
- tests
5 changes: 2 additions & 3 deletions src/CryptKey.php
Original file line number Diff line number Diff line change
@@ -27,7 +27,6 @@
use function openssl_pkey_get_private;
use function openssl_pkey_get_public;
use function sprintf;
use function strpos;
use function trigger_error;

class CryptKey implements CryptKeyInterface
@@ -43,13 +42,13 @@ class CryptKey implements CryptKeyInterface

public function __construct(string $keyPath, protected ?string $passPhrase = null, bool $keyPermissionsCheck = true)
{
if (strpos($keyPath, self::FILE_PREFIX) !== 0 && $this->isValidKey($keyPath, $this->passPhrase ?? '')) {
if (str_starts_with($keyPath, self::FILE_PREFIX) === false && $this->isValidKey($keyPath, $this->passPhrase ?? '')) {
$this->keyContents = $keyPath;
$this->keyPath = '';
// There's no file, so no need for permission check.
$keyPermissionsCheck = false;
} elseif (is_file($keyPath)) {
if (strpos($keyPath, self::FILE_PREFIX) !== 0) {
if (str_starts_with($keyPath, self::FILE_PREFIX) === false) {
$keyPath = self::FILE_PREFIX . $keyPath;
}

6 changes: 5 additions & 1 deletion src/Entities/RefreshTokenEntityInterface.php
Original file line number Diff line number Diff line change
@@ -18,13 +18,17 @@ interface RefreshTokenEntityInterface
{
/**
* Get the token's identifier.
*
* @return non-empty-string
*/
public function getIdentifier(): string;

/**
* Set the token's identifier.
*
* @param non-empty-string $identifier
*/
public function setIdentifier(mixed $identifier): void;
public function setIdentifier(string $identifier): void;

/**
* Get the token's expiry date time.
2 changes: 2 additions & 0 deletions src/Entities/ScopeEntityInterface.php
Original file line number Diff line number Diff line change
@@ -18,6 +18,8 @@ interface ScopeEntityInterface extends JsonSerializable
{
/**
* Get the scope's identifier.
*
* @return non-empty-string
*/
public function getIdentifier(): string;
}
10 changes: 8 additions & 2 deletions src/Entities/TokenInterface.php
Original file line number Diff line number Diff line change
@@ -18,13 +18,17 @@ interface TokenInterface
{
/**
* Get the token's identifier.
*
* @return non-empty-string
*/
public function getIdentifier(): string;

/**
* Set the token's identifier.
*
* @param non-empty-string $identifier
*/
public function setIdentifier(mixed $identifier): void;
public function setIdentifier(string $identifier): void;

/**
* Get the token's expiry date time.
@@ -45,8 +49,10 @@ public function setUserIdentifier(string $identifier): void;

/**
* Get the token user's identifier.
*
* @return non-empty-string|null
*/
public function getUserIdentifier(): string|int|null;
public function getUserIdentifier(): string|null;

/**
* Get the client that the token was issued to.
16 changes: 9 additions & 7 deletions src/Entities/Traits/AccessTokenTrait.php
Original file line number Diff line number Diff line change
@@ -59,12 +59,6 @@ public function initJwtConfiguration(): void
*/
private function convertToJWT(): Token
{
$userIdentifier = $this->getUserIdentifier();

if ($userIdentifier === null) {
throw new RuntimeException('JWT access tokens MUST contain a subject identifier');
}

$this->initJwtConfiguration();

return $this->jwtConfiguration->builder()
@@ -73,7 +67,7 @@ private function convertToJWT(): Token
->issuedAt(new DateTimeImmutable())
->canOnlyBeUsedAfter(new DateTimeImmutable())
->expiresAt($this->getExpiryDateTime())
->relatedTo($userIdentifier)
->relatedTo($this->getSubjectIdentifier())
->withClaim('scopes', $this->getScopes())
->getToken($this->jwtConfiguration->signer(), $this->jwtConfiguration->signingKey());
}
@@ -104,4 +98,12 @@ abstract public function getScopes(): array;
* @return non-empty-string
*/
abstract public function getIdentifier(): string;

/**
* @return non-empty-string
*/
private function getSubjectIdentifier(): string
{
return $this->getUserIdentifier() ?? $this->getClient()->getIdentifier();
}
}
3 changes: 3 additions & 0 deletions src/Entities/Traits/DeviceCodeTrait.php
Original file line number Diff line number Diff line change
@@ -59,6 +59,9 @@ abstract public function getExpiryDateTime(): DateTimeImmutable;
*/
abstract public function getScopes(): array;

/**
* @return non-empty-string
*/
abstract public function getIdentifier(): string;

public function getLastPolledAt(): ?DateTimeImmutable
5 changes: 4 additions & 1 deletion src/Entities/Traits/EntityTrait.php
Original file line number Diff line number Diff line change
@@ -27,7 +27,10 @@ public function getIdentifier(): string
return $this->identifier;
}

public function setIdentifier(mixed $identifier): void
/**
* @param non-empty-string $identifier
*/
public function setIdentifier(string $identifier): void
{
$this->identifier = $identifier;
}
4 changes: 3 additions & 1 deletion src/Entities/UserEntityInterface.php
Original file line number Diff line number Diff line change
@@ -16,6 +16,8 @@ interface UserEntityInterface
{
/**
* Return the user's identifier.
*
* @return non-empty-string
*/
public function getIdentifier(): mixed;
public function getIdentifier(): string;
}
Loading