Skip to content

Commit 5d57bbf

Browse files
authored
Merge pull request #235 from nikosev/feature/pkce
Add support for PKCE
2 parents 96613ef + 721a96e commit 5d57bbf

File tree

4 files changed

+98
-0
lines changed

4 files changed

+98
-0
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](http://keepachangelog.com/)
55
and this project adheres to [Semantic Versioning](http://semver.org/).
66

7+
## [Unreleased]
8+
9+
### Added
10+
* Support for [PKCE](https://tools.ietf.org/html/rfc7636). Currently the supported methods are 'plain' and 'S256'.
11+
712
## [0.9.1]
813

914
### Added

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,20 @@ if (!$data->active) {
129129

130130
```
131131

132+
## Example 8: PKCE Client ##
133+
134+
```php
135+
use Jumbojett\OpenIDConnectClient;
136+
137+
$oidc = new OpenIDConnectClient('https://id.provider.com',
138+
'ClientIDHere',
139+
null);
140+
$oidc->setCodeChallengeMethod('S256');
141+
$oidc->authenticate();
142+
$name = $oidc->requestUserInfo('given_name');
143+
144+
```
145+
132146

133147
## Development Environments ##
134148
In some cases you may need to disable SSL security on on your development systems.

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"require": {
66
"php": ">=5.4",
77
"phpseclib/phpseclib" : "~2.0",
8+
"paragonie/random_compat":"2.0.19",
89
"ext-json": "*",
910
"ext-curl": "*",
1011
"paragonie/random_compat": ">=2"

src/OpenIDConnectClient.php

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,17 @@ class OpenIDConnectClient
226226

227227
protected $enc_type = PHP_QUERY_RFC1738;
228228

229+
/**
230+
* @var string holds code challenge method for PKCE mode
231+
* @see https://tools.ietf.org/html/rfc7636
232+
*/
233+
private $codeChallengeMethod = false;
234+
235+
/**
236+
* @var array holds PKCE supported algorithms
237+
*/
238+
private $pkceAlgs = array('S256' => 'sha256', 'plain' => false);
239+
229240
/**
230241
* @param $provider_url string optional
231242
*
@@ -649,6 +660,21 @@ private function requestAuthorization() {
649660
$auth_params = array_merge($auth_params, array('response_type' => implode(' ', $this->responseTypes)));
650661
}
651662

663+
// If the client supports Proof Key for Code Exchange (PKCE)
664+
if (!empty($this->getCodeChallengeMethod()) && in_array($this->getCodeChallengeMethod(), $this->getProviderConfigValue('code_challenge_methods_supported'))) {
665+
$codeVerifier = bin2hex(random_bytes(64));
666+
$this->setCodeVerifier($codeVerifier);
667+
if (!empty($this->pkceAlgs[$this->getCodeChallengeMethod()])) {
668+
$codeChallenge = rtrim(strtr(base64_encode(hash($this->pkceAlgs[$this->getCodeChallengeMethod()], $codeVerifier, true)), '+/', '-_'), '=');
669+
} else {
670+
$codeChallenge = $codeVerifier;
671+
}
672+
$auth_params = array_merge($auth_params, array(
673+
'code_challenge' => $codeChallenge,
674+
'code_challenge_method' => $this->getCodeChallengeMethod()
675+
));
676+
}
677+
652678
$auth_endpoint .= (strpos($auth_endpoint, '?') === false ? '?' : '&') . http_build_query($auth_params, null, '&', $this->enc_type);
653679

654680
$this->commitSession();
@@ -746,6 +772,15 @@ protected function requestTokens($code) {
746772
unset($token_params['client_id']);
747773
}
748774

775+
if (!empty($this->getCodeChallengeMethod()) && !empty($this->getCodeVerifier())) {
776+
$headers = [];
777+
unset($token_params['client_secret']);
778+
$token_params = array_merge($token_params, array(
779+
'client_id' => $this->clientID,
780+
'code_verifier' => $this->getCodeVerifier()
781+
));
782+
}
783+
749784
// Convert token params to string format
750785
$token_params = http_build_query($token_params, null, '&', $this->enc_type);
751786

@@ -1588,6 +1623,35 @@ protected function unsetState() {
15881623
$this->unsetSessionKey('openid_connect_state');
15891624
}
15901625

1626+
/**
1627+
* Stores $codeVerifier
1628+
*
1629+
* @param string $codeVerifier
1630+
* @return string
1631+
*/
1632+
protected function setCodeVerifier($codeVerifier) {
1633+
$this->setSessionKey('openid_connect_code_verifier', $codeVerifier);
1634+
return $codeVerifier;
1635+
}
1636+
1637+
/**
1638+
* Get stored codeVerifier
1639+
*
1640+
* @return string
1641+
*/
1642+
protected function getCodeVerifier() {
1643+
return $this->getSessionKey('openid_connect_code_verifier');
1644+
}
1645+
1646+
/**
1647+
* Cleanup state
1648+
*
1649+
* @return void
1650+
*/
1651+
protected function unsetCodeVerifier() {
1652+
$this->unsetSessionKey('openid_connect_code_verifier');
1653+
}
1654+
15911655
/**
15921656
* Get the response code from last action/curl request.
15931657
*
@@ -1741,4 +1805,18 @@ public function getLeeway()
17411805
{
17421806
return $this->leeway;
17431807
}
1808+
1809+
/**
1810+
* @return string
1811+
*/
1812+
public function getCodeChallengeMethod() {
1813+
return $this->codeChallengeMethod;
1814+
}
1815+
1816+
/**
1817+
* @param string $codeChallengeMethod
1818+
*/
1819+
public function setCodeChallengeMethod($codeChallengeMethod) {
1820+
$this->codeChallengeMethod = $codeChallengeMethod;
1821+
}
17441822
}

0 commit comments

Comments
 (0)