master f20665aeeeec cached
36 files
57.8 KB
16.9k tokens
55 symbols
1 requests
Download .txt
Repository: steverhoades/oauth2-openid-connect-server
Branch: master
Commit: f20665aeeeec
Files: 36
Total size: 57.8 KB

Directory structure:
gitextract_m4beu0jy/

├── .gitattributes
├── .github/
│   └── workflows/
│       └── ci.yml
├── .gitignore
├── .scrutinizer.yml
├── LICENSE
├── README.md
├── composer.json
├── examples/
│   ├── README.md
│   ├── composer.json
│   ├── public/
│   │   ├── auth_code.php
│   │   ├── client_credentials.php
│   │   ├── implicit.php
│   │   └── password.php
│   └── src/
│       ├── Entities/
│       │   └── UserEntity.php
│       └── Repositories/
│           ├── IdentityRepository.php
│           └── ScopeRepository.php
├── phpunit.xml.dist
├── src/
│   ├── ClaimExtractor.php
│   ├── Entities/
│   │   ├── ClaimSetEntity.php
│   │   ├── ClaimSetEntityInterface.php
│   │   ├── ClaimSetInterface.php
│   │   └── ScopeInterface.php
│   ├── Exception/
│   │   └── InvalidArgumentException.php
│   ├── IdTokenResponse.php
│   └── Repositories/
│       ├── ClaimSetRepositoryInterface.php
│       └── IdentityProviderInterface.php
└── tests/
    ├── Bootstrap.php
    ├── ClaimExtractorTest.php
    ├── ResponseTypes/
    │   └── IdTokenResponseTest.php
    └── Stubs/
        ├── IdentityProvider.php
        ├── UserEntity.php
        ├── UserNoClaimSetEntity.php
        ├── UserNoIdentifierEntity.php
        ├── private.key
        ├── private.key.crlf
        └── public.key

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitattributes
================================================
* text=auto

/tests export-ignore
/.gitattributes export-ignore
/.gitignore export-ignore
/.github export-ignore
.scrutinizer.yml export-ignore
/phpunit.xml.dist export-ignore
/README.md export-ignore


================================================
FILE: .github/workflows/ci.yml
================================================
name: CI

on:
    push:
        branches:
            - master
            - develop
    pull_request:
    workflow_dispatch:

jobs:
    check_composer:
        name: Check composer.json
        runs-on: ubuntu-latest
        steps:
            - uses: actions/checkout@v4
            - uses: shivammathur/setup-php@v2
              with:
                  coverage: none
                  php-version: '8.3'
            - run: composer validate --strict --no-check-lock

    tests:
        name: "Tests on PHP ${{ matrix.php }}"
        runs-on: ubuntu-latest

        strategy:
            fail-fast: false
            matrix:
                php: [ '7.4', '8.0', '8.1', '8.2', '8.3' ]

        steps:
            -   uses: actions/checkout@v4
            -   uses: shivammathur/setup-php@v2
                with:
                    coverage: "none"
                    php-version: "${{ matrix.php }}"
                    ini-file: development

            -   name: Update permissions
                run: |
                    chmod 600 tests/Stubs/private.key
                    chmod 600 tests/Stubs/public.key

            -   name: Install dependencies
                run: composer update --ansi --no-progress --no-interaction

            -   name: Run tests
                run: php -d error_reporting="E_ALL & ~E_USER_DEPRECATED" vendor/bin/phpunit -v --colors=always


================================================
FILE: .gitignore
================================================
/vendor/
phpunit.xml
/build
composer.lock
/examples/*.key


================================================
FILE: .scrutinizer.yml
================================================
build:
    environment:
        php: 7.4.30

filter:
    excluded_paths:
        - tests/*
        - vendor/*
        - examples/*
checks:
    php:
        code_rating: true
        remove_extra_empty_lines: true
        remove_php_closing_tag: true
        remove_trailing_whitespace: true
        fix_use_statements:
            remove_unused: true
            preserve_multiple: false
            preserve_blanklines: true
            order_alphabetically: true
        fix_php_opening_tag: true
        fix_linefeed: true
        fix_line_ending: true
        fix_identation_4spaces: true
        fix_doc_comments: true
tools:
    external_code_coverage:
        timeout: 1800
    php_code_coverage: false
    php_code_sniffer:
        config:
            standard: PSR2
        filter:
            paths: ['src']
    php_loc:
        enabled: true
        excluded_dirs: [vendor, tests, examples]
    php_cpd:
        enabled: true
        excluded_dirs: [vendor, tests, examples]


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2018 Steve Rhoades <sedonami@gmail.com>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


================================================
FILE: README.md
================================================
# OAuth 2.0 OpenID Connect Server

[![Build Status](https://travis-ci.org/steverhoades/oauth2-openid-connect-server.svg?branch=master)](https://travis-ci.org/steverhoades/oauth2-openid-connect-server) [![Code Coverage](https://scrutinizer-ci.com/g/steverhoades/oauth2-openid-connect-server/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/steverhoades/oauth2-openid-connect-server/?branch=master) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/steverhoades/oauth2-openid-connect-server/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/steverhoades/oauth2-openid-connect-server/?branch=master)

This implements the OpenID Connect specification on top of The PHP League's [OAuth2 Server](https://github.com/thephpleague/oauth2-server).

## Requirements

* Requires PHP version 7.4 or greater.
* [league/oauth2-server](https://github.com/thephpleague/oauth2-server) 8.4.2 or greater.

Note: league/oauth2-server version may have a higher PHP requirement.

## Usage
The following classes will need to be configured and passed to the AuthorizationServer in order to provide OpenID Connect functionality.

1. IdentityRepository.  This MUST implement the OpenIDConnectServer\Repositories\IdentityProviderInterface and return the identity of the user based on the return value of $accessToken->getUserIdentifier().
   1. The IdentityRepository MUST return a UserEntity that implements the following interfaces
      1. OpenIDConnectServer\Entities\ClaimSetInterface
      1. League\OAuth2\Server\Entities\UserEntityInterface.
1. ClaimSet.  ClaimSet is a way to associate claims to a given scope.
1. ClaimExtractor. The ClaimExtractor takes an array of ClaimSets and in addition provides default claims for the OpenID Connect specified scopes of: profile, email, phone and address.
1. IdTokenResponse. This class must be passed to the AuthorizationServer during construction and is responsible for adding the id_token to the response.
1. ScopeRepository. The getScopeEntityByIdentifier($identifier) method must return a ScopeEntity for the `openid` scope in order to enable support. See examples.

### Example Configuration

```php
// Init Repositories
$clientRepository       = new ClientRepository();
$scopeRepository        = new ScopeRepository();
$accessTokenRepository  = new AccessTokenRepository();
$authCodeRepository     = new AuthCodeRepository();
$refreshTokenRepository = new RefreshTokenRepository();

$privateKeyPath = 'file://' . __DIR__ . '/../private.key';
$publicKeyPath = 'file://' . __DIR__ . '/../public.key';

// OpenID Connect Response Type
$responseType = new IdTokenResponse(new IdentityRepository(), new ClaimExtractor());

// Setup the authorization server
$server = new \League\OAuth2\Server\AuthorizationServer(
    $clientRepository,
    $accessTokenRepository,
    $scopeRepository,
    $privateKey,
    $publicKey,
    $responseType
);

$grant = new \League\OAuth2\Server\Grant\AuthCodeGrant(
    $authCodeRepository,
    $refreshTokenRepository,
    new \DateInterval('PT10M') // authorization codes will expire after 10 minutes
);

$grant->setRefreshTokenTTL(new \DateInterval('P1M')); // refresh tokens will expire after 1 month

// Enable the authentication code grant on the server
$server->enableGrantType(
    $grant,
    new \DateInterval('PT1H') // access tokens will expire after 1 hour
);

return $server;
```
After the server has been configured it should be used as described in the [OAuth2 Server documentation](https://oauth2.thephpleague.com/).

## UserEntity
In order for this library to work properly you will need to add your IdentityProvider to the IdTokenResponse object.  This will be used internally to lookup a UserEntity by it's identifier.  Additionally your UserEntity must implement the ClaimSetInterface which includes a single method getClaims().  The getClaims() method should return a list of attributes as key/value pairs that can be returned if the proper scope has been defined.
```php
use League\OAuth2\Server\Entities\Traits\EntityTrait;
use League\OAuth2\Server\Entities\UserEntityInterface;
use OpenIDConnectServer\Entities\ClaimSetInterface;

class UserEntity implements UserEntityInterface, ClaimSetInterface
{
    use EntityTrait;

    protected $attributes;

    public function getClaims()
    {
        return $this->attributes;
    }
}

```

## ClaimSets
A ClaimSet is a scope that defines a list of claims.
```php
// Example of the profile ClaimSet
$claimSet = new ClaimSetEntity('profile', [
        'name',
        'family_name',
        'given_name',
        'middle_name',
        'nickname',
        'preferred_username',
        'profile',
        'picture',
        'website',
        'gender',
        'birthdate',
        'zoneinfo',
        'locale',
        'updated_at'
    ]);

```
As you can see from the above, profile lists a set of claims that can be extracted from our UserEntity if the profile scope is included with the authorization request.

### Adding Custom ClaimSets
At some point you will likely want to include your own group of custom claims. To do this you will need to create a ClaimSetEntity, give it a scope (the value you will include in the scope parameter of your OAuth2 request) and the list of claims it supports.
```php
$extractor = new ClaimExtractor();
// Create your custom scope
$claimSet = new ClaimSetEntity('company', [
        'company_name',
        'company_phone',
        'company_address'
    ]);
// Add it to the ClaimExtract (this is what you pass to IdTokenResponse, see configuration above)
$extractor->addClaimSet($claimSet);
```
Now, when you pass the company scope with your request it will attempt to locate those properties from your UserEntity::getClaims().

## Install

Via Composer

``` bash
$ composer require steverhoades/oauth2-openid-connect-server
```

## Testing
To run the unit tests you will need to require league/oauth2-server from the source as this repository utilizes some of their existing test infrastructure.
```bash
$ composer require league/oauth2-server --prefer-source
```

Run PHPUnit from the root directory:
```bash
$ vendor/bin/phpunit
```
## License

The MIT License (MIT). Please see [License File](https://github.com/steverhoades/oauth2-openid-connect-client/blob/master/LICENSE) for more information.

[PSR-1]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md
[PSR-2]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md
[PSR-4]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md


================================================
FILE: composer.json
================================================
{
    "name": "steverhoades/oauth2-openid-connect-server",
    "description": "An OpenID Connect Server that sites on The PHP League's OAuth2 Server",
    "license": "MIT",
    "authors": [
        {
            "name": "Steve Rhoades",
            "email": "sedonami@gmail.com"
        }
    ],
    "require": {
        "php": ">=7.4",
        "league/oauth2-server": "^8.4.2|^9.0",
        "lcobucci/jwt": "4.1.5|^4.2|^4.3|^5.0"
    },
    "require-dev": {
        "phpunit/phpunit": "^5.0|^9.5",
        "laminas/laminas-diactoros": "^1.3.2 || ^3.3"
    },
    "autoload": {
        "psr-4": {
            "OpenIDConnectServer\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "OpenIDConnectServer\\Test\\": "tests/",
            "LeagueTests\\": "vendor/league/oauth2-server/tests/"
        }
    },
    "config": {
        "preferred-install": {
            "league/oauth2-server": "source"
        }
    }
}


================================================
FILE: examples/README.md
================================================
# OpenID Connect Example Implementations
The following examples piggyback off the PHP Leagues OAuth2 Server examples. Please follow the instructions below carefully.

## Installation

0. Run `composer install --prefer-source` in this directory to install dependencies
0. Create a private key `openssl genrsa -out private.key 2048`
0. Create a public key `openssl rsa -in private.key -pubout > public.key`
0. Change permissions of the .key files or a PHP Notice will be thrown `chmod 660 *.key`
0. `cd` into the public directory
0. Start a PHP server `php -S localhost:4444`

## Testing the client credentials grant example

Send the following cURL request:

```
curl -X "POST" "http://localhost:4444/client_credentials.php/access_token" \
	-H "Content-Type: application/x-www-form-urlencoded" \
	-H "Accept: 1.0" \
	--data-urlencode "grant_type=client_credentials" \
	--data-urlencode "client_id=myawesomeapp" \
	--data-urlencode "client_secret=abc123" \
	--data-urlencode "scope=openid email"
```

## Testing the password grant example

Send the following cURL request:

```
curl -X "POST" "http://localhost:4444/password.php/access_token" \
	-H "Content-Type: application/x-www-form-urlencoded" \
	-H "Accept: 1.0" \
	--data-urlencode "grant_type=password" \
	--data-urlencode "client_id=myawesomeapp" \
	--data-urlencode "client_secret=abc123" \
	--data-urlencode "username=alex" \
	--data-urlencode "password=whisky" \
	--data-urlencode "scope=openid email"
```


================================================
FILE: examples/composer.json
================================================
{
    "require": {
        "slim/slim": "3.0.*",
        "league/oauth2-server": "^7.0"
    },
    "require-dev": {
        "league/event": "^2.1",
        "lcobucci/jwt": "^3.1",
        "paragonie/random_compat": "^2.0",
        "psr/http-message": "^1.0",
        "defuse/php-encryption": "^2.1",
        "zendframework/zend-diactoros": "^1.0"
    },
    "autoload": {
        "psr-4": {
            "OpenIDConnectServerExamples\\": "src/",
            "OpenIDConnectServer\\": "../src/",
            "OAuth2ServerExamples\\": "vendor/league/oauth2-server/examples/src"
        }
    }
}


================================================
FILE: examples/public/auth_code.php
================================================
<?php
/**
 * @author      Alex Bilbie <hello@alexbilbie.com>
 * @copyright   Copyright (c) Alex Bilbie
 * @license     http://mit-license.org/
 *
 * @link        https://github.com/thephpleague/oauth2-server
 */

