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

Add OpenID Response Type #1316

Open
wants to merge 57 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
6d95ac4
ID Token response for OIDC implementation
marcriemer Nov 24, 2022
55e0d4e
Finalization of IdTokenResponce implementation
marcriemer Nov 25, 2022
d965e1a
Add IdTokenResponce related tests
marcriemer Nov 25, 2022
f588884
Fixed Coding Style
marcriemer Nov 25, 2022
a7a2c3e
Removed ServerRequestInterface
marcriemer Nov 25, 2022
656d07e
Fixed code quality
marcriemer Nov 25, 2022
bbe4c20
Remove old ID token related const
marcriemer Nov 25, 2022
a8c58c9
Fixed CI issue
marcriemer Nov 25, 2022
dd55a7d
Fixed class name for id token events
marcriemer Nov 26, 2022
4635e33
Apply fixes from StyleCI
marcriemer Nov 26, 2022
d2be736
Merge pull request #1 from marcriemer/analysis-ZnG0ON
marcriemer Nov 26, 2022
8e00c4e
Fixed id token events and add ClaimExtractorIntercace
marcriemer Nov 26, 2022
9afb766
Apply fixes from StyleCI
marcriemer Nov 26, 2022
9249d96
Merge pull request #3 from marcriemer/analysis-g6LQJk
marcriemer Nov 26, 2022
e7c54f3
Add UserInfoResponse
marcriemer Nov 28, 2022
35af257
Add ServerRequestInterface to getClaimSetEntry
marcriemer Nov 28, 2022
e9975a3
Revert "Add ServerRequestInterface to getClaimSetEntry"
marcriemer Nov 29, 2022
5befb21
Fixed backwards compatibility
marcriemer Nov 29, 2022
d37193b
Fixed testGenerateHttpResponseWithIdToken test
marcriemer Nov 29, 2022
ffb4dc2
Apply fixes from StyleCI
marcriemer Nov 29, 2022
65a14cb
Merge pull request #4 from marcriemer/analysis-g6LkZw
marcriemer Nov 29, 2022
a3856e6
lcobucci/jwt 3.4.6 compatibility
marcriemer Nov 29, 2022
a3cb121
Apply fixes from StyleCI
marcriemer Nov 29, 2022
4e49f90
Merge pull request #5 from marcriemer/analysis-Ko3NmQ
marcriemer Nov 29, 2022
68e1ca8
Add openid default claims
marcriemer Apr 12, 2023
f22eb58
Apply fixes from StyleCI
marcriemer Apr 12, 2023
f378d9e
Merge pull request #6 from marcriemer/analysis-o75ZmB
marcriemer Apr 12, 2023
7fe78ac
Merge remote-tracking branch 'upstream/master' into openid-implementa…
Sephster Jun 8, 2023
2503f38
Merge branch 'thephpleague:master' into openid-implementation
marcriemer Jul 31, 2023
9fd5db9
Merge branch 'thephpleague:master' into openid-implementation
marcriemer Aug 30, 2023
ef616e4
Fixed interface name of ClaimExtractorInterface
marcriemer Sep 6, 2023
5f1db1e
Renamed parameter
marcriemer Sep 7, 2023
7bc34f7
Fixed ClaimExtractor
marcriemer Oct 26, 2023
7c2141e
Merge branch 'thephpleague:master' into openid-implementation
marcriemer Oct 26, 2023
4e25d35
Merge branch 'thephpleague:master' into openid-implementation
marcriemer Nov 12, 2023
2e710f9
Changed ClaimSetInterface into ClaimSetEntryInterface
marcriemer Mar 28, 2024
f3e1990
solve conflicts
marcriemer May 19, 2024
0170cf2
Apply fixes from StyleCI
marcriemer May 19, 2024
f50d749
Merge pull request #8 from marcriemer/analysis-PGEDro
marcriemer May 19, 2024
cdc66d3
Revert "Apply fixes from StyleCI"
marcriemer May 19, 2024
b3f2ca8
Merge pull request #9 from marcriemer/revert-8-analysis-PGEDro
marcriemer May 19, 2024
baaee9f
Merge branch 'thephpleague:master' into openid-implementation
marcriemer May 21, 2024
4219c8c
Apply fixes from StyleCI
marcriemer May 21, 2024
594b060
Merge pull request #12 from marcriemer/analysis-nebyG0
marcriemer May 21, 2024
95f6ac3
Fix Lcobucci incompatibility and core quality
marcriemer May 26, 2024
174c2b0
Fix unit test
marcriemer May 26, 2024
58cbe2a
Fix code quality issues
marcriemer May 26, 2024
9113224
Apply fixes from StyleCI
marcriemer May 26, 2024
c0d74c6
Merge pull request #14 from marcriemer/analysis-kYbjYy
marcriemer May 26, 2024
1bdd17d
Merge branch 'thephpleague:master' into openid-implementation
marcriemer Oct 22, 2024
4ab66a7
ClaimSetRepositoryInterface::getClaimSet returns a ClaimSetInterface
marcriemer Oct 22, 2024
66e4dc6
Apply fixes from StyleCI
marcriemer Oct 22, 2024
d2447d8
Merge pull request #17 from marcriemer/analysis-4wLr71
marcriemer Oct 22, 2024
799455d
fixed doc
marcriemer Oct 22, 2024
b1400a1
Fixed ClaimExtractor
marcriemer Oct 22, 2024
9b30823
Apply fixes from StyleCI
marcriemer Oct 22, 2024
d4f5bf5
Merge pull request #18 from marcriemer/analysis-KoOamD
marcriemer Oct 22, 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
43 changes: 43 additions & 0 deletions examples/src/Repositories/IdTokenRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

