Skip to content

Commit fa2aecb

Browse files
committed
Merge pull request #79 from kralos/jwt
2 parents 2c4f0fa + dbb20bd commit fa2aecb

File tree

3 files changed

+142
-7
lines changed

3 files changed

+142
-7
lines changed

lib/OAuth2.php

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ class OAuth2
243243
*
244244
* @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-4.5
245245
*/
246-
const GRANT_TYPE_REGEXP = '#^(authorization_code|token|password|client_credentials|refresh_token|https?://.*)$#';
246+
const GRANT_TYPE_REGEXP = '#^(authorization_code|token|password|client_credentials|refresh_token|https?://.+|urn:.+)$#';
247247

248248
/**
249249
* @}
@@ -823,12 +823,18 @@ public function grantAccessToken(Request $request = null)
823823
$stored = $this->grantAccessTokenRefreshToken($client, $input);
824824
break;
825825
default:
826-
if (filter_var($input["grant_type"], FILTER_VALIDATE_URL)) {
827-
// returns: true || array('scope' => scope)
828-
$stored = $this->grantAccessTokenExtension($client, $inputData, $authHeaders);
829-
} else {
830-
throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_REQUEST, 'Invalid grant_type parameter or parameter missing');
826+
if (substr($input["grant_type"], 0, 4) !== 'urn:'
827+
&& !filter_var($input["grant_type"], FILTER_VALIDATE_URL)
828+
) {
829+
throw new OAuth2ServerException(
830+
self::HTTP_BAD_REQUEST,
831+
self::ERROR_INVALID_REQUEST,
832+
'Invalid grant_type parameter or parameter missing'
833+
);
831834
}
835+
836+
// returns: true || array('scope' => scope)
837+
$stored = $this->grantAccessTokenExtension($client, $inputData, $authHeaders);
832838
}
833839

834840
if (!is_array($stored)) {
@@ -1001,7 +1007,12 @@ protected function grantAccessTokenExtension(IOAuth2Client $client, array $input
10011007
if (!($this->storage instanceof IOAuth2GrantExtension)) {
10021008
throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_UNSUPPORTED_GRANT_TYPE);
10031009
}
1004-
$uri = filter_var($inputData["grant_type"], FILTER_VALIDATE_URL);
1010+
1011+
$uri = $inputData["grant_type"];
1012+
if (substr($uri, 0, 4) !== 'urn:') {
1013+
$uri = filter_var($uri, FILTER_VALIDATE_URL);
1014+
}
1015+
10051016
$stored = $this->storage->checkGrantExtension($client, $uri, $inputData, $authHeaders);
10061017

10071018
if ($stored === false) {
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
namespace OAuth2\Tests\Fixtures;
4+
5+
use OAuth2\OAuth2;
6+
use OAuth2\IOAuth2GrantExtension;
7+
use OAuth2\OAuth2ServerException;
8+
use OAuth2\Model\IOAuth2Client;
9+
use OAuth2\Tests\Fixtures\OAuth2StorageStub;
10+
11+
class OAuth2GrantExtensionJwtBearer extends OAuth2StorageStub implements IOAuth2GrantExtension
12+
{
13+
protected $sub = null;
14+
15+
public function checkGrantExtension(IOAuth2Client $client, $uri, array $inputData, array $authHeaders)
16+
{
17+
if ('urn:ietf:params:oauth:grant-type:jwt-bearer' !== $uri) {
18+
throw new OAuth2ServerException(OAuth2::HTTP_BAD_REQUEST, OAuth2::ERROR_UNSUPPORTED_GRANT_TYPE);
19+
}
20+
21+
if (!isset($inputData['jwt'])) {
22+
return false;
23+
}
24+
25+
$jsonWebToken = $inputData['jwt'];
26+
$decodedJwtStruct = self::decodeJwt($jsonWebToken);
27+
28+
// Check our JWT has a subject
29+
if (!isset($decodedJwtStruct['sub'])) {
30+
return false;
31+
}
32+
33+
// Check the subject is the expected one
34+
if ($this->sub !== $decodedJwtStruct['sub']) {
35+
return false;
36+
}
37+
38+
return array(
39+
'data' => $decodedJwtStruct,
40+
);
41+
}
42+
43+
public function setExpectedSubject($sub)
44+
{
45+
$this->sub = $sub;
46+
}
47+
48+
/**
49+
* Let's pretend a JWT is endoded and signed by wrapping it in -ENCODED-JWT-
50+
*
51+
* In real life, we would verify the JWT is valid, and get the subject from it after decoding
52+
*
53+
* @param string An encoded JWT string
54+
* @return array The decoded JWT struct
55+
*/
56+
public static function decodeJwt($encodedJwt)
57+
{
58+
$decodedJwt = str_replace('-ENCODED-JWT-', '', $encodedJwt);
59+
return json_decode($decodedJwt, true);
60+
}
61+
62+
/**
63+
* Let's pretend a JWT is endoded and signed by wrapping it in -ENCODED-JWT-
64+
*
65+
* In real life, we would verify the JWT is valid, and get the subject from it after decoding
66+
*
67+
* @param array A struct to encode as a JWT
68+
* @return string The encoded JWT
69+
*/
70+
public static function encodeJwt($decodedStruct)
71+
{
72+
$decodedJwt = json_encode($decodedStruct);
73+
$wrapper = '-ENCODED-JWT-';
74+
$encodedJwt = sprintf(
75+
'%s%s%s',
76+
$wrapper,
77+
$decodedJwt,
78+
$wrapper
79+
);
80+
return $encodedJwt;
81+
}
82+
}