use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Grant\AuthCodeGrant;
use OAuth2ServerExamples\Entities\UserEntity;
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
use OAuth2ServerExamples\Repositories\AuthCodeRepository;
use OAuth2ServerExamples\Repositories\ClientRepository;
use OAuth2ServerExamples\Repositories\RefreshTokenRepository;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\App;
use Laminas\Diactoros\Stream;
use OpenIDConnectServer\IdTokenResponse;
use OpenIDConnectServerExamples\Repositories\IdentityRepository;
use OpenIDConnectServerExamples\Repositories\ScopeRepository;
use OpenIDConnectServer\ClaimExtractor;

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

$app = new App([
    'settings'    => [
        'displayErrorDetails' => true,
    ],
    AuthorizationServer::class => function () {
        // Init our repositories
        $clientRepository = new ClientRepository();
        $scopeRepository = new ScopeRepository();
        $accessTokenRepository = new AccessTokenRepository();
        $authCodeRepository = new AuthCodeRepository();
        $refreshTokenRepository = new RefreshTokenRepository();

        $privateKeyPath = 'file://' . __DIR__ . '/../private.key';

        // OpenID Connect Response Type
        $responseType = new IdTokenResponse(new IdentityRepository(), new ClaimExtractor());

        // Setup the authorization server
        $server = new AuthorizationServer(
            $clientRepository,
            $accessTokenRepository,
            $scopeRepository,
            $privateKeyPath,
            'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen',
            $responseType
        );

        // Enable the authentication code grant on the server with a token TTL of 1 hour
        $server->enableGrantType(
            new AuthCodeGrant(
                $authCodeRepository,
                $refreshTokenRepository,
                new \DateInterval('PT10M')
            ),
            new \DateInterval('PT1H')
        );

        return $server;
    },
]);

$app->get('/authorize', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
    /* @var \League\OAuth2\Server\AuthorizationServer $server */
    $server = $app->getContainer()->get(AuthorizationServer::class);

    try {
        // Validate the HTTP request and return an AuthorizationRequest object.
        // The auth request object can be serialized into a user's session
        $authRequest = $server->validateAuthorizationRequest($request);

        // Once the user has logged in set the user on the AuthorizationRequest
        $authRequest->setUser(new UserEntity());

        // Once the user has approved or denied the client update the status
        // (true = approved, false = denied)
        $authRequest->setAuthorizationApproved(true);

        // Return the HTTP redirect response
        return $server->completeAuthorizationRequest($authRequest, $response);
    } catch (OAuthServerException $exception) {
        return $exception->generateHttpResponse($response);
    } catch (\Exception $exception) {
        $body = new Stream('php://temp', 'r+');
        $body->write($exception->getMessage());

        return $response->withStatus(500)->withBody($body);
    }
});

$app->post('/access_token', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
    /* @var \League\OAuth2\Server\AuthorizationServer $server */
    $server = $app->getContainer()->get(AuthorizationServer::class);

    try {
        return $server->respondToAccessTokenRequest($request, $response);
    } catch (OAuthServerException $exception) {
        return $exception->generateHttpResponse($response);
    } catch (\Exception $exception) {
        $body = new Stream('php://temp', 'r+');
        $body->write($exception->getMessage());

        return $response->withStatus(500)->withBody($body);
    }
});

$app->run();


================================================
FILE: examples/public/client_credentials.php
================================================
<?php
/**
 * @author      Alex Bilbie <hello@alexbilbie.com>
 * @copyright   Copyright (c) Alex Bilbie
 * @license     http://mit-license.org/
 *
 * @link        https://github.com/thephpleague/oauth2-server
 */

use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Exception\OAuthServerException;
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
use OAuth2ServerExamples\Repositories\ClientRepository;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\App;
use Laminas\Diactoros\Stream;
use OpenIDConnectServer\IdTokenResponse;
use OpenIDConnectServerExamples\Repositories\IdentityRepository;
use OpenIDConnectServerExamples\Repositories\ScopeRepository;
use OpenIDConnectServer\ClaimExtractor;

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

$app = new App([
    'settings'                 => [
        'displayErrorDetails' => true,
    ],
    AuthorizationServer::class => function () {
        // Init our repositories
        $clientRepository = new ClientRepository(); // instance of ClientRepositoryInterface
        $scopeRepository = new ScopeRepository(); // instance of ScopeRepositoryInterface
        $accessTokenRepository = new AccessTokenRepository(); // instance of AccessTokenRepositoryInterface

        // Path to public and private keys
        $privateKey = 'file://' . __DIR__ . '/../private.key';
        //$privateKey = new CryptKey('file://path/to/private.key', 'passphrase'); // if private key has a pass phrase

        // OpenID Connect Response Type
        $responseType = new IdTokenResponse(new IdentityRepository(), new ClaimExtractor());

        // Setup the authorization server
        $server = new AuthorizationServer(
            $clientRepository,
            $accessTokenRepository,
            $scopeRepository,
            $privateKey,
            'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen',
            $responseType
        );

        // Enable the client credentials grant on the server
        $server->enableGrantType(
            new \League\OAuth2\Server\Grant\ClientCredentialsGrant(),
            new \DateInterval('PT1H') // access tokens will expire after 1 hour
        );

        return $server;
    },
]);

$app->post('/access_token', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {

    /* @var \League\OAuth2\Server\AuthorizationServer $server */
    $server = $app->getContainer()->get(AuthorizationServer::class);

    try {

        // Try to respond to the request
        return $server->respondToAccessTokenRequest($request, $response);
    } catch (OAuthServerException $exception) {

        // All instances of OAuthServerException can be formatted into a HTTP response
        return $exception->generateHttpResponse($response);
    } catch (\Exception $exception) {

        // Unknown exception
        $body = new Stream('php://temp', 'r+');
        $body->write($exception->getMessage());

        return $response->withStatus(500)->withBody($body);
    }
});

$app->run();


================================================
FILE: examples/public/implicit.php
================================================
<?php
/**
 * @author      Alex Bilbie <hello@alexbilbie.com>
 * @copyright   Copyright (c) Alex Bilbie
 * @license     http://mit-license.org/
 *
 * @link        https://github.com/thephpleague/oauth2-server
 */

use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Grant\ImplicitGrant;
use OAuth2ServerExamples\Entities\UserEntity;
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
use OAuth2ServerExamples\Repositories\ClientRepository;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\App;
use Laminas\Diactoros\Stream;
use OpenIDConnectServer\IdTokenResponse;
use OpenIDConnectServerExamples\Repositories\IdentityRepository;
use OpenIDConnectServerExamples\Repositories\ScopeRepository;
use OpenIDConnectServer\ClaimExtractor;

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