declare(strict_types=1);

namespace League\OAuth2\Server\Repositories;

use DateTimeImmutable;
use Lcobucci\JWT\Encoding\ChainedFormatter;
use Lcobucci\JWT\Encoding\JoseEncoder;
use Lcobucci\JWT\Token\Builder;
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;

/**
* Exmaple implemnation of IdTokenRepositoryInterface
*
* @author Marc Riemer <[email protected]>
* @license http://opensource.org/licenses/MIT MIT
*/
class IdTokenRepository implements IdTokenRepositoryInterface
{
public function __construct(private string $issuedBy, private ?string $nonce = null)
{
}

/**
* {@inheritdoc}
*/
public function getBuilder(AccessTokenEntityInterface $accessToken): Builder
{
$builder = (new Builder(new JoseEncoder(), ChainedFormatter::withUnixTimestampDates()))
->permittedFor($accessToken->getClient()->getIdentifier())
->issuedBy($this->issuedBy)
->issuedAt(new DateTimeImmutable())
->expiresAt($accessToken->getExpiryDateTime())
->relatedTo($accessToken->getUserIdentifier());

if ($this->nonce) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't the nonce come from the authorization request ? Configuring as a constructor argument of the repository is probably not usable. It would be great to have an actual example of the openid setup.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, the example is far from being perfect. The nonce must be part of the AuthCodeEntityInterface and used by ClaimSetRepositoryInterface to create ClaimSetInterface with the nonce claim.

$builder->withClaim('nonce', $this->nonce);
}

return $builder;
}
}
178 changes: 178 additions & 0 deletions src/ClaimExtractor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
<?php

declare(strict_types=1);

namespace League\OAuth2\Server;

use InvalidArgumentException;
use League\OAuth2\Server\Entities\ClaimSetEntry;
use League\OAuth2\Server\Entities\ClaimSetEntryInterface;
use League\OAuth2\Server\Entities\ClaimSetInterface;
use League\OAuth2\Server\Entities\ScopeEntityInterface;

use function array_filter;
use function array_intersect;
use function array_keys;
use function array_merge;
use function in_array;
use function sprintf;

/**
* ClaimExtractor
*
* @link https://github.com/steverhoades/oauth2-openid-connect-server
*
* @author Steve Rhoades <[email protected]>
* @author Marc Riemer <[email protected]>
*/
class ClaimExtractor implements ClaimExtractorInterface
{
/**
* claimSetEntries
*
* @var ClaimSetEntryInterface[]
*/
protected array $claimSetEntries = [];

/**
* Protected claims
*
* @var string[]
*/
protected array $protectedClaims = ['profile', 'email', 'address', 'phone'];

/**
* ClaimExtractor constructor
*
* @param array<int, ClaimSetEntryInterface> $claimSetEntries
*/
public function __construct(array $claimSetEntries = [])
{
$this->claimSetEntries = self::getDefaultClaimSetEnties();
foreach ($claimSetEntries as $claimSetEntry) {
$this->addClaimSetEntry($claimSetEntry);
}
}

/**
* @return $this
*
* @throws \InvalidArgumentException
*/
public function addClaimSetEntry(ClaimSetEntryInterface $claimSetEntry): ClaimExtractor
{
if (in_array($claimSetEntry->getScope(), $this->protectedClaims) && !$this->getClaimSetEntry($claimSetEntry->getScope())) {
throw new InvalidArgumentException(
sprintf('%s is a protected scope and is pre-defined by the OpenID Connect specification.', $claimSetEntry->getScope())
);
}

$this->claimSetEntries[] = $claimSetEntry;

return $this;
}

public function getClaimSetEntry(string $scope): ?ClaimSetEntryInterface
{
foreach ($this->claimSetEntries as $entry) {
if ($entry->getScope() === $scope) {
return $entry;
}
}

return null;
}

/**
* Get claimSetEntries
*
* @return array<ClaimSetInterface>
*/
public function getClaimSetEntries(): array
{
return $this->claimSetEntries;
}

/**
* {@inheritdoc}
*/
public function extract(array $scopes, array $claims): array
{
$claimData = [];
$keys = array_keys($claims);

foreach ($scopes as $scope) {
$scopeName = ($scope instanceof ScopeEntityInterface) ? $scope->getIdentifier() : $scope;

$claimSet = $this->getClaimSetEntry($scopeName);
if (null === $claimSet) {
continue;
}

$intersected = array_intersect($claimSet->getClaims(), $keys);

if (empty($intersected)) {
continue;
}

$data = array_filter(
$claims,
function ($key) use ($intersected) {
return in_array($key, $intersected);
},
ARRAY_FILTER_USE_KEY
);

$claimData = array_merge($claimData, $data);
}

return $claimData;
}

/**
* Create a array default openID connect claims
*
* @see http://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims
*
* @return ClaimSetEntry[]
*/
public static function getDefaultClaimSetEnties(): array
{
return [
new ClaimSetEntry('profile', [
'name',
'family_name',
'given_name',
'middle_name',
'nickname',
'preferred_username',
'profile',
'picture',
'website',
'gender',
'birthdate',
'zoneinfo',
'locale',
'updated_at',
]),
new ClaimSetEntry('email', [
'email',
'email_verified',
]),
new ClaimSetEntry('address', [
'address',
]),
new ClaimSetEntry('phone', [
'phone_number',
'phone_number_verified',
]),
new ClaimSetEntry('openid', [
'nonce',
'auth_time',
'acr',
'amr',
'azp',
]),
];
}
}
20 changes: 20 additions & 0 deletions src/ClaimExtractorInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace League\OAuth2\Server;