tests/OAuth2Test.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,48 @@ public function testGrantAccessTokenWithGrantExtensionLimitedLifetime()
684684
$this->assertRegExp('{"access_token":"[^"]+","expires_in":86400,"token_type":"bearer"}', $response->getContent());
685685
}
686686

687+
/**
688+
* Tests OAuth2->grantAccessToken() with urn: extension
689+
*/
690+
public function testGrantAccessTokenWithGrantExtensionJwtBearer()
691+
{
692+
$clientId = 'cid';
693+
$clientSecret = 'csecret';
694+
$grantType = 'urn:ietf:params:oauth:grant-type:jwt-bearer';
695+
$subject = 1234;
696+
697+
$stub = new \OAuth2\Tests\Fixtures\OAuth2GrantExtensionJwtBearer();
698+
$stub->addClient(new OAuth2Client($clientId, $clientSecret));
699+
$stub->setAllowedGrantTypes(array($grantType));
700+
$stub->setExpectedSubject($subject);
701+
$oauth2 = new OAuth2($stub);
702+
703+
$response = $oauth2->grantAccessToken(new Request(array(
704+
'grant_type' => $grantType,
705+
'client_id' => $clientId,
706+
'client_secret' => $clientSecret,
707+
'jwt' => \OAuth2\Tests\Fixtures\OAuth2GrantExtensionJwtBearer::encodeJwt(array(
708+
'sub' => $subject,
709+
)),
710+
)));
711+
712+
$this->assertSame(array(
713+
'content-type' => array('application/json'),
714+
'cache-control' => array('no-store, private'),
715+
'pragma' => array('no-cache'),
716+
), array_diff_key(
717+
$response->headers->all(),
718+
array('date' => null)
719+
));
720+
721+
$this->assertRegExp('{"access_token":"[^"]+","expires_in":3600,"token_type":"bearer","scope":null,"refresh_token":"[^"]+"}', $response->getContent());
722+
723+
$token = $stub->getLastAccessToken();
724+
$this->assertSame('cid', $token->getClientId());
725+
$data = $token->getData();
726+
$this->assertSame($subject, $data['sub']);
727+
}
728+
687729

688730
/**
689731
* Tests OAuth2->getAuthorizeParams()

0 commit comments

Comments
 (0)