$app = new App([
    'settings'    => [
        'displayErrorDetails' => true,
    ],
    AuthorizationServer::class => function () {
        // Init our repositories
        $clientRepository = new ClientRepository();
        $scopeRepository = new ScopeRepository();
        $accessTokenRepository = new AccessTokenRepository();

        $privateKeyPath = 'file://' . __DIR__ . '/../private.key';

        // OpenID Connect Response Type
        $responseType = new IdTokenResponse(new IdentityRepository(), new ClaimExtractor());

        // Setup the authorization server
        $server = new AuthorizationServer(
            $clientRepository,
            $accessTokenRepository,
            $scopeRepository,
            $privateKeyPath,
            'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen',
            $responseType
        );

        // Enable the implicit grant on the server with a token TTL of 1 hour
        $server->enableGrantType(new ImplicitGrant(new \DateInterval('PT1H')));

        return $server;
    },
]);

$app->get('/authorize', function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {
    /* @var \League\OAuth2\Server\AuthorizationServer $server */
    $server = $app->getContainer()->get(AuthorizationServer::class);

    try {
        // Validate the HTTP request and return an AuthorizationRequest object.
        // The auth request object can be serialized into a user's session
        $authRequest = $server->validateAuthorizationRequest($request);

        // Once the user has logged in set the user on the AuthorizationRequest
        $authRequest->setUser(new UserEntity());

        // Once the user has approved or denied the client update the status
        // (true = approved, false = denied)
        $authRequest->setAuthorizationApproved(true);

        // Return the HTTP redirect response
        return $server->completeAuthorizationRequest($authRequest, $response);
    } catch (OAuthServerException $exception) {
        return $exception->generateHttpResponse($response);
    } catch (\Exception $exception) {
        $body = new Stream('php://temp', 'r+');
        $body->write($exception->getMessage());

        return $response->withStatus(500)->withBody($body);
    }
});

$app->run();


================================================
FILE: examples/public/password.php
================================================
<?php

use League\OAuth2\Server\AuthorizationServer;
use League\OAuth2\Server\Exception\OAuthServerException;
use League\OAuth2\Server\Grant\PasswordGrant;
use OAuth2ServerExamples\Repositories\AccessTokenRepository;
use OAuth2ServerExamples\Repositories\ClientRepository;
use OAuth2ServerExamples\Repositories\RefreshTokenRepository;
use OAuth2ServerExamples\Repositories\UserRepository;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\App;
use OpenIDConnectServer\IdTokenResponse;
use OpenIDConnectServerExamples\Repositories\IdentityRepository;
use OpenIDConnectServerExamples\Repositories\ScopeRepository;
use OpenIDConnectServer\ClaimExtractor;

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

$app = new App([
    // Add the authorization server to the DI container
    AuthorizationServer::class => function () {
        // OpenID Connect Response Type
        $responseType = new IdTokenResponse(new IdentityRepository(), new ClaimExtractor());

        // Setup the authorization server
        $server = new AuthorizationServer(
            new ClientRepository(),                 // instance of ClientRepositoryInterface
            new AccessTokenRepository(),            // instance of AccessTokenRepositoryInterface
            new ScopeRepository(),                  // instance of ScopeRepositoryInterface
            'file://' . __DIR__ . '/../private.key',    // path to private key
            'lxZFUEsBCJ2Yb14IF2ygAHI5N4+ZAUXXaSeeJm6+twsUmIen',      // encryption key
            $responseType
        );

        $grant = new PasswordGrant(
            new UserRepository(),           // instance of UserRepositoryInterface
            new RefreshTokenRepository()    // instance of RefreshTokenRepositoryInterface
        );
        $grant->setRefreshTokenTTL(new \DateInterval('P1M')); // refresh tokens will expire after 1 month

        // Enable the password grant on the server with a token TTL of 1 hour
        $server->enableGrantType(
            $grant,
            new \DateInterval('PT1H') // access tokens will expire after 1 hour
        );

        return $server;
    },
]);

$app->post(
    '/access_token',
    function (ServerRequestInterface $request, ResponseInterface $response) use ($app) {

        /* @var \League\OAuth2\Server\AuthorizationServer $server */
        $server = $app->getContainer()->get(AuthorizationServer::class);

        try {

            // Try to respond to the access token request
            return $server->respondToAccessTokenRequest($request, $response);
        } catch (OAuthServerException $exception) {

            // All instances of OAuthServerException can be converted to a PSR-7 response
            return $exception->generateHttpResponse($response);
        } catch (\Exception $exception) {

            // Catch unexpected exceptions
            $body = $response->getBody();
            $body->write($exception->getMessage());

            return $response->withStatus(500)->withBody($body);
        }
    }
);

$app->run();


================================================
FILE: examples/src/Entities/UserEntity.php
================================================
<?php

namespace OpenIDConnectServerExamples\Entities;

use OpenIDConnectServer\Entities\ClaimSetInterface;

class UserEntity extends \OAuth2ServerExamples\Entities\UserEntity implements ClaimSetInterface
{
    public function getClaims()
    {
        return [
            // profile
            'name' => 'John Smith',
            'family_name' => 'Smith',
            'given_name' => 'John',
            'middle_name' => 'Doe',
            'nickname' => 'JDog',
            'preferred_username' => 'jdogsmith77',
            'profile' => '',
            'picture' => 'avatar.png',
            'website' => 'http://www.google.com',
            'gender' => 'M',
            'birthdate' => '01/01/1990',
            'zoneinfo' => '',
            'locale' => 'US',
            'updated_at' => '01/01/2018',
            // email
            'email' => 'john.doe@example.com',
            'email_verified' => true,
            // phone
            'phone_number' => '(866) 555-5555',
            'phone_number_verified' => true,
            // address
            'address' => '50 any street, any state, 55555',
        ];
    }
}


================================================
FILE: examples/src/Repositories/IdentityRepository.php
================================================
<?php
namespace OpenIDConnectServerExamples\Repositories;

use OpenIDConnectServer\Repositories\IdentityProviderInterface;
use OpenIDConnectServerExamples\Entities\UserEntity;

class IdentityRepository implements IdentityProviderInterface
{
    public function getUserEntityByIdentifier($identifier)
    {
        return new UserEntity();
    }
}


================================================
FILE: examples/src/Repositories/ScopeRepository.php
================================================
<?php
/**
 * @author      Alex Bilbie <hello@alexbilbie.com>
 * @copyright   Copyright (c) Alex Bilbie
 * @license     http://mit-license.org/
 *
 * @link        https://github.com/thephpleague/oauth2-server
 */

namespace OpenIDConnectServerExamples\Repositories;

use League\OAuth2\Server\Entities\ClientEntityInterface;
use League\OAuth2\Server\Repositories\ScopeRepositoryInterface;
use OAuth2ServerExamples\Entities\ScopeEntity;

class ScopeRepository extends \OAuth2ServerExamples\Repositories\ScopeRepository
{
    /**
     * {@inheritdoc}
     */
    public function getScopeEntityByIdentifier($scopeIdentifier)
    {
        $scopes = [
            // Without this OpenID Connect cannot work.
            'openid' => [
                'description' => 'Enable OpenID Connect support'
            ],
            'basic' => [
                'description' => 'Basic details about you',
            ],
            'email' => [
                'description' => 'Your email address',
            ],
        ];

        if (array_key_exists($scopeIdentifier, $scopes) === false) {
            return;
        }

        $scope = new ScopeEntity();
        $scope->setIdentifier($scopeIdentifier);

        return $scope;
    }
}


================================================
FILE: phpunit.xml.dist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<phpunit colors="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" stopOnError="true"
         stopOnFailure="true" stopOnIncomplete="false" stopOnSkipped="false" bootstrap="tests/Bootstrap.php">
    <testsuites>
        <testsuite name="Tests">
            <directory>./tests/</directory>
        </testsuite>
    </testsuites>
    <filter>
        <whitelist addUncoveredFilesFromWhitelist="true">
            <directory suffix=".php">src</directory>
        </whitelist>
    </filter>
    <logging>
        <log type="coverage-text" target="php://stdout" title="steverhoades/oauth2-openid-connect-server" charset="UTF-8" yui="true"
             highlight="true" lowUpperBound="60" highLowerBound="90"/>
        <log type="coverage-html" target="build/coverage" title="steverhoades/oauth2-openid-connect-server" charset="UTF-8" yui="true"
             highlight="true" lowUpperBound="60" highLowerBound="90"/>
    </logging>
</phpunit>


================================================
FILE: src/ClaimExtractor.php
================================================
<?php
/**
 * @author Steve Rhoades <sedonami@gmail.com>
 * @license http://opensource.org/licenses/MIT MIT
 */
namespace OpenIDConnectServer;

use OpenIDConnectServer\Entities\ClaimSetEntity;
use OpenIDConnectServer\Entities\ClaimSetEntityInterface;
use OpenIDConnectServer\Exception\InvalidArgumentException;
use League\OAuth2\Server\Entities\ScopeEntityInterface;

class ClaimExtractor
{
    protected $claimSets;

    protected $protectedClaims = ['profile', 'email', 'address', 'phone'];

    /**
     * ClaimExtractor constructor.
     * @param ClaimSetEntity[] $claimSets
     */
    public function __construct($claimSets = [])
    {
        // Add Default OpenID Connect Claims
        // @see http://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims
        $this->addClaimSet(
            new ClaimSetEntity('profile', [
                'name',
                'family_name',
                'given_name',
                'middle_name',
                'nickname',
                'preferred_username',
                'profile',
                'picture',
                'website',
                'gender',
                'birthdate',
                'zoneinfo',
                'locale',
                'updated_at'
            ])
        );
        $this->addClaimSet(
            new ClaimSetEntity('email', [
                'email',
                'email_verified'
            ])
        );
        $this->addClaimSet(
            new ClaimSetEntity('address', [
                'address'
            ])
        );
        $this->addClaimSet(
            new ClaimSetEntity('phone', [
                'phone_number',
                'phone_number_verified'
            ])
        );

        foreach ($claimSets as $claimSet) {
            $this->addClaimSet($claimSet);
        }
    }

