Skip to content

Commit

Permalink
Merge pull request #79 from kralos/jwt
Browse files Browse the repository at this point in the history
  • Loading branch information
GuilhemN committed Mar 31, 2016
2 parents 2c4f0fa + dbb20bd commit fa2aecb
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 7 deletions.
25 changes: 18 additions & 7 deletions lib/OAuth2.php
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ class OAuth2
*
* @see http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-4.5
*/
const GRANT_TYPE_REGEXP = '#^(authorization_code|token|password|client_credentials|refresh_token|https?://.*)$#';
const GRANT_TYPE_REGEXP = '#^(authorization_code|token|password|client_credentials|refresh_token|https?://.+|urn:.+)$#';

/**
* @}
Expand Down Expand Up @@ -823,12 +823,18 @@ public function grantAccessToken(Request $request = null)
$stored = $this->grantAccessTokenRefreshToken($client, $input);
break;
default:
if (filter_var($input["grant_type"], FILTER_VALIDATE_URL)) {
// returns: true || array('scope' => scope)
$stored = $this->grantAccessTokenExtension($client, $inputData, $authHeaders);
} else {
throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_INVALID_REQUEST, 'Invalid grant_type parameter or parameter missing');
if (substr($input["grant_type"], 0, 4) !== 'urn:'
&& !filter_var($input["grant_type"], FILTER_VALIDATE_URL)
) {
throw new OAuth2ServerException(
self::HTTP_BAD_REQUEST,
self::ERROR_INVALID_REQUEST,
'Invalid grant_type parameter or parameter missing'
);
}

// returns: true || array('scope' => scope)
$stored = $this->grantAccessTokenExtension($client, $inputData, $authHeaders);
}

if (!is_array($stored)) {
Expand Down Expand Up @@ -1001,7 +1007,12 @@ protected function grantAccessTokenExtension(IOAuth2Client $client, array $input
if (!($this->storage instanceof IOAuth2GrantExtension)) {
throw new OAuth2ServerException(self::HTTP_BAD_REQUEST, self::ERROR_UNSUPPORTED_GRANT_TYPE);
}
$uri = filter_var($inputData["grant_type"], FILTER_VALIDATE_URL);

$uri = $inputData["grant_type"];
if (substr($uri, 0, 4) !== 'urn:') {
$uri = filter_var($uri, FILTER_VALIDATE_URL);
}

$stored = $this->storage->checkGrantExtension($client, $uri, $inputData, $authHeaders);

if ($stored === false) {
Expand Down
82 changes: 82 additions & 0 deletions tests/Fixtures/OAuth2GrantExtensionJwtBearer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

namespace OAuth2\Tests\Fixtures;

use OAuth2\OAuth2;
use OAuth2\IOAuth2GrantExtension;
use OAuth2\OAuth2ServerException;
use OAuth2\Model\IOAuth2Client;
use OAuth2\Tests\Fixtures\OAuth2StorageStub;

class OAuth2GrantExtensionJwtBearer extends OAuth2StorageStub implements IOAuth2GrantExtension
{
protected $sub = null;

public function checkGrantExtension(IOAuth2Client $client, $uri, array $inputData, array $authHeaders)
{
if ('urn:ietf:params:oauth:grant-type:jwt-bearer' !== $uri) {
throw new OAuth2ServerException(OAuth2::HTTP_BAD_REQUEST, OAuth2::ERROR_UNSUPPORTED_GRANT_TYPE);
}

if (!isset($inputData['jwt'])) {
return false;
}

$jsonWebToken = $inputData['jwt'];
$decodedJwtStruct = self::decodeJwt($jsonWebToken);

// Check our JWT has a subject
if (!isset($decodedJwtStruct['sub'])) {
return false;
}

// Check the subject is the expected one
if ($this->sub !== $decodedJwtStruct['sub']) {
return false;
}

return array(
'data' => $decodedJwtStruct,
);
}

public function setExpectedSubject($sub)
{
$this->sub = $sub;
}

/**
* Let's pretend a JWT is endoded and signed by wrapping it in -ENCODED-JWT-
*
* In real life, we would verify the JWT is valid, and get the subject from it after decoding
*
* @param string An encoded JWT string
* @return array The decoded JWT struct
*/
public static function decodeJwt($encodedJwt)
{
$decodedJwt = str_replace('-ENCODED-JWT-', '', $encodedJwt);
return json_decode($decodedJwt, true);
}

/**
* Let's pretend a JWT is endoded and signed by wrapping it in -ENCODED-JWT-
*
* In real life, we would verify the JWT is valid, and get the subject from it after decoding
*
* @param array A struct to encode as a JWT
* @return string The encoded JWT
*/
public static function encodeJwt($decodedStruct)
{
$decodedJwt = json_encode($decodedStruct);
$wrapper = '-ENCODED-JWT-';
$encodedJwt = sprintf(
'%s%s%s',
$wrapper,
$decodedJwt,
$wrapper
);
return $encodedJwt;
}
}
42 changes: 42 additions & 0 deletions tests/OAuth2Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,48 @@ public function testGrantAccessTokenWithGrantExtensionLimitedLifetime()
$this->assertRegExp('{"access_token":"[^"]+","expires_in":86400,"token_type":"bearer"}', $response->getContent());
}

/**
* Tests OAuth2->grantAccessToken() with urn: extension
*/
public function testGrantAccessTokenWithGrantExtensionJwtBearer()
{
$clientId = 'cid';
$clientSecret = 'csecret';
$grantType = 'urn:ietf:params:oauth:grant-type:jwt-bearer';
$subject = 1234;

$stub = new \OAuth2\Tests\Fixtures\OAuth2GrantExtensionJwtBearer();
$stub->addClient(new OAuth2Client($clientId, $clientSecret));
$stub->setAllowedGrantTypes(array($grantType));
$stub->setExpectedSubject($subject);
$oauth2 = new OAuth2($stub);

$response = $oauth2->grantAccessToken(new Request(array(
'grant_type' => $grantType,
'client_id' => $clientId,
'client_secret' => $clientSecret,
'jwt' => \OAuth2\Tests\Fixtures\OAuth2GrantExtensionJwtBearer::encodeJwt(array(
'sub' => $subject,
)),
)));

$this->assertSame(array(
'content-type' => array('application/json'),
'cache-control' => array('no-store, private'),
'pragma' => array('no-cache'),
), array_diff_key(
$response->headers->all(),
array('date' => null)
));

$this->assertRegExp('{"access_token":"[^"]+","expires_in":3600,"token_type":"bearer","scope":null,"refresh_token":"[^"]+"}', $response->getContent());

$token = $stub->getLastAccessToken();
$this->assertSame('cid', $token->getClientId());
$data = $token->getData();
$this->assertSame($subject, $data['sub']);
}


/**
* Tests OAuth2->getAuthorizeParams()
Expand Down

0 comments on commit fa2aecb

Please sign in to comment.