Skip to content

Commit 05dd3a3

Browse files
committed
Added JWT Support:
- Using `lcobucci/jwt` for now. - Allows multiple credential pairs.
1 parent f93df84 commit 05dd3a3

File tree

9 files changed

+366
-8
lines changed

9 files changed

+366
-8
lines changed

composer.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@
1818
"php": ">=5.4",
1919
"php-http/client-implementation": "^1.0",
2020
"zendframework/zend-diactoros": "^1.3",
21-
"php-http/guzzle6-adapter": "^1.0"
21+
"php-http/guzzle6-adapter": "^1.0",
22+
"lcobucci/jwt": "^3.2"
2223
},
2324
"require-dev": {
2425
"phpunit/phpunit": "^5.3",
@@ -29,4 +30,4 @@
2930
"Nexmo\\": "src/"
3031
}
3132
}
32-
}
33+
}

src/Client.php

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
namespace Nexmo;
1010
use Http\Client\HttpClient;
1111
use Nexmo\Client\Credentials\Basic;
12+
use Nexmo\Client\Credentials\Container;
1213
use Nexmo\Client\Credentials\CredentialsInterface;
14+
use Nexmo\Client\Credentials\Keypair;
1315
use Nexmo\Client\Credentials\OAuth;
1416
use Nexmo\Client\Credentials\SharedSecret;
1517
use Nexmo\Client\Factory\FactoryInterface;
@@ -71,7 +73,7 @@ public function __construct(CredentialsInterface $credentials, $options = array(
7173
$this->setHttpClient($client);
7274

7375
//make sure we know how to use the credentials
74-
if(!($credentials instanceof Basic) && !($credentials instanceof SharedSecret) && !($credentials instanceof OAuth)){
76+
if(!($credentials instanceof Container) && !($credentials instanceof Basic) && !($credentials instanceof SharedSecret) && !($credentials instanceof OAuth)){
7577
throw new \RuntimeException('unknown credentials type: ' . get_class($credentials));
7678
}
7779

@@ -218,8 +220,15 @@ public static function authRequest(RequestInterface $request, Basic $credentials
218220
*/
219221
public function send(\Psr\Http\Message\RequestInterface $request)
220222
{
221-
//also an instance of Basic, so checked first
222-
if($this->credentials instanceof SharedSecret){
223+
if($this->credentials instanceof Container) {
224+
if (strpos($request->getUri()->getPath(), '/v1/calls') === 0) {
225+
$request = $request->withHeader('Authorization', 'Bearer ' . $this->credentials->get(Keypair::class)->getJwt());
226+
} else {
227+
$request = self::authRequest($request, $this->credentials->get(Basic::class));
228+
}
229+
} elseif($this->credentials instanceof Keypair){
230+
$request = $request->withHeader('Authorization', 'Bearer ' . $this->credentials->get(Keypair::class)->getJwt());
231+
} elseif($this->credentials instanceof SharedSecret){
223232
$request = self::signRequest($request, $this->credentials);
224233
} elseif($this->credentials instanceof Basic){
225234
$request = self::authRequest($request, $this->credentials);
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
/**
3+
* Nexmo Client Library for PHP
4+
*
5+
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
6+
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
7+
*/
8+
9+
namespace Nexmo\Client\Credentials;
10+
11+
class Container extends AbstractCredentials implements CredentialsInterface
12+
{
13+
protected $types = [
14+
Basic::class,
15+
SharedSecret::class,
16+
Keypair::class
17+
];
18+
19+
protected $credentials;
20+
21+
public function __construct($credentials)
22+
{
23+
if(!is_array($credentials)){
24+
$credentials = func_get_args();
25+
}
26+
27+
foreach($credentials as $credential){
28+
$this->addCredential($credential);
29+
}
30+
}
31+
32+
protected function addCredential(CredentialsInterface $credential)
33+
{
34+
$type = $this->getType($credential);
35+
if(isset($this->credentials[$type])){
36+
throw new \RuntimeException('can not use more than one of a single credential type');
37+
}
38+
39+
$this->credentials[$type] = $credential;
40+
}
41+
42+
protected function getType(CredentialsInterface $credential)
43+
{
44+
foreach ($this->types as $type) {
45+
if($credential instanceof $type){
46+
return $type;
47+
}
48+
}
49+
}
50+
51+
public function get($type)
52+
{
53+
if(!isset($this->credentials[$type])){
54+
throw new \RuntimeException('credental not set');
55+
}
56+
57+
return $this->credentials[$type];
58+
}
59+
60+
public function has($type)
61+
{
62+
return isset($this->credentials[$type]);
63+
}
64+
65+
}

src/Client/Credentials/Keypair.php

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?php
2+
/**
3+
* Nexmo Client Library for PHP
4+
*
5+
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
6+
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
7+
*/
8+
9+
namespace Nexmo\Client\Credentials;
10+
11+
use Lcobucci\JWT\Builder;
12+
use Lcobucci\JWT\Signer\Key;
13+
use Lcobucci\JWT\Signer\Rsa\Sha256;
14+
use Nexmo\Application\Application;
15+
16+
class Keypair extends AbstractCredentials implements CredentialsInterface
17+
{
18+
19+
protected $key;
20+
21+
protected $signer;
22+
23+
public function __construct($privateKey, $application = null)
24+
{
25+
$this->credentials['key'] = $privateKey;
26+
if($application){
27+
if($application instanceof Application){
28+
$application = $application->getId();
29+
}
30+
31+
$this->credentials['application'] = $application;
32+
}
33+
34+
$this->key = new Key($privateKey);
35+
$this->signer = new Sha256();
36+
}
37+
38+
public function getJwt($exp = null, $nfb = null, $jti = null, $iat = null)
39+
{
40+
if(is_null($exp)){
41+
$exp = time() + 60;
42+
}
43+
44+
if(is_null($iat)){
45+
$iat = time();
46+
}
47+
48+
if(is_null($jti)){
49+
$jti = base64_encode(mt_rand());
50+
}
51+
52+
$builder = new Builder();
53+
$builder->setIssuedAt($iat)
54+
->setExpiration($exp);
55+
56+
57+
if(isset($this->credentials['application'])){
58+
$builder->set('application_id', $this->credentials['application']);
59+
}
60+
61+
if(!is_null($nfb)){
62+
$builder->setNotBefore($nfb);
63+
}
64+
65+
if(!is_null($jti)){
66+
$builder->setId($jti);
67+
}
68+
69+
if(isset($this->credentials['application'])){
70+
$builder->set('application_id', $this->credentials['application']);
71+
}
72+
73+
return $builder->sign($this->signer, $this->key)->getToken();
74+
}
75+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
/**
3+
* Nexmo Client Library for PHP
4+
*
5+
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
6+
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
7+
*/
8+
9+
namespace NexmoTest\Client\Credentials;
10+
11+
12+
use Nexmo\Client\Credentials\Container;
13+
use Nexmo\Client\Credentials\Keypair;
14+
use Nexmo\Client\Credentials\Basic;
15+
use Nexmo\Client\Credentials\OAuth;
16+
use Nexmo\Client\Credentials\SharedSecret;
17+
use Webmozart\Expression\Selector\Key;
18+
19+
class ContainerTest extends \PHPUnit_Framework_TestCase
20+
{
21+
protected $types = [
22+
Basic::class,
23+
SharedSecret::class,
24+
Keypair::class
25+
];
26+
27+
protected $basic;
28+
protected $secret;
29+
protected $keypair;
30+
31+
public function setUp()
32+
{
33+
$this->basic = new Basic('key', 'secret');
34+
$this->secret = new SharedSecret('key', 'secret');
35+
$this->keypair = new Keypair('key', 'app');
36+
}
37+
38+
/**
39+
* @dataProvider credentials
40+
*/
41+
public function testBasic($credential, $type)
42+
{
43+
$container = new Container($credential);
44+
45+
$this->assertSame($credential, $container->get($type));
46+
$this->assertSame($credential, $container[$type]);
47+
48+
foreach($this->types as $class){
49+
if($type == $class){
50+
$this->assertTrue($container->has($class));
51+
} else {
52+
$this->assertFalse($container->has($class));
53+
}
54+
}
55+
}
56+
57+
/**
58+
* @dataProvider credentials
59+
*/
60+
public function testOnlyOneType($credential, $type)
61+
{
62+
$other = clone $credential;
63+
64+
$this->expectException('RuntimeException');
65+
66+
$container = new Container($credential, $other);
67+
}
68+
69+
public function testMultiple()
70+
{
71+
$container = new Container($this->basic, $this->secret, $this->keypair);
72+
73+
foreach($this->types as $class){
74+
$this->assertTrue($container->has($class));
75+
}
76+
77+
}
78+
79+
public function credentials()
80+
{
81+
return [
82+
[new Basic('key', 'secret'), Basic::class],
83+
[new SharedSecret('key', 'secret'), SharedSecret::class],
84+
[new Keypair('key', 'app'), Keypair::class]
85+
];
86+
}
87+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
/**
3+
* Nexmo Client Library for PHP
4+
*
5+
* @copyright Copyright (c) 2016 Nexmo, Inc. (http://nexmo.com)
6+
* @license https://github.com/Nexmo/nexmo-php/blob/master/LICENSE.txt MIT License
7+
*/
8+
9+
namespace NexmoTest\Client\Credentials;
10+
use Nexmo\Client\Credentials\Keypair;
11+
12+
class KeypairTest extends \PHPUnit_Framework_TestCase
13+
{
14+
protected $key;
15+
protected $application = 'c90ddd99-9a5d-455f-8ade-dde4859e590e';
16+
17+
public function setUp()
18+
{
19+
$this->key = file_get_contents(__DIR__ . '/test.key');
20+
}
21+
22+
public function testAsArray()
23+
{
24+
$credentials = new Keypair($this->key, $this->application);
25+
26+
$array = $credentials->asArray();
27+
$this->assertEquals($this->key, $array['key']);
28+
$this->assertEquals($this->application, $array['application']);
29+
}
30+
31+
public function testArrayAccess()
32+
{
33+
$credentials = new Keypair($this->key, $this->application);
34+
35+
$this->assertEquals($this->key, $credentials['key']);
36+
$this->assertEquals($this->application, $credentials['application']);
37+
}
38+
39+
public function testProperties()
40+
{
41+
$credentials = new Keypair($this->key, $this->application);
42+
43+
$this->assertEquals($this->key, $credentials->key);
44+
$this->assertEquals($this->application, $credentials->application);
45+
}
46+
47+
public function testGetJWT()
48+
{
49+
$credentials = new Keypair($this->key, $this->application);
50+
$jwt = $credentials->getJwt();
51+
$this->markTestIncomplete('generated JWT, but not tested as valid');
52+
}
53+
}

test/Client/Credentials/test.key

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCPvPoIrW4gW0hv
3+
UOC0my5Jas6FCEejtngnGlFwaktOzgSjZgicpCWqNuRyLwe1h08sA3ahhUiCP118
4+
39on8x94JAVWZ2y3p/OEhdEpKyygUT9kS/nZlMByDG6t4vDcKgwGd1sBb4ogxVd1
5+
/pGDe3N132rZf3cOoGC9HENoaQjGG3+eZiqCxapvlTN/QZ0AiQMN+KQYfrbq3S4s
6+
KeSbv+BZAezrP/TWSrNUQZoeJeN4TD5z8MKdBZ90QlBtiGUZwhcdbRn4dLKMPfmq
7+
YOAssgn5+++zoYCzEGPdvduxKcFEZnkziNZsbwnr9A2H7+i8zIHm3qbaqWxRqvlI
8+
9gvKDhe3AgMBAAECggEABUlrnucWHl2NJ/7/DNWKWcvyZaU80VI0UCfhJ/PY6kCc
9+
ng/yMCS/d+fF9kcxjuU3rcRA2EcJODUxcJbhNMf199rHUXrDXmvwgobTfyKl5Q2n
10+
+b3rpiuY+njnl0C6IDbxs0kvkTlziKoJgf8HhiEDyamaif5suB6BAGOqPQxj9Llf
11+
FJ4tCDxiGTnbdZypZtzCTC6Lo63voZ3XsBmY5rDAWRpr8yodCldUtL0gVE+wRVEd
12+
tyRLtO75yDWOr9V0UsA/BLUNoHJ7iwUq/3IX3tCM+n0WTszc7K7WlOQPT9d3Q/Lz
13+
4AWvC/CJYcrjtGiZhyERe1gjcE2lu2dA1ASw0loFSQKBgQDHZl8fv/FMYA4N/6T7
14+
grH0RCpW1N62oG0Doib6sZ033DFDa7Lwv3xo5VYqMkUwBXU6V+bn0Mhe65sahr7G
15+
BVXxgC8TC9XIbndggmByhO3l83gtF1yKBRv2/3q9hns4Yfsz3kTKjoxxMlrhKeDk
16+
K254NUqKZ11Z4OHo8Y3OeWwBzwKBgQC4ieHNYrnICedmIC0rmtDvJXmSUx9bg0JO
17+
wpY3QJFRC65oA/ph3sxElbODvBW9e+6kgBRif+np6AomFsahjcA69z2sR+PJnkIA
18+
zCCS8SkAY/crmmSI6jJQHcGHosRLdKLVSuekrakbbLt6E13F/ZO2EaAikCzKshnp
19+
fumOtt6NmQKBgGVKKGoNa7q7VIhh42Hryw/lDIjdS2ED7zyYQyq3zMBSdyfjbpuC
20+
+eSjEvkOXjz9mMYRXvdFBHPLRRfdeM1Iapbp4X/QVEGjc7qvn+Ssh9h2rAZjxptJ
21+
6yG2N5hM1w0WILABaXpnnQnnZWjZiCb/tPcVQw85YJ9GcBuPkNRgs6/bAoGAO6zS
22+
6UD4xPh27O6QzN4GnJ8ovinFJSnAIooIW5u0olm9r4NBz65lrfQfFgWXnivakzWb
23+
4fJtaSeRSJnq58lYFXloZzLkNYnI3EsmaX40/RxWjLIjuqbJWGEW+U6oXaI9Ge5c
24+
FEPYQLcbtTFYDLOgtarjdunaoj2P5ZMV4gG+3FkCgYAXTQV8+jb/5xgsuG5KHhhZ
25+
3YQ/UHLBI36CqunXL6u12f3LzMCK2+gAFVsPBn5JOXZCxreEekKxm4imq8mMlnde
26+
LGX3+B96ZQkr3rTLEsBD9rz58ig24R/cAuTZT6vPAZSQE32XU1KgOMewsV/QyyGM
27+
rMIxeQ87Gd13CfrKD4l3gw==
28+
-----END PRIVATE KEY-----

test/Client/Credentials/test.pub

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
-----BEGIN PUBLIC KEY-----
2+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAj7z6CK1uIFtIb1DgtJsu
3+
SWrOhQhHo7Z4JxpRcGpLTs4Eo2YInKQlqjbkci8HtYdPLAN2oYVIgj9dfN/aJ/Mf
4+
eCQFVmdst6fzhIXRKSssoFE/ZEv52ZTAcgxureLw3CoMBndbAW+KIMVXdf6Rg3tz
5+
dd9q2X93DqBgvRxDaGkIxht/nmYqgsWqb5Uzf0GdAIkDDfikGH626t0uLCnkm7/g
6+
WQHs6z/01kqzVEGaHiXjeEw+c/DCnQWfdEJQbYhlGcIXHW0Z+HSyjD35qmDgLLIJ
7+
+fvvs6GAsxBj3b3bsSnBRGZ5M4jWbG8J6/QNh+/ovMyB5t6m2qlsUar5SPYLyg4X
8+
twIDAQAB
9+
-----END PUBLIC KEY-----

0 commit comments

Comments
 (0)