    /**
     * @param ClaimSetEntityInterface $claimSet
     * @return $this
     * @throws InvalidArgumentException
     */
    public function addClaimSet(ClaimSetEntityInterface $claimSet)
    {
        $scope = $claimSet->getScope();

        if (in_array($scope, $this->protectedClaims) && !empty($this->claimSets[$scope])) {
            throw new InvalidArgumentException(
                sprintf("%s is a protected scope and is pre-defined by the OpenID Connect specification.", $scope)
            );
        }

        $this->claimSets[$scope] = $claimSet;

        return $this;
    }

    /**
     * @param string $scope
     * @return ClaimSetEntity|null
     */
    public function getClaimSet($scope)
    {
        if (!$this->hasClaimSet($scope)) {
            return null;
        }

        return $this->claimSets[$scope];
    }

    /**
     * @param string $scope
     * @return bool
     */
    public function hasClaimSet($scope)
    {
        return array_key_exists($scope, $this->claimSets);
    }

    /**
     * For given scopes and aggregated claims get all claims that have been configured on the extractor.
     *
     * @param array $scopes
     * @param array $claims
     * @return array
     */
    public function extract(array $scopes, array $claims)
    {
        $claimData  = [];
        $keys       = array_keys($claims);

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

            $claimSet = $this->getClaimSet($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;
    }
}


================================================
FILE: src/Entities/ClaimSetEntity.php
================================================
<?php
/**
 * @author Steve Rhoades <sedonami@gmail.com>
 * @license http://opensource.org/licenses/MIT MIT
 */
namespace OpenIDConnectServer\Entities;

class ClaimSetEntity implements ClaimSetEntityInterface
{
    protected $scope;

    protected $claims;

    public function __construct($scope, array $claims)
    {
        $this->scope    = $scope;
        $this->claims   = $claims;
    }

    public function getScope()
    {
        return $this->scope;
    }

    public function getClaims()
    {
        return $this->claims;
    }
}


================================================
FILE: src/Entities/ClaimSetEntityInterface.php
================================================
<?php
/**
 * @author Steve Rhoades <sedonami@gmail.com>
 * @license http://opensource.org/licenses/MIT MIT
 */
namespace OpenIDConnectServer\Entities;


interface ClaimSetEntityInterface extends ClaimSetInterface, ScopeInterface
{
}


================================================
FILE: src/Entities/ClaimSetInterface.php
================================================
<?php
/**
 * @author Steve Rhoades <sedonami@gmail.com>
 * @license http://opensource.org/licenses/MIT MIT
 */
namespace OpenIDConnectServer\Entities;


interface ClaimSetInterface
{
    /**
     * @return array
     */
    public function getClaims();
}


================================================
FILE: src/Entities/ScopeInterface.php
================================================
<?php
/**
 * @author Steve Rhoades <sedonami@gmail.com>
 * @license http://opensource.org/licenses/MIT MIT
 */
namespace OpenIDConnectServer\Entities;


interface ScopeInterface
{
    /**
     * @return string
     */
    public function getScope();
}


================================================
FILE: src/Exception/InvalidArgumentException.php
================================================
<?php
/**
 * @author Steve Rhoades <sedonami@gmail.com>
 * @license http://opensource.org/licenses/MIT MIT
 */
namespace OpenIDConnectServer\Exception;


class InvalidArgumentException extends \Exception
{

}


================================================
FILE: src/IdTokenResponse.php
================================================
<?php
/**
 * @author Steve Rhoades <sedonami@gmail.com>
 * @license http://opensource.org/licenses/MIT MIT
 */
namespace OpenIDConnectServer;

use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Signer\Key\LocalFileReference;
use OpenIDConnectServer\Repositories\IdentityProviderInterface;
use OpenIDConnectServer\Entities\ClaimSetInterface;
use League\OAuth2\Server\Entities\UserEntityInterface;
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
use League\OAuth2\Server\Entities\ScopeEntityInterface;
use League\OAuth2\Server\ResponseTypes\BearerTokenResponse;
use Lcobucci\JWT\Signer\Rsa\Sha256;
use Lcobucci\JWT\Encoding\ChainedFormatter;
use Lcobucci\JWT\Token\Builder;
use Lcobucci\JWT\Encoding\JoseEncoder;

class IdTokenResponse extends BearerTokenResponse
{
    /**
     * @var IdentityProviderInterface
     */
    protected $identityProvider;

    /**
     * @var ClaimExtractor
     */
    protected $claimExtractor;

    /**
     * @var string|null
     */
    protected $keyIdentifier;
    
    public function __construct(
        IdentityProviderInterface $identityProvider,
        ClaimExtractor $claimExtractor,
        ?string $keyIdentifier = null
    ) {
        $this->identityProvider = $identityProvider;
        $this->claimExtractor   = $claimExtractor;
        $this->keyIdentifier   = $keyIdentifier;
    }

    protected function getBuilder(AccessTokenEntityInterface $accessToken, UserEntityInterface $userEntity)
    {
        $claimsFormatter = ChainedFormatter::withUnixTimestampDates();
        $builder = new Builder(new JoseEncoder(), $claimsFormatter);

        // Since version 8.0 league/oauth2-server returns \DateTimeImmutable
        $expiresAt = $accessToken->getExpiryDateTime();
        if ($expiresAt instanceof \DateTime) {
            $expiresAt = \DateTimeImmutable::createFromMutable($expiresAt);
        }

        // Add required id_token claims
        return $builder
            ->permittedFor($accessToken->getClient()->getIdentifier())
            ->issuedBy('https://' . $_SERVER['HTTP_HOST'])
            ->issuedAt(new \DateTimeImmutable())
            ->expiresAt($expiresAt)
            ->relatedTo($userEntity->getIdentifier());
    }

    /**
     * @param AccessTokenEntityInterface $accessToken
     * @return array
     */
    protected function getExtraParams(AccessTokenEntityInterface $accessToken): array
    {
        if (false === $this->isOpenIDRequest($accessToken->getScopes())) {
            return [];
        }

        /** @var UserEntityInterface $userEntity */
        $userEntity = $this->identityProvider->getUserEntityByIdentifier($accessToken->getUserIdentifier());

        if (false === is_a($userEntity, UserEntityInterface::class)) {
            throw new \RuntimeException('UserEntity must implement UserEntityInterface');
        } else if (false === is_a($userEntity, ClaimSetInterface::class)) {
            throw new \RuntimeException('UserEntity must implement ClaimSetInterface');
        }

        // Add required id_token claims
        $builder = $this->getBuilder($accessToken, $userEntity);

        // Need a claim factory here to reduce the number of claims by provided scope.
        $claims = $this->claimExtractor->extract($accessToken->getScopes(), $userEntity->getClaims());

        foreach ($claims as $claimName => $claimValue) {
            $builder = $builder->withClaim($claimName, $claimValue);
        }

        if ($this->keyIdentifier !== null) {
            $builder = $builder->withHeader('kid', $this->keyIdentifier);
        }

        if (
            method_exists($this->privateKey, 'getKeyContents')
            && !empty($this->privateKey->getKeyContents())
        ) {
            $key = InMemory::plainText($this->privateKey->getKeyContents(), (string)$this->privateKey->getPassPhrase());
        } else {
            $key = LocalFileReference::file($this->privateKey->getKeyPath(), (string)$this->privateKey->getPassPhrase());
        }

        $token = $builder->getToken(new Sha256(), $key);

        return [
            'id_token' => $token->toString()
        ];
    }

    /**
     * @param ScopeEntityInterface[] $scopes
     * @return bool
     */
    private function isOpenIDRequest($scopes)
    {
        // Verify scope and make sure openid exists.
        $valid  = false;

        foreach ($scopes as $scope) {
            if ($scope->getIdentifier() === 'openid') {
                $valid = true;
                break;
            }
        }

        return $valid;
    }

}


================================================
FILE: src/Repositories/ClaimSetRepositoryInterface.php
================================================
<?php
/**
 * @author Steve Rhoades <sedonami@gmail.com>
 * @license http://opensource.org/licenses/MIT MIT
 */
namespace OpenIDConnectServer\Repositories;


interface ClaimSetRepositoryInterface
{
    public function getClaimSetByScopeIdentifier($scopeIdentifier);
}


================================================
FILE: src/Repositories/IdentityProviderInterface.php
================================================
<?php
/**
 * @author Steve Rhoades <sedonami@gmail.com>
 * @license http://opensource.org/licenses/MIT MIT
 */
namespace OpenIDConnectServer\Repositories;

use League\OAuth2\Server\Entities\UserEntityInterface;
use League\OAuth2\Server\Repositories\RepositoryInterface;
use OpenIDConnectServer\Entities\ClaimSetInterface;

interface IdentityProviderInterface extends RepositoryInterface
{
    /**
     * @return UserEntityInterface&ClaimSetInterface
     */
    public function getUserEntityByIdentifier($identifier);
}


================================================
FILE: tests/Bootstrap.php
================================================
<?php

if (!@include_once __DIR__ . '/../vendor/autoload.php') {
    $message = <<<MSG
You must set up the project dependencies, run the following commands:
> wget http://getcomposer.org/composer.phar
> php composer.phar install
MSG;

    exit($message);
}


================================================
FILE: tests/ClaimExtractorTest.php
================================================
<?php
namespace OpenIDConnectServer\Test;

use OpenIDConnectServer\ClaimExtractor;
use OpenIDConnectServer\Entities\ClaimSetEntity;
use PHPUnit\Framework\TestCase;
use OpenIDConnectServer\Exception\InvalidArgumentException;

class ClaimExtractorTest extends TestCase
{
    public function testDefaultClaimSetsExist()
    {
        $extractor = new ClaimExtractor();
        self::assertTrue($extractor->hasClaimSet('profile'));
        self::assertTrue($extractor->hasClaimSet('email'));
        self::assertTrue($extractor->hasClaimSet('address'));
        self::assertTrue($extractor->hasClaimSet('phone'));
    }

    public function testCanAddCustomClaimSet()
    {
        $claims = new ClaimSetEntity('custom', ['custom_claim']);
        $extractor = new ClaimExtractor([$claims]);
        self::assertTrue($extractor->hasClaimSet('custom'));

        $result = $extractor->extract(['custom'], ['custom_claim' => 'test']);
        self::assertEquals($result['custom_claim'], 'test');
    }

