Skip to content

Commit

Permalink
Custom implementation of PKCE for thephpleague/oauth2-client till the…
Browse files Browse the repository at this point in the history
  • Loading branch information
rhertogh committed Nov 12, 2021
1 parent 39aa6cc commit 3406f6f
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 5 deletions.
120 changes: 119 additions & 1 deletion tests/_helpers/ClientTokenProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

class ClientTokenProvider extends GenericProvider
{

public function getAccessTokenRequestWrapper($grant, array $options = [])
{
$grant = $this->verifyGrant($grant);
Expand All @@ -24,4 +23,123 @@ public function getAccessTokenRequestWrapper($grant, array $options = [])
$params = $grant->prepareRequestParameters($params, $options);
return $this->getAccessTokenRequest($params);
}




# region PKCE https://github.com/thephpleague/oauth2-client/pull/901
const PKCE_METHOD_S256 = 'S256';
protected $pkceCode;
public $pkceMethod = null;
public function setPkceCode($pkceCode)
{
$this->pkceCode = $pkceCode;
return $this;
}
public function getPkceCode()
{
return $this->pkceCode;
}
protected function getRandomPkceCode($length = 64)
{
return substr(
strtr(
base64_encode(random_bytes($length)),
'+/',
'-_'
),
0,
$length
);
}
protected function getPkceMethod()
{
return $this->pkceMethod;
}
protected function getAuthorizationParameters(array $options)
{
if (empty($options['state'])) {
$options['state'] = $this->getRandomState();
}
if (empty($options['scope'])) {
$options['scope'] = $this->getDefaultScopes();
}
$options += [
'response_type' => 'code',
'approval_prompt' => 'auto'
];
if (is_array($options['scope'])) {
$separator = $this->getScopeSeparator();
$options['scope'] = implode($separator, $options['scope']);
}
// Store the state as it may need to be accessed later on.
$this->state = $options['state'];

$pkceMethod = $this->getPkceMethod();
if (!empty($pkceMethod)) {
$this->pkceCode = $this->getRandomPkceCode();
if ($pkceMethod === static::PKCE_METHOD_S256) {
$options['code_challenge'] = trim(
strtr(
base64_encode(hash('sha256', $this->pkceCode, true)),
'+/',
'-_'
),
'='
);
} elseif ($pkceMethod === static::PKCE_METHOD_PLAIN) {
$options['code_challenge'] = $this->pkceCode;
} else {
throw new InvalidArgumentException('Unknown PKCE method "' . $pkceMethod . '".');
}
$options['code_challenge_method'] = $pkceMethod;
}

// Business code layer might set a different redirect_uri parameter
// depending on the context, leave it as-is
if (!isset($options['redirect_uri'])) {
$options['redirect_uri'] = $this->redirectUri;
}
$options['client_id'] = $this->clientId;
return $options;
}
public function getAccessToken($grant, array $options = [])
{
$grant = $this->verifyGrant($grant);
$params = [
'client_id' => $this->clientId,
'client_secret' => $this->clientSecret,
'redirect_uri' => $this->redirectUri,
];

if (!empty($this->pkceCode)) {
$params['code_verifier'] = $this->pkceCode;
}

$params = $grant->prepareRequestParameters($params, $options);
$request = $this->getAccessTokenRequest($params);
$response = $this->getParsedResponse($request);
if (false === is_array($response)) {
throw new UnexpectedValueException(
'Invalid response received from Authorization Server. Expected JSON.'
);
}
$prepared = $this->prepareAccessTokenResponse($response);
$token = $this->createAccessToken($prepared, $grant);
return $token;
}
protected function getConfigurableOptions()
{
return array_merge($this->getRequiredOptions(), [
'accessTokenMethod',
'accessTokenResourceOwnerId',
'scopeSeparator',
'responseError',
'responseCode',
'responseResourceOwnerId',
'scopes',
'pkceMethod',
]);
}
# endregion
}
5 changes: 1 addition & 4 deletions tests/functional/AuthorizationCodeGrantCest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,13 @@
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Signer\Rsa\Sha256;
use League\OAuth2\Client\Token\AccessToken;
use rhertogh\Yii2Oauth2Server\helpers\UrlHelper;
use rhertogh\Yii2Oauth2Server\interfaces\components\authorization\Oauth2ClientAuthorizationRequestInterface;
use rhertogh\Yii2Oauth2Server\Oauth2Module;
use Yii;
use yii\helpers\ArrayHelper;
use yii\helpers\Json;
use Yii2Oauth2ServerTests\_helpers\ClientTokenProvider;
use Yii2Oauth2ServerTests\_helpers\TestUserModel;
use Yii2Oauth2ServerTests\functional\_base\BaseGrantCest;
use Yii2Oauth2ServerTests\ApiTester;
use Yii2Oauth2ServerTests\functional\_base\BaseGrantCest;

/**
* @covers \rhertogh\Yii2Oauth2Server\controllers\web\server\Oauth2AuthorizeAction
Expand Down

0 comments on commit 3406f6f

Please sign in to comment.