use League\OAuth2\Server\Entities\ScopeEntityInterface;

interface ClaimExtractorInterface
{
/**
* For given scopes and aggregated claims get all claims that have been configured on the extractor.
*
* @param array<int, ScopeEntityInterface> $scopes
* @param array<string, string> $claims
*
* @return array<string, string>
*/
public function extract(array $scopes, array $claims): array;
}
45 changes: 45 additions & 0 deletions src/Entities/ClaimSetEntry.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

declare(strict_types=1);

namespace League\OAuth2\Server\Entities;

/**
* ClaimSetEntry
*
* @author Steve Rhoades <[email protected]>
* @author Marc Riemer <[email protected]>
* @license http://opensource.org/licenses/MIT MIT
*/
class ClaimSetEntry implements ClaimSetEntryInterface
{
/**
* Summary of __construct
*
* @param string $scope Scope of the claimset
* @param string[] $claims The claims
*/
public function __construct(
protected string $scope,
protected array $claims
) {
}

/**
* Get scope
*/
public function getScope(): string
{
return $this->scope;
}

/**
* Get claims
*
* @return array<string, string>
*/
public function getClaims(): array
{
return $this->claims;
}
}
17 changes: 17 additions & 0 deletions src/Entities/ClaimSetEntryInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace League\OAuth2\Server\Entities;

/**
* ClaimSetEntryInterface
*
* @author Steve Rhoades <[email protected]>
* @author Marc Riemer <[email protected]>
* @license http://opensource.org/licenses/MIT MIT
*/
interface ClaimSetEntryInterface extends ClaimSetInterface
{
public function getScope(): string;
}
22 changes: 22 additions & 0 deletions src/Entities/ClaimSetInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace League\OAuth2\Server\Entities;

/**
* ClaimSetEntryInterface
*
* @author Steve Rhoades <[email protected]>
* @author Marc Riemer <[email protected]>
* @license http://opensource.org/licenses/MIT MIT
*/
interface ClaimSetInterface
{
/**
* Get Claims
*
* @return array<string, string>
*/
public function getClaims(): array;
}
33 changes: 33 additions & 0 deletions src/IdTokenClaimsCreatedEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace League\OAuth2\Server;

use Lcobucci\JWT\Builder;

/**
* IdTokenClaimsCreatedEvent Event helps to extend claims of the id_token
*
* A usecase is to add nonce If requested by the client
*
* @author Marc Riemer <[email protected]>
*/
final class IdTokenClaimsCreatedEvent extends IdTokenEvent
{
/**
* Builder
*/
private Builder $builder;

public function __construct(string $name, Builder $builder)
{
parent::__construct($name);
$this->builder = $builder;
}

public function getBuilder(): Builder
{
return $this->builder;
}
}
20 changes: 20 additions & 0 deletions src/IdTokenEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

declare(strict_types=1);

namespace League\OAuth2\Server;

use League\OAuth2\Server\EventEmitting\AbstractEvent;

/**
* IdTokenEvent
*
* @author Marc Riemer <[email protected]>
*/
class IdTokenEvent extends AbstractEvent
{
public const ID_TOKEN_ISSUED = 'id_token.issued';

// This event can be used to extent claims of the id_token
public const ID_TOKEN_CLAIMS_CREATED = 'id_token.claims.created';
}
Loading