    public function testCanNotOverrideDefaultScope()
    {
        $this->expectException(InvalidArgumentException::class);
        $claims = new ClaimSetEntity('profile', ['custom_claim']);
        $extractor = new ClaimExtractor([$claims]);
    }

    public function testCanGetClaimSet()
    {
        $extractor = new ClaimExtractor();
        $claimset = $extractor->getClaimSet('profile');
        self::assertEquals($claimset->getScope(), 'profile');
        $claimset = $extractor->getClaimSet('unknown');
        self::assertNull($claimset);
    }

    public function testExtract()
    {
        $extractor = new ClaimExtractor();
        // no result
        $result = $extractor->extract(['custom'], ['custom_claim' => 'test']);
        self::assertEmpty($result);

        // result
        $result = $extractor->extract(['profile'], ['name' => 'Steve']);
        self::assertEquals($result['name'], 'Steve');

        // no result
        $result = $extractor->extract(['profile'], ['invalid' => 'Steve']);
        self::assertEmpty($result);
    }
}


================================================
FILE: tests/ResponseTypes/IdTokenResponseTest.php
================================================
<?php

namespace OpenIDConnectServer\Test\ResponseTypes;

use OpenIDConnectServer\ClaimExtractor;
use OpenIDConnectServer\IdTokenResponse;
use OpenIDConnectServer\Test\Stubs\IdentityProvider;
use PHPUnit\Framework\TestCase;
use League\OAuth2\Server\CryptKey;
use LeagueTests\Stubs\AccessTokenEntity;
use LeagueTests\Stubs\ClientEntity;
use LeagueTests\Stubs\RefreshTokenEntity;
use LeagueTests\Stubs\ScopeEntity;
use Psr\Http\Message\ResponseInterface;
use Laminas\Diactoros\Response;

class IdTokenResponseTest extends TestCase
{
    /**
     * @dataProvider provideCryptKeys
     */
    public function testGeneratesDefaultHttpResponse($privateKey)
    {
        $responseType = new IdTokenResponse(new IdentityProvider(), new ClaimExtractor());
        $response = $this->processResponseType($responseType, $privateKey);

        self::assertInstanceOf(ResponseInterface::class, $response);
        self::assertEquals(200, $response->getStatusCode());
        self::assertEquals('no-cache', $response->getHeader('pragma')[0]);
        self::assertEquals('no-store', $response->getHeader('cache-control')[0]);
        self::assertEquals('application/json; charset=UTF-8', $response->getHeader('content-type')[0]);

        $response->getBody()->rewind();
        $json = json_decode($response->getBody()->getContents());
        self::assertEquals('Bearer', $json->token_type);
        self::assertObjectHasAttribute('expires_in', $json);
        self::assertObjectHasAttribute('access_token', $json);
        self::assertObjectHasAttribute('refresh_token', $json);
    }

    /**
     * @dataProvider provideCryptKeys
     */
    public function testOpenIDConnectHttpResponse($privateKey)
    {
        $responseType = new IdTokenResponse(new IdentityProvider(), new ClaimExtractor());
        $response = $this->processResponseType($responseType, $privateKey, ['openid']);

        self::assertInstanceOf(ResponseInterface::class, $response);
        self::assertEquals(200, $response->getStatusCode());
        self::assertEquals('no-cache', $response->getHeader('pragma')[0]);
        self::assertEquals('no-store', $response->getHeader('cache-control')[0]);
        self::assertEquals('application/json; charset=UTF-8', $response->getHeader('content-type')[0]);

        $response->getBody()->rewind();
        $json = json_decode($response->getBody()->getContents());
        self::assertEquals('Bearer', $json->token_type);
        self::assertObjectHasAttribute('expires_in', $json);
        self::assertObjectHasAttribute('access_token', $json);
        self::assertObjectHasAttribute('refresh_token', $json);
        self::assertObjectHasAttribute('id_token', $json);
    }

    // test additional claims
    // test fails without claimsetinterface
    /**
     * @dataProvider provideCryptKeys
     */
    public function testThrowsRuntimeExceptionWhenMissingClaimSetInterface($privateKey)
    {
        $this->expectException(\RuntimeException::class);

        $_SERVER['HTTP_HOST'] = 'https://localhost';
        $responseType = new IdTokenResponse(
            new IdentityProvider(IdentityProvider::NO_CLAIMSET),
            new ClaimExtractor()
        );
        $this->processResponseType($responseType, $privateKey, ['openid']);
        self::fail('Exception should have been thrown');
    }

    // test fails without identityinterface
    /**
     * @dataProvider provideCryptKeys
     */
    public function testThrowsRuntimeExceptionWhenMissingIdentifierSetInterface($privateKey)
    {
        $this->expectException(\RuntimeException::class);
        $responseType = new IdTokenResponse(
            new IdentityProvider(IdentityProvider::NO_IDENTIFIER),
            new ClaimExtractor()
        );
        $this->processResponseType($responseType, $privateKey, ['openid']);
        self::fail('Exception should have been thrown');
    }

    /**
     * @dataProvider provideCryptKeys
     */
    public function testClaimsGetExtractedFromUserEntity($privateKey)
    {
        $responseType = new IdTokenResponse(new IdentityProvider(), new ClaimExtractor());
        $response = $this->processResponseType($responseType, $privateKey, ['openid', 'email']);

        self::assertInstanceOf(ResponseInterface::class, $response);
        self::assertEquals(200, $response->getStatusCode());
        self::assertEquals('no-cache', $response->getHeader('pragma')[0]);
        self::assertEquals('no-store', $response->getHeader('cache-control')[0]);
        self::assertEquals('application/json; charset=UTF-8', $response->getHeader('content-type')[0]);

        $response->getBody()->rewind();
        $json = json_decode($response->getBody()->getContents(),false);

        self::assertEquals('Bearer', $json->token_type);
        self::assertObjectHasAttribute('expires_in', $json);
        self::assertObjectHasAttribute('access_token', $json);
        self::assertObjectHasAttribute('refresh_token', $json);
        self::assertObjectHasAttribute('id_token', $json);

        if (class_exists("\Lcobucci\JWT\Token\Parser")) {
            $parser = new \Lcobucci\JWT\Token\Parser(new \Lcobucci\JWT\Encoding\JoseEncoder, \Lcobucci\JWT\Encoding\ChainedFormatter::withUnixTimestampDates());
        } else {
            $parser = new \Lcobucci\JWT\Parser();
        }

        $token = $parser->parse($json->id_token);
        self::assertTrue($token->claims()->has("email"));
    }

    public static function provideCryptKeys()
    {
        return array(
            array(new CryptKey('file://'.__DIR__.'/../Stubs/private.key')),
            array(new CryptKey(
                <<<KEY
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAzjgUbG97UD+bZwkceejvxkbcq/17YqmriWGpBu7etXAA2WZp
x7vLaQi6ygApfCsYDz13W27DyriH2kg56GOa2v9M88OORiW1rQMaGF4hn/L7agFc
cvdNAWBKD8ue+QUPz3prA3TLF+lSqMn5BgF+4j7XNlODvfOX3Tra1JQcVik4pyjg
QeTLaaBSf6KaCvDzVCcvVuYISC5oku5v6o+BIug8taRhcXN8/9gLQ9akrGG73z+u
dAoZ2+v1k7VZ704steLM9pf7aY+L6kR2Qmc7E4j/WM9Sv8CHUaJG41MRAbjdHtGh
zGcsZwf/mNjyjW3UalI1dih2muhPTSAZT0xL+wIDAQABAoIBAAFDBJT5RabjDL9f
peX1D+qFqnn+7g9XfG41w8QAGCrCCa9K2iDXvFHjNMlhoN9aoCYPuTg9AEOwR1yF
jp0mZt8qKr1fF/LD7k2ltDYr9Ua2ROWMJpWpf7YfcbSRCWL6rfMWC6uUvl1iFxhj
S/vGbJFT0xtI/YhfAjHfV1FvqpC4YwmKVe4/QqU0Kw+CjmYKLqeR0lvARP0aRfRm
GRIy/9ZtUzbcUSnLScNS8U2HhdKEOl/R9dSjHVG+rr+/sJmFRUiQWJx3UkDZ4lTd
lEIqj8i0CZMooARZjOIhIjP+wT9zr1sWFaxU81ourMnfoOQSKIrkUUYGc4rOk3Ao
DuqBx9ECgYEA5miGw2v+2YIm1XtmTRvLJiDgr8mTWydylPXxjTlghYNKts4bU5rh
EN/Ok+DUPLqPGQLnFgjcadT/clZfJXF3/gOS8V9hxVWI6Gwku1Ez7OqtCv3plA0i
46fxh0PmK6lFOzlbWMN1wpQ/mB3dWh4YaC6ERG1Z3L1DCIqYs/LVFsMCgYEA5R/A
dJAwtPWjdEjKFaajH7Z7iB/BSUydyPg++sMDjETbrV3wddO67LEkhH3vB4VDEKKt
FnD1iSyWprb94gDK0PTxQyHJ3JdzDP05L+7C+lwLqEKCrh2BZpScvub0FRgECNNO
OCuoMtX1HG/dGjkJsxB7e1lr7LGFMyPjR6I+0mkCgYEAzvcznpUCvnTX10naUgdW
SzCbQ6xJDkd3+HCYAuh4WFXgJicrisUDyHmRgWoim05lPe1KkJNzEim/MAB/xQ2Q
4H5rXx/zniPAMC78K7q8buM6fzYnu9K09VQldAC835lUU+eosyoYPKmYGlcxP0Lr
X6HxM9oaL1tevGxq0LGfUasCgYEA3R4ybouE5e61KxDgLdreTEmgl/MFZwbQs1WX
+grfzqvZUUt6N0v5dllSQ6cBWkGqQlCsOB8VZqeoUAYDp+tZ0CTC/SWLmR5zwtJS
MUb71f+kpGJjmUMSUXwUdUuPvRerNRUvxJelQEIpxaLTP25SRQQgFx9qP0fmoz78
JXKXrBkCgYEApBfmVsOTG5S+oO7WZFpndeofLnXYn9xRvlc738+dANY4mWwHJlBd
z2wzJ5wfjzlXsZoKcV0I6pRWLrgw3Gd5cwu3O5+MUN89cdQuVrfB77KQJHIF+S06
fHDgr/HSgH8LCXDq4DSd5XC0WxCPTDYrTN8iiHop2k35Ex0UXYeE+g0=
-----END RSA PRIVATE KEY-----
KEY
            ),
        ));
    }

