Skip to content

Commit

Permalink
Merge pull request #9 from coudenysj/jwt-token-exp
Browse files Browse the repository at this point in the history
Add support for the JWT exp payload
  • Loading branch information
eljam authored Jun 27, 2018
2 parents b61b2e7 + 8643312 commit cec479a
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 5 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,16 @@ $jwtManager = new JwtManager(
);
```

## Authorization Header Type

Some endpoints use different Authorization header types (Bearer, JWT, etc...).

The default is Bearer, but another type can be supplied in the middleware:

```php
$stack->push(new JwtMiddleware($jwtManager, 'JWT'));
```

## Cached token

To avoid too many calls between multiple request, there is a cache system.
Expand All @@ -161,3 +171,5 @@ $jwtManager = new JwtManager(
'expire_key' => 'expires_in', # default is expires_in if not set
]
);

The bundle natively supports the [exp field](https://tools.ietf.org/html/rfc7519.html#section-4.1.4) in the JWT payload.
40 changes: 39 additions & 1 deletion Tests/JwtMiddlewareTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public function testJwtAuthorizationHeader()
function (RequestInterface $request) {
$this->assertTrue($request->hasHeader('Authorization'));
$this->assertSame(
sprintf(JwtMiddleware::AUTH_BEARER, 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9'),
'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9',
$request->getHeader('Authorization')[0]
);

Expand All @@ -53,4 +53,42 @@ function (RequestInterface $request) {
$client = new Client(['handler' => $handler]);
$client->get('http://api.example.com/api/ping');
}

/**
* testJwtAuthorizationHeaderType.
*/
public function testJwtAuthorizationHeaderType()
{
$authMockHandler = new MockHandler([
new Response(
200,
['Content-Type' => 'application/json'],
json_encode(['token' => 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9'])
),
]);

$authClient = new Client(['handler' => $authMockHandler]);
$jwtManager = new JwtManager(
$authClient,
(new HttpBasicAuthStrategy(['username' => 'test', 'password' => 'test']))
);

$mockHandler = new MockHandler([
function (RequestInterface $request) {
$this->assertTrue($request->hasHeader('Authorization'));
$this->assertSame(
'JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9',
$request->getHeader('Authorization')[0]
);

return new Response(200, [], json_encode(['data' => 'pong']));
},
]);

$handler = HandlerStack::create($mockHandler);
$handler->push(new JwtMiddleware($jwtManager, 'JWT'));

$client = new Client(['handler' => $handler]);
$client->get('http://api.example.com/api/ping');
}
}
63 changes: 63 additions & 0 deletions Tests/Manager/JwtManagerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -212,4 +212,67 @@ function (RequestInterface $request) {
$this->assertInstanceOf(JwtToken::class, $token);
$this->assertEquals('1453720507', $token->getToken());
}

public function testGetTokenShouldUseTheCachedTokenIfItIsValidBasedOnExpField()
{
$jwtToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9'
. '.eyJleHAiOiIzMjUwMzY4MDAwMCJ9'
. '.k4YJmJooaa9B4pAM_U8Pi-4ss6RdKFtj9iQqLIAndVA';

$mockHandler = new MockHandler(
[
function (RequestInterface $request) use ($jwtToken) {

$this->assertTrue($request->hasHeader('timeout'));
$this->assertEquals(
3,
$request->getHeaderLine('timeout')
);

return new Response(
200,
['Content-Type' => 'application/json'],
json_encode(['token' => $jwtToken])
);
},
function (RequestInterface $request) {

$this->assertTrue($request->hasHeader('timeout'));
$this->assertEquals(
3,
$request->getHeaderLine('timeout')
);

return new Response(
200,
['Content-Type' => 'application/json'],
json_encode(['token' => uniqid('token', true)])
);
},
]
);

$handler = HandlerStack::create($mockHandler);

$authClient = new Client([
'handler' => $handler,
]);

$authStrategy = new QueryAuthStrategy(['username' => 'admin', 'password' => 'admin']);

$jwtManager = new JwtManager(
$authClient,
$authStrategy,
['token_url' => '/api/token', 'timeout' => 3]
);
$token = $jwtManager->getJwtToken();

$this->assertInstanceOf(JwtToken::class, $token);
$this->assertEquals($jwtToken, $token->getToken());

$token = $jwtManager->getJwtToken();

$this->assertInstanceOf(JwtToken::class, $token);
$this->assertEquals($jwtToken, $token->getToken());
}
}
16 changes: 12 additions & 4 deletions src/JwtMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,30 @@
*/
class JwtMiddleware
{
const AUTH_BEARER = 'Bearer %s';

/**
* $JwtManager.
*
* @var JwtManager
*/
protected $jwtManager;

/**
* The Authorization Header Type (defaults to Bearer)
*
* @var string
*/
protected $authorizationHeaderType;

/**
* Constructor.
*
* @param JwtManager $jwtManager
* @param string $authorizationHeaderType
*/
public function __construct(JwtManager $jwtManager)
public function __construct(JwtManager $jwtManager, $authorizationHeaderType = 'Bearer')
{
$this->jwtManager = $jwtManager;
$this->authorizationHeaderType = $authorizationHeaderType;
}

/**
Expand All @@ -48,9 +55,10 @@ public function __invoke(callable $handler)
$manager
) {
$token = $manager->getJwtToken()->getToken();

return $handler($request->withHeader(
'Authorization',
sprintf(self::AUTH_BEARER, $token)
sprintf('%s %s', $this->authorizationHeaderType, $token)
), $options);
};
}
Expand Down
7 changes: 7 additions & 0 deletions src/Manager/JwtManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,13 @@ public function getJwtToken()

if ($expiresIn) {
$expiration = new \DateTime('now + ' . $expiresIn . ' seconds');
} elseif (count($jwtParts = explode('.', $body[$this->options['token_key']])) === 3
&& is_array($payload = json_decode(base64_decode($jwtParts[1]), true))
// https://tools.ietf.org/html/rfc7519.html#section-4.1.4
&& array_key_exists('exp', $payload)
) {
// Manually process the payload part to avoid having to drag in a new library
$expiration = new \DateTime('@' . $payload['exp']);
} else {
$expiration = null;
}
Expand Down

0 comments on commit cec479a

Please sign in to comment.