    private function processResponseType($responseType, $privateKey,  array $scopeNames = ['basic'])
    {
        $_SERVER['HTTP_HOST'] = 'https://localhost';

        $responseType->setPrivateKey($privateKey);

        // league/oauth2-server 5.1.0 does not support this interface
        if (method_exists($responseType, 'setEncryptionKey')) {
            $responseType->setEncryptionKey(base64_encode(random_bytes(36)));
        }

        $client = new ClientEntity();
        $client->setIdentifier('clientName');

        $scopes = [];
        foreach ($scopeNames as $scopeName) {
            $scope = new ScopeEntity();
            $scope->setIdentifier($scopeName);
            $scopes[] = $scope;
        }

        $accessToken = new AccessTokenEntity();
        $accessToken->setIdentifier('abcdef');

        if (method_exists($accessToken, 'setPrivateKey')) {
            $accessToken->setPrivateKey($privateKey);
        }

        // Use DateTime for older libraries, DateTimeImmutable for new ones.
        try {
            $accessToken->setExpiryDateTime(
                (new \DateTime())->add(new \DateInterval('PT1H'))
            );
        } catch(\TypeError $e) {
            $accessToken->setExpiryDateTime(
                (new \DateTimeImmutable())->add(new \DateInterval('PT1H'))
            );
        }
        $accessToken->setClient($client);

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

        $refreshToken = new RefreshTokenEntity();
        $refreshToken->setIdentifier('abcdef');
        $refreshToken->setAccessToken($accessToken);

        // Use DateTime for older libraries, DateTimeImmutable for new ones.
        try {
            $refreshToken->setExpiryDateTime(
                (new \DateTime())->add(new \DateInterval('PT1H'))
            );
        } catch(\TypeError $e) {
            $refreshToken->setExpiryDateTime(
                (new \DateTimeImmutable())->add(new \DateInterval('PT1H'))
            );
        }

        $responseType->setAccessToken($accessToken);
        $responseType->setRefreshToken($refreshToken);

        return $responseType->generateHttpResponse(new Response());
    }
}


================================================
FILE: tests/Stubs/IdentityProvider.php
================================================
<?php

namespace OpenIDConnectServer\Test\Stubs;

use OpenIDConnectServer\Repositories\IdentityProviderInterface;

class IdentityProvider implements IdentityProviderInterface
{
    const NO_CLAIMSET = 'no_claimset';
    const NO_IDENTIFIER = 'no_idetifier';

    protected $entity;

    public function __construct($type = null)
    {
        switch($type) {
            case self::NO_CLAIMSET:
                $this->entity = new UserNoClaimSetEntity();
                break;
            case self::NO_IDENTIFIER:
                $this->entity = new UserNoIdentifierEntity();
                break;
            default:
                $this->entity = new UserEntity();
        }
    }

    public function getUserEntityByIdentifier($identifier)
    {
        return $this->entity;
    }

}


================================================
FILE: tests/Stubs/UserEntity.php
================================================
<?php

namespace OpenIDConnectServer\Test\Stubs;

use League\OAuth2\Server\Entities\Traits\EntityTrait;
use League\OAuth2\Server\Entities\UserEntityInterface;
use OpenIDConnectServer\Entities\ClaimSetInterface;

class UserEntity implements UserEntityInterface, ClaimSetInterface
{
    use EntityTrait;

    public function __construct()
    {
        $this->setIdentifier(123);
    }

    public function getClaims()
    {
        return [
            'first_name'    => 'Steve',
            'last_name'     => 'Rhoades',
            'email'         => 'steve.rhoades@stephenrhoades.com'
        ];
    }
}


================================================
FILE: tests/Stubs/UserNoClaimSetEntity.php
================================================
<?php

namespace OpenIDConnectServer\Test\Stubs;

use League\OAuth2\Server\Entities\Traits\EntityTrait;
use League\OAuth2\Server\Entities\UserEntityInterface;

class UserNoClaimSetEntity implements UserEntityInterface
{
    use EntityTrait;

    public function __construct()
    {
        $this->setIdentifier(123);
    }
}


================================================
FILE: tests/Stubs/UserNoIdentifierEntity.php
================================================
<?php

namespace OpenIDConnectServer\Test\Stubs;

use OpenIDConnectServer\Entities\ClaimSetInterface;

class UserNoIdentifierEntity implements ClaimSetInterface
{
    public function getClaims()
    {
        return [
            'first_name'    => 'Steve',
            'last_name'     => 'Rhoades',
            'email'         => 'steve.rhoades@stephenrhoades.com'
        ];
    }
}



================================================
FILE: tests/Stubs/private.key
================================================
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAzjgUbG97UD+bZwkceejvxkbcq/17YqmriWGpBu7etXAA2WZp
x7vLaQi6ygApfCsYDz13W27DyriH2kg56GOa2v9M88OORiW1rQMaGF4hn/L7agFc
cvdNAWBKD8ue+QUPz3prA3TLF+lSqMn5BgF+4j7XNlODvfOX3Tra1JQcVik4pyjg
QeTLaaBSf6KaCvDzVCcvVuYISC5oku5v6o+BIug8taRhcXN8/9gLQ9akrGG73z+u
dAoZ2+v1k7VZ704steLM9pf7aY+L6kR2Qmc7E4j/WM9Sv8CHUaJG41MRAbjdHtGh
zGcsZwf/mNjyjW3UalI1dih2muhPTSAZT0xL+wIDAQABAoIBAAFDBJT5RabjDL9f
peX1D+qFqnn+7g9XfG41w8QAGCrCCa9K2iDXvFHjNMlhoN9aoCYPuTg9AEOwR1yF
jp0mZt8qKr1fF/LD7k2ltDYr9Ua2ROWMJpWpf7YfcbSRCWL6rfMWC6uUvl1iFxhj
S/vGbJFT0xtI/YhfAjHfV1FvqpC4YwmKVe4/QqU0Kw+CjmYKLqeR0lvARP0aRfRm
GRIy/9ZtUzbcUSnLScNS8U2HhdKEOl/R9dSjHVG+rr+/sJmFRUiQWJx3UkDZ4lTd
lEIqj8i0CZMooARZjOIhIjP+wT9zr1sWFaxU81ourMnfoOQSKIrkUUYGc4rOk3Ao
DuqBx9ECgYEA5miGw2v+2YIm1XtmTRvLJiDgr8mTWydylPXxjTlghYNKts4bU5rh
EN/Ok+DUPLqPGQLnFgjcadT/clZfJXF3/gOS8V9hxVWI6Gwku1Ez7OqtCv3plA0i
46fxh0PmK6lFOzlbWMN1wpQ/mB3dWh4YaC6ERG1Z3L1DCIqYs/LVFsMCgYEA5R/A
dJAwtPWjdEjKFaajH7Z7iB/BSUydyPg++sMDjETbrV3wddO67LEkhH3vB4VDEKKt
FnD1iSyWprb94gDK0PTxQyHJ3JdzDP05L+7C+lwLqEKCrh2BZpScvub0FRgECNNO
OCuoMtX1HG/dGjkJsxB7e1lr7LGFMyPjR6I+0mkCgYEAzvcznpUCvnTX10naUgdW
SzCbQ6xJDkd3+HCYAuh4WFXgJicrisUDyHmRgWoim05lPe1KkJNzEim/MAB/xQ2Q
4H5rXx/zniPAMC78K7q8buM6fzYnu9K09VQldAC835lUU+eosyoYPKmYGlcxP0Lr
X6HxM9oaL1tevGxq0LGfUasCgYEA3R4ybouE5e61KxDgLdreTEmgl/MFZwbQs1WX
+grfzqvZUUt6N0v5dllSQ6cBWkGqQlCsOB8VZqeoUAYDp+tZ0CTC/SWLmR5zwtJS
MUb71f+kpGJjmUMSUXwUdUuPvRerNRUvxJelQEIpxaLTP25SRQQgFx9qP0fmoz78
JXKXrBkCgYEApBfmVsOTG5S+oO7WZFpndeofLnXYn9xRvlc738+dANY4mWwHJlBd
z2wzJ5wfjzlXsZoKcV0I6pRWLrgw3Gd5cwu3O5+MUN89cdQuVrfB77KQJHIF+S06
fHDgr/HSgH8LCXDq4DSd5XC0WxCPTDYrTN8iiHop2k35Ex0UXYeE+g0=
-----END RSA PRIVATE KEY-----


================================================
FILE: tests/Stubs/private.key.crlf
================================================
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAzjgUbG97UD+bZwkceejvxkbcq/17YqmriWGpBu7etXAA2WZp
x7vLaQi6ygApfCsYDz13W27DyriH2kg56GOa2v9M88OORiW1rQMaGF4hn/L7agFc
cvdNAWBKD8ue+QUPz3prA3TLF+lSqMn5BgF+4j7XNlODvfOX3Tra1JQcVik4pyjg
QeTLaaBSf6KaCvDzVCcvVuYISC5oku5v6o+BIug8taRhcXN8/9gLQ9akrGG73z+u
dAoZ2+v1k7VZ704steLM9pf7aY+L6kR2Qmc7E4j/WM9Sv8CHUaJG41MRAbjdHtGh
zGcsZwf/mNjyjW3UalI1dih2muhPTSAZT0xL+wIDAQABAoIBAAFDBJT5RabjDL9f
peX1D+qFqnn+7g9XfG41w8QAGCrCCa9K2iDXvFHjNMlhoN9aoCYPuTg9AEOwR1yF
jp0mZt8qKr1fF/LD7k2ltDYr9Ua2ROWMJpWpf7YfcbSRCWL6rfMWC6uUvl1iFxhj
S/vGbJFT0xtI/YhfAjHfV1FvqpC4YwmKVe4/QqU0Kw+CjmYKLqeR0lvARP0aRfRm
GRIy/9ZtUzbcUSnLScNS8U2HhdKEOl/R9dSjHVG+rr+/sJmFRUiQWJx3UkDZ4lTd
lEIqj8i0CZMooARZjOIhIjP+wT9zr1sWFaxU81ourMnfoOQSKIrkUUYGc4rOk3Ao
DuqBx9ECgYEA5miGw2v+2YIm1XtmTRvLJiDgr8mTWydylPXxjTlghYNKts4bU5rh
EN/Ok+DUPLqPGQLnFgjcadT/clZfJXF3/gOS8V9hxVWI6Gwku1Ez7OqtCv3plA0i
46fxh0PmK6lFOzlbWMN1wpQ/mB3dWh4YaC6ERG1Z3L1DCIqYs/LVFsMCgYEA5R/A
dJAwtPWjdEjKFaajH7Z7iB/BSUydyPg++sMDjETbrV3wddO67LEkhH3vB4VDEKKt
FnD1iSyWprb94gDK0PTxQyHJ3JdzDP05L+7C+lwLqEKCrh2BZpScvub0FRgECNNO
OCuoMtX1HG/dGjkJsxB7e1lr7LGFMyPjR6I+0mkCgYEAzvcznpUCvnTX10naUgdW
SzCbQ6xJDkd3+HCYAuh4WFXgJicrisUDyHmRgWoim05lPe1KkJNzEim/MAB/xQ2Q
4H5rXx/zniPAMC78K7q8buM6fzYnu9K09VQldAC835lUU+eosyoYPKmYGlcxP0Lr
X6HxM9oaL1tevGxq0LGfUasCgYEA3R4ybouE5e61KxDgLdreTEmgl/MFZwbQs1WX
+grfzqvZUUt6N0v5dllSQ6cBWkGqQlCsOB8VZqeoUAYDp+tZ0CTC/SWLmR5zwtJS
MUb71f+kpGJjmUMSUXwUdUuPvRerNRUvxJelQEIpxaLTP25SRQQgFx9qP0fmoz78
JXKXrBkCgYEApBfmVsOTG5S+oO7WZFpndeofLnXYn9xRvlc738+dANY4mWwHJlBd
z2wzJ5wfjzlXsZoKcV0I6pRWLrgw3Gd5cwu3O5+MUN89cdQuVrfB77KQJHIF+S06
fHDgr/HSgH8LCXDq4DSd5XC0WxCPTDYrTN8iiHop2k35Ex0UXYeE+g0=
-----END RSA PRIVATE KEY-----


================================================
FILE: tests/Stubs/public.key
================================================
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzjgUbG97UD+bZwkceejv
xkbcq/17YqmriWGpBu7etXAA2WZpx7vLaQi6ygApfCsYDz13W27DyriH2kg56GOa
2v9M88OORiW1rQMaGF4hn/L7agFccvdNAWBKD8ue+QUPz3prA3TLF+lSqMn5BgF+
4j7XNlODvfOX3Tra1JQcVik4pyjgQeTLaaBSf6KaCvDzVCcvVuYISC5oku5v6o+B
Iug8taRhcXN8/9gLQ9akrGG73z+udAoZ2+v1k7VZ704steLM9pf7aY+L6kR2Qmc7
E4j/WM9Sv8CHUaJG41MRAbjdHtGhzGcsZwf/mNjyjW3UalI1dih2muhPTSAZT0xL
+wIDAQAB
-----END PUBLIC KEY-----
Download .txt
gitextract_m4beu0jy/

├── .gitattributes
├── .github/
│   └── workflows/
│       └── ci.yml
├── .gitignore
├── .scrutinizer.yml
├── LICENSE
├── README.md
├── composer.json
├── examples/
│   ├── README.md
│   ├── composer.json
│   ├── public/
│   │   ├── auth_code.php
│   │   ├── client_credentials.php
│   │   ├── implicit.php
│   │   └── password.php
│   └── src/
│       ├── Entities/
│       │   └── UserEntity.php
│       └── Repositories/
│           ├── IdentityRepository.php
│           └── ScopeRepository.php
├── phpunit.xml.dist
├── src/
│   ├── ClaimExtractor.php
│   ├── Entities/
│   │   ├── ClaimSetEntity.php
│   │   ├── ClaimSetEntityInterface.php
│   │   ├── ClaimSetInterface.php
│   │   └── ScopeInterface.php
│   ├── Exception/
│   │   └── InvalidArgumentException.php
│   ├── IdTokenResponse.php
│   └── Repositories/
│       ├── ClaimSetRepositoryInterface.php
│       └── IdentityProviderInterface.php
└── tests/
    ├── Bootstrap.php
    ├── ClaimExtractorTest.php
    ├── ResponseTypes/
    │   └── IdTokenResponseTest.php
    └── Stubs/
        ├── IdentityProvider.php
        ├── UserEntity.php
        ├── UserNoClaimSetEntity.php
        ├── UserNoIdentifierEntity.php
        ├── private.key
        ├── private.key.crlf
        └── public.key
Download .txt
SYMBOL INDEX (55 symbols across 18 files)

FILE: examples/src/Entities/UserEntity.php
  class UserEntity (line 7) | class UserEntity extends \OAuth2ServerExamples\Entities\UserEntity imple...
    method getClaims (line 9) | public function getClaims()

FILE: examples/src/Repositories/IdentityRepository.php
  class IdentityRepository (line 7) | class IdentityRepository implements IdentityProviderInterface
    method getUserEntityByIdentifier (line 9) | public function getUserEntityByIdentifier($identifier)

FILE: examples/src/Repositories/ScopeRepository.php
  class ScopeRepository (line 16) | class ScopeRepository extends \OAuth2ServerExamples\Repositories\ScopeRe...
    method getScopeEntityByIdentifier (line 21) | public function getScopeEntityByIdentifier($scopeIdentifier)

FILE: src/ClaimExtractor.php
  class ClaimExtractor (line 13) | class ClaimExtractor
    method __construct (line 23) | public function __construct($claimSets = [])
    method addClaimSet (line 73) | public function addClaimSet(ClaimSetEntityInterface $claimSet)
    method getClaimSet (line 92) | public function getClaimSet($scope)
    method hasClaimSet (line 105) | public function hasClaimSet($scope)
    method extract (line 117) | public function extract(array $scopes, array $claims)

FILE: src/Entities/ClaimSetEntity.php
  class ClaimSetEntity (line 8) | class ClaimSetEntity implements ClaimSetEntityInterface
    method __construct (line 14) | public function __construct($scope, array $claims)
    method getScope (line 20) | public function getScope()
    method getClaims (line 25) | public function getClaims()

FILE: src/Entities/ClaimSetEntityInterface.php
  type ClaimSetEntityInterface (line 9) | interface ClaimSetEntityInterface extends ClaimSetInterface, ScopeInterface

FILE: src/Entities/ClaimSetInterface.php
  type ClaimSetInterface (line 9) | interface ClaimSetInterface
    method getClaims (line 14) | public function getClaims();

FILE: src/Entities/ScopeInterface.php
  type ScopeInterface (line 9) | interface ScopeInterface
    method getScope (line 14) | public function getScope();

FILE: src/Exception/InvalidArgumentException.php
  class InvalidArgumentException (line 9) | class InvalidArgumentException extends \Exception

FILE: src/IdTokenResponse.php
  class IdTokenResponse (line 21) | class IdTokenResponse extends BearerTokenResponse
    method __construct (line 38) | public function __construct(
    method getBuilder (line 48) | protected function getBuilder(AccessTokenEntityInterface $accessToken,...
    method getExtraParams (line 72) | protected function getExtraParams(AccessTokenEntityInterface $accessTo...
    method isOpenIDRequest (line 121) | private function isOpenIDRequest($scopes)

FILE: src/Repositories/ClaimSetRepositoryInterface.php
  type ClaimSetRepositoryInterface (line 9) | interface ClaimSetRepositoryInterface
    method getClaimSetByScopeIdentifier (line 11) | public function getClaimSetByScopeIdentifier($scopeIdentifier);

FILE: src/Repositories/IdentityProviderInterface.php
  type IdentityProviderInterface (line 12) | interface IdentityProviderInterface extends RepositoryInterface
    method getUserEntityByIdentifier (line 17) | public function getUserEntityByIdentifier($identifier);

FILE: tests/ClaimExtractorTest.php
  class ClaimExtractorTest (line 9) | class ClaimExtractorTest extends TestCase
    method testDefaultClaimSetsExist (line 11) | public function testDefaultClaimSetsExist()
    method testCanAddCustomClaimSet (line 20) | public function testCanAddCustomClaimSet()
    method testCanNotOverrideDefaultScope (line 30) | public function testCanNotOverrideDefaultScope()
    method testCanGetClaimSet (line 37) | public function testCanGetClaimSet()
    method testExtract (line 46) | public function testExtract()

FILE: tests/ResponseTypes/IdTokenResponseTest.php
  class IdTokenResponseTest (line 17) | class IdTokenResponseTest extends TestCase
    method testGeneratesDefaultHttpResponse (line 22) | public function testGeneratesDefaultHttpResponse($privateKey)
    method testOpenIDConnectHttpResponse (line 44) | public function testOpenIDConnectHttpResponse($privateKey)
    method testThrowsRuntimeExceptionWhenMissingClaimSetInterface (line 69) | public function testThrowsRuntimeExceptionWhenMissingClaimSetInterface...
    method testThrowsRuntimeExceptionWhenMissingIdentifierSetInterface (line 86) | public function testThrowsRuntimeExceptionWhenMissingIdentifierSetInte...
    method testClaimsGetExtractedFromUserEntity (line 100) | public function testClaimsGetExtractedFromUserEntity($privateKey)
    method provideCryptKeys (line 130) | public static function provideCryptKeys()
    method processResponseType (line 168) | private function processResponseType($responseType, $privateKey,  arra...

FILE: tests/Stubs/IdentityProvider.php
  class IdentityProvider (line 7) | class IdentityProvider implements IdentityProviderInterface
    method __construct (line 14) | public function __construct($type = null)
    method getUserEntityByIdentifier (line 28) | public function getUserEntityByIdentifier($identifier)

FILE: tests/Stubs/UserEntity.php
  class UserEntity (line 9) | class UserEntity implements UserEntityInterface, ClaimSetInterface
    method __construct (line 13) | public function __construct()
    method getClaims (line 18) | public function getClaims()

FILE: tests/Stubs/UserNoClaimSetEntity.php
  class UserNoClaimSetEntity (line 8) | class UserNoClaimSetEntity implements UserEntityInterface
    method __construct (line 12) | public function __construct()

FILE: tests/Stubs/UserNoIdentifierEntity.php
  class UserNoIdentifierEntity (line 7) | class UserNoIdentifierEntity implements ClaimSetInterface
    method getClaims (line 9) | public function getClaims()
Condensed preview — 36 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (64K chars).
[
  {
    "path": ".gitattributes",
    "chars": 201,
    "preview": "* text=auto\n\n/tests export-ignore\n/.gitattributes export-ignore\n/.gitignore export-ignore\n/.github export-ignore\n.scruti"
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 1383,
    "preview": "name: CI\n\non:\n    push:\n        branches:\n            - master\n            - develop\n    pull_request:\n    workflow_disp"
  },
  {
    "path": ".gitignore",
    "chars": 58,
    "preview": "/vendor/\nphpunit.xml\n/build\ncomposer.lock\n/examples/*.key\n"
  },
  {
    "path": ".scrutinizer.yml",
    "chars": 986,
    "preview": "build:\n    environment:\n        php: 7.4.30\n\nfilter:\n    excluded_paths:\n        - tests/*\n        - vendor/*\n        - "
  },
  {
    "path": "LICENSE",
    "chars": 1101,
    "preview": "The MIT License (MIT)\n\nCopyright (c) 2018 Steve Rhoades <sedonami@gmail.com>\n\nPermission is hereby granted, free of char"
  },
  {
    "path": "README.md",
    "chars": 6594,
    "preview": "# OAuth 2.0 OpenID Connect Server\n\n[![Build Status](https://travis-ci.org/steverhoades/oauth2-openid-connect-server.svg?"
  },
  {
    "path": "composer.json",
    "chars": 945,
    "preview": "{\n    \"name\": \"steverhoades/oauth2-openid-connect-server\",\n    \"description\": \"An OpenID Connect Server that sites on Th"
  },
  {
    "path": "examples/README.md",
    "chars": 1466,
    "preview": "# OpenID Connect Example Implementations\nThe following examples piggyback off the PHP Leagues OAuth2 Server examples. Pl"
  },
  {
    "path": "examples/composer.json",
    "chars": 591,
    "preview": "{\n    \"require\": {\n        \"slim/slim\": \"3.0.*\",\n        \"league/oauth2-server\": \"^7.0\"\n    },\n    \"require-dev\": {\n    "
  },
  {
    "path": "examples/public/auth_code.php",
    "chars": 4258,
    "preview": "<?php\n/**\n * @author      Alex Bilbie <hello@alexbilbie.com>\n * @copyright   Copyright (c) Alex Bilbie\n * @license     h"
  },
  {
    "path": "examples/public/client_credentials.php",
    "chars": 3070,
    "preview": "<?php\n/**\n * @author      Alex Bilbie <hello@alexbilbie.com>\n * @copyright   Copyright (c) Alex Bilbie\n * @license     h"
  },
  {
    "path": "examples/public/implicit.php",
    "chars": 3191,
    "preview": "<?php\n/**\n * @author      Alex Bilbie <hello@alexbilbie.com>\n * @copyright   Copyright (c) Alex Bilbie\n * @license     h"
  },
  {
    "path": "examples/public/password.php",
    "chars": 3060,
    "preview": "<?php\n\nuse League\\OAuth2\\Server\\AuthorizationServer;\nuse League\\OAuth2\\Server\\Exception\\OAuthServerException;\nuse League"
  },
  {
    "path": "examples/src/Entities/UserEntity.php",
    "chars": 1128,
    "preview": "<?php\n\nnamespace OpenIDConnectServerExamples\\Entities;\n\nuse OpenIDConnectServer\\Entities\\ClaimSetInterface;\n\nclass UserE"
  },
  {
    "path": "examples/src/Repositories/IdentityRepository.php",
    "chars": 347,
    "preview": "<?php\nnamespace OpenIDConnectServerExamples\\Repositories;\n\nuse OpenIDConnectServer\\Repositories\\IdentityProviderInterfac"
  },
  {
    "path": "examples/src/Repositories/ScopeRepository.php",
    "chars": 1232,
    "preview": "<?php\n/**\n * @author      Alex Bilbie <hello@alexbilbie.com>\n * @copyright   Copyright (c) Alex Bilbie\n * @license     h"
  },
  {
    "path": "phpunit.xml.dist",
    "chars": 1002,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit colors=\"true\" convertNoticesToExceptions=\"true\" convertWarningsToExcepti"
  },
  {
    "path": "src/ClaimExtractor.php",
    "chars": 3938,
    "preview": "<?php\n/**\n * @author Steve Rhoades <sedonami@gmail.com>\n * @license http://opensource.org/licenses/MIT MIT\n */\nnamespace"
  },
  {
    "path": "src/Entities/ClaimSetEntity.php",
    "chars": 543,
    "preview": "<?php\n/**\n * @author Steve Rhoades <sedonami@gmail.com>\n * @license http://opensource.org/licenses/MIT MIT\n */\nnamespace"
  },
  {
    "path": "src/Entities/ClaimSetEntityInterface.php",
    "chars": 233,
    "preview": "<?php\n/**\n * @author Steve Rhoades <sedonami@gmail.com>\n * @license http://opensource.org/licenses/MIT MIT\n */\nnamespace"
  },
  {
    "path": "src/Entities/ClaimSetInterface.php",
    "chars": 255,
    "preview": "<?php\n/**\n * @author Steve Rhoades <sedonami@gmail.com>\n * @license http://opensource.org/licenses/MIT MIT\n */\nnamespace"
  },
  {
    "path": "src/Entities/ScopeInterface.php",
    "chars": 252,
    "preview": "<?php\n/**\n * @author Steve Rhoades <sedonami@gmail.com>\n * @license http://opensource.org/licenses/MIT MIT\n */\nnamespace"
  },
  {
    "path": "src/Exception/InvalidArgumentException.php",
    "chars": 209,
    "preview": "<?php\n/**\n * @author Steve Rhoades <sedonami@gmail.com>\n * @license http://opensource.org/licenses/MIT MIT\n */\nnamespace"
  },
  {
    "path": "src/IdTokenResponse.php",
    "chars": 4542,
    "preview": "<?php\n/**\n * @author Steve Rhoades <sedonami@gmail.com>\n * @license http://opensource.org/licenses/MIT MIT\n */\nnamespace"
  },
  {
    "path": "src/Repositories/ClaimSetRepositoryInterface.php",
    "chars": 267,
    "preview": "<?php\n/**\n * @author Steve Rhoades <sedonami@gmail.com>\n * @license http://opensource.org/licenses/MIT MIT\n */\nnamespace"
  },
  {
    "path": "src/Repositories/IdentityProviderInterface.php",
    "chars": 520,
    "preview": "<?php\n/**\n * @author Steve Rhoades <sedonami@gmail.com>\n * @license http://opensource.org/licenses/MIT MIT\n */\nnamespace"
  },
  {
    "path": "tests/Bootstrap.php",
    "chars": 257,
    "preview": "<?php\n\nif (!@include_once __DIR__ . '/../vendor/autoload.php') {\n    $message = <<<MSG\nYou must set up the project depen"
  },
  {
    "path": "tests/ClaimExtractorTest.php",
    "chars": 2062,
    "preview": "<?php\nnamespace OpenIDConnectServer\\Test;\n\nuse OpenIDConnectServer\\ClaimExtractor;\nuse OpenIDConnectServer\\Entities\\Clai"
  },
  {
    "path": "tests/ResponseTypes/IdTokenResponseTest.php",
    "chars": 9535,
    "preview": "<?php\n\nnamespace OpenIDConnectServer\\Test\\ResponseTypes;\n\nuse OpenIDConnectServer\\ClaimExtractor;\nuse OpenIDConnectServe"
  },
  {
    "path": "tests/Stubs/IdentityProvider.php",
    "chars": 793,
    "preview": "<?php\n\nnamespace OpenIDConnectServer\\Test\\Stubs;\n\nuse OpenIDConnectServer\\Repositories\\IdentityProviderInterface;\n\nclass"
  },
  {
    "path": "tests/Stubs/UserEntity.php",
    "chars": 607,
    "preview": "<?php\n\nnamespace OpenIDConnectServer\\Test\\Stubs;\n\nuse League\\OAuth2\\Server\\Entities\\Traits\\EntityTrait;\nuse League\\OAuth"
  },
  {
    "path": "tests/Stubs/UserNoClaimSetEntity.php",
    "chars": 325,
    "preview": "<?php\n\nnamespace OpenIDConnectServer\\Test\\Stubs;\n\nuse League\\OAuth2\\Server\\Entities\\Traits\\EntityTrait;\nuse League\\OAuth"
  },
  {
    "path": "tests/Stubs/UserNoIdentifierEntity.php",
    "chars": 386,
    "preview": "<?php\n\nnamespace OpenIDConnectServer\\Test\\Stubs;\n\nuse OpenIDConnectServer\\Entities\\ClaimSetInterface;\n\nclass UserNoIdent"
  },
  {
    "path": "tests/Stubs/private.key",
    "chars": 1679,
    "preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAzjgUbG97UD+bZwkceejvxkbcq/17YqmriWGpBu7etXAA2WZp\nx7vLaQi6ygApfCsYDz13W27"
  },
  {
    "path": "tests/Stubs/private.key.crlf",
    "chars": 1679,
    "preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIEpQIBAAKCAQEAzjgUbG97UD+bZwkceejvxkbcq/17YqmriWGpBu7etXAA2WZp\nx7vLaQi6ygApfCsYDz13W27"
  },
  {
    "path": "tests/Stubs/public.key",
    "chars": 451,
    "preview": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzjgUbG97UD+bZwkceejv\nxkbcq/17YqmriWGpBu7etXAA2WZp"
  }
]

About this extraction

This page contains the full source code of the steverhoades/oauth2-openid-connect-server GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 36 files (57.8 KB), approximately 16.9k tokens, and a symbol index with 55 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!