Skip to content

Commit

Permalink
Finished GNP Auth and SimSwap MVP implementation (#475)
Browse files Browse the repository at this point in the history
* Finished GNP Auth and SimSwap MVP implementation

* Add documentation

* PSR-4 fix

* clean imports

* Finished GNP Auth and SimSwap MVP implementation

* bring psr7 trait back (#482)

* Fix broken git history
  • Loading branch information
SecondeJK authored Jun 24, 2024
1 parent 9d6b64a commit 060cbfe
Show file tree
Hide file tree
Showing 47 changed files with 692 additions and 85 deletions.
46 changes: 46 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -980,6 +980,52 @@ $response = $client->account()->updateConfig([
print_r($response->toArray());
```

### Use SimSwap to Check the Status and Date of a SIM in a handset

In order to use Vonage's Network APIs you'll need to be enabled within
the [Vonage Network Registry](https://developer.vonage.com/en/getting-started-network/registration?source=sim-swap)

Once you have a registered MSNDIN, you will be able to use SimSwap.

SimSwap uses the Global Network Platform authentication mechanism, so the
authorisation flow looks a little different from other API Clients. Under
the hood, the SDK will handle multiple calls for you to configure a
CAMARA standard access token.

Here's an example of checking if a SIM has been recently been swapped:

```php
$credentials = new \Vonage\Client\Credentials\Gnp(
'tel:+447700900000',
fopen('./my-private-key'),
'my-application-id'
);

$client = new \Vonage\Client($credentials);

if ($client->simswap()->checkSimSwap('07700009999', 240)) {
echo 'Warning: SIM Swap Check Failed'
} else {
echo 'SIM Swap Check Pass'
}
```

And here is how you retrieve the swap date:

```php
$credentials = new \Vonage\Client\Credentials\Gnp(
'tel:+447700900000',
fopen('./my-private-key'),
'my-application-id'
);

$client = new \Vonage\Client($credentials);
$date = $client->simswap()->checkSimSwapDate('07700009999')

echo $date;
```


### Get Information About a Number

The [Number Insights API](https://developer.nexmo.com/api/number-insight) allows a user to check that a number is valid and to find out more about how to use it.
Expand Down
9 changes: 9 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,15 @@
<testsuite name="verify2">
<directory>test/Verify2</directory>
</testsuite>
<testsuite name="client">
<directory>test/Client</directory>
</testsuite>
<testsuite name="conversations">
<directory>test/Conversation</directory>
</testsuite>
<testsuite name="client">
<directory>test/Client</directory>
</testsuite>
<testsuite name="proactive_connect">
<directory>test/ProactiveConnect</directory>
</testsuite>
Expand All @@ -41,6 +47,9 @@
<testsuite name="sms">
<directory>test/SMS</directory>
</testsuite>
<testsuite name="simswap">
<directory>test/SimSwap</directory>
</testsuite>
<testsuite name="subaccount">
<directory>test/Subaccount</directory>
</testsuite>
Expand Down
2 changes: 1 addition & 1 deletion src/Account/ClientFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public function __invoke(ContainerInterface $container): Client
->setBaseUrl($accountApi->getClient()->getRestUrl())
->setIsHAL(false)
->setBaseUri('/account')
->setAuthHandler(new BasicQueryHandler())
->setAuthHandlers(new BasicQueryHandler())
;

return new Client($accountApi);
Expand Down
2 changes: 1 addition & 1 deletion src/Application/ClientFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public function __invoke(ContainerInterface $container): Client
$api
->setBaseUri('/v2/applications')
->setCollectionName('applications')
->setAuthHandler(new BasicHandler());
->setAuthHandlers(new BasicHandler());

return new Client($api, new Hydrator());
}
Expand Down
4 changes: 4 additions & 0 deletions src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
use Vonage\Numbers\ClientFactory as NumbersClientFactory;
use Vonage\Redact\ClientFactory as RedactClientFactory;
use Vonage\Secrets\ClientFactory as SecretsClientFactory;
use Vonage\SimSwap\ClientFactory as SimSwapClientFactory;
use Vonage\SMS\ClientFactory as SMSClientFactory;
use Vonage\Subaccount\ClientFactory as SubaccountClientFactory;
use Vonage\Messages\ClientFactory as MessagesClientFactory;
Expand Down Expand Up @@ -79,6 +80,7 @@
* @method Numbers\Client numbers()
* @method Redact\Client redact()
* @method Secrets\Client secrets()
* @method SimSwap\Client simswap()
* @method SMS\Client sms()
* @method Subaccount\Client subaccount()
* @method Users\Client users()
Expand Down Expand Up @@ -219,6 +221,7 @@ public function __construct(
'messages' => MessagesClientFactory::class,
'redact' => RedactClientFactory::class,
'secrets' => SecretsClientFactory::class,
'simswap' => SimSwapClientFactory::class,
'sms' => SMSClientFactory::class,
'subaccount' => SubaccountClientFactory::class,
'users' => UsersClientFactory::class,
Expand All @@ -228,6 +231,7 @@ public function __construct(

// Additional utility classes
APIResource::class => APIResource::class,
Client::class => function() { return $this; }
];

if (class_exists('Vonage\Video\ClientFactory')) {
Expand Down
29 changes: 15 additions & 14 deletions src/Client/APIResource.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<?php

declare(strict_types=1);

namespace Vonage\Client;
Expand Down Expand Up @@ -28,7 +29,7 @@ class APIResource implements ClientAwareInterface
/**
* @var HandlerInterface[]
*/
protected array $authHandler = [];
protected array $authHandlers = [];

/**
* Base URL that we will hit. This can be overridden from the underlying
Expand Down Expand Up @@ -68,8 +69,8 @@ public function addAuth(RequestInterface $request): RequestInterface
{
$credentials = $this->getClient()->getCredentials();

if (is_array($this->getAuthHandler())) {
foreach ($this->getAuthHandler() as $handler) {
if (is_array($this->getAuthHandlers())) {
foreach ($this->getAuthHandlers() as $handler) {
try {
$request = $handler($request, $credentials);
break;
Expand All @@ -84,7 +85,7 @@ public function addAuth(RequestInterface $request): RequestInterface
return $request;
}

return $this->getAuthHandler()($request, $credentials);
return $this->getAuthHandlers()($request, $credentials);
}

/**
Expand All @@ -106,7 +107,7 @@ public function create(array $body, string $uri = '', array $headers = []): ?arr

$request->getBody()->write(json_encode($body));

if ($this->getAuthHandler()) {
if ($this->getAuthHandlers()) {
$request = $this->addAuth($request);
}

Expand Down Expand Up @@ -154,7 +155,7 @@ public function delete(string $id, array $headers = []): ?array
$headers
);

if ($this->getAuthHandler()) {
if ($this->getAuthHandlers()) {
$request = $this->addAuth($request);
}

Expand Down Expand Up @@ -207,7 +208,7 @@ public function get($id, array $query = [], array $headers = [], bool $jsonRespo
$headers
);

if ($this->getAuthHandler()) {
if ($this->getAuthHandlers()) {
$request = $this->addAuth($request);
}

Expand All @@ -231,10 +232,10 @@ public function get($id, array $query = [], array $headers = [], bool $jsonRespo
return json_decode($response->getBody()->getContents(), true);
}

public function getAuthHandler()
public function getAuthHandlers()
{
// If we have not set a handler, default to Basic and issue warning.
if (!$this->authHandler) {
if (!$this->authHandlers) {
$this->log(
LogLevel::WARNING,
'Warning: no authorisation handler set for this Client. Defaulting to Basic which might not be
Expand All @@ -244,7 +245,7 @@ public function getAuthHandler()
return new BasicHandler();
}

return $this->authHandler;
return $this->authHandlers;
}

public function getBaseUrl(): ?string
Expand Down Expand Up @@ -352,12 +353,12 @@ public function search(?FilterInterface $filter = null, string $uri = ''): Itera
*
* @return $this
*/
public function setAuthHandler($handler): self
public function setAuthHandlers($handler): self
{
if (!is_array($handler)) {
$handler = [$handler];
}
$this->authHandler = $handler;
$this->authHandlers = $handler;

return $this;
}
Expand Down Expand Up @@ -430,7 +431,7 @@ public function submit(array $formData = [], string $uri = '', array $headers =
$headers
);

if ($this->getAuthHandler()) {
if ($this->getAuthHandlers()) {
$request = $this->addAuth($request);
}

Expand Down Expand Up @@ -473,7 +474,7 @@ protected function updateEntity(string $method, string $id, array $body, array $
$headers
);

if ($this->getAuthHandler()) {
if ($this->getAuthHandlers()) {
$request = $this->addAuth($request);
}

Expand Down
25 changes: 25 additions & 0 deletions src/Client/Credentials/Gnp.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace Vonage\Client\Credentials;

class Gnp extends Keypair
{
public function __construct(protected string $msisdn, protected string $key, $application = null)
{
parent::__construct($key, $application);
}

public function getMsisdn(): string
{
return $this->msisdn;
}

public function setMsisdn(string $msisdn): Gnp
{
$this->msisdn = $msisdn;

return $this;
}
}
2 changes: 1 addition & 1 deletion src/Client/Credentials/Handler/AbstractHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

abstract class AbstractHandler implements HandlerInterface
{
abstract function __invoke(RequestInterface $request, CredentialsInterface $credentials): RequestInterface;
abstract public function __invoke(RequestInterface $request, CredentialsInterface $credentials): RequestInterface;

protected function extract(string $class, CredentialsInterface $credentials): CredentialsInterface
{
Expand Down
60 changes: 60 additions & 0 deletions src/Client/Credentials/Handler/GnpHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

namespace Vonage\Client\Credentials\Handler;

use Psr\Http\Message\RequestInterface;
use Vonage\Client;
use Vonage\Client\APIResource;
use Vonage\Client\Credentials\CredentialsInterface;
use Vonage\Client\Credentials\Gnp;

/**
* This handler is for Vonage GNP APIs that require the CAMARA standard OAuth Flow
*/
class GnpHandler extends AbstractHandler
{
use Client\ClientAwareTrait;
use Client\ScopeAwareTrait;

protected const VONAGE_GNP_AUTH_BACKEND_URL = 'https://api-eu.vonage.com/oauth2/bc-authorize';
protected const VONAGE_GNP_AUTH_TOKEN_URL = 'https://api-eu.vonage.com/oauth2/token';
protected const VONAGE_GNP_AUTH_TOKEN_GRANT_TYPE = 'urn:openid:params:grant-type:ciba';

public function __invoke(RequestInterface $request, CredentialsInterface $credentials): RequestInterface
{
/** @var Gnp $credentials */
$credentials = $this->extract(Gnp::class, $credentials);
$msisdn = $credentials->getMsisdn();

// Request OIDC, returns Auth Request ID
// Reconfigure new client for GNP Auth
$api = new APIResource();
$api->setAuthHandlers(new KeypairHandler());
$api->setClient($this->getClient());
$api->setBaseUrl(self::VONAGE_GNP_AUTH_BACKEND_URL);

// This handler requires an injected client configured with a Gnp credentials object and a configured scope
$response = $api->submit([
'login_hint' => $msisdn,
'scope' => $this->getScope()
]);

$decoded = json_decode($response, true, 512, JSON_THROW_ON_ERROR);

$authReqId = $decoded['auth_req_id'];

// CAMARA Access Token
$api->setBaseUrl(self::VONAGE_GNP_AUTH_TOKEN_URL);
$response = $api->submit([
'grant_type' => self::VONAGE_GNP_AUTH_TOKEN_GRANT_TYPE,
'auth_req_id' => $authReqId
]);

$decoded = json_decode($response, true, 512, JSON_THROW_ON_ERROR);

$token = $decoded['access_token'];

// Add CAMARA Access Token to request and return to make API call
return $request->withHeader('Authorization', 'Bearer ' . $token);
}
}
25 changes: 25 additions & 0 deletions src/Client/ScopeAwareTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace Vonage\Client;

use RuntimeException;
use Vonage\Client;

trait ScopeAwareTrait
{
protected ?string $scope = null;

public function setScope(string $scope): self
{
$this->scope = $scope;

return $this;
}

public function getScope(): ?string
{
return $this->scope;
}
}
3 changes: 2 additions & 1 deletion src/Conversation/ClientFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ class ClientFactory
{
public function __invoke(ContainerInterface $container): Client
{
/** @var APIResource $api */
$api = $container->make(APIResource::class);
$api->setIsHAL(true)
->setErrorsOn200(false)
->setAuthHandler(new KeypairHandler())
->setAuthHandlers(new KeypairHandler())
->setBaseUrl('https://api.nexmo.com/v1/conversations');

return new Client($api);
Expand Down
2 changes: 1 addition & 1 deletion src/Conversion/ClientFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public function __invoke(ContainerInterface $container): Client
/** @var APIResource $api */
$api = $container->make(APIResource::class);
$api->setBaseUri('/conversions/');
$api->setAuthHandler(new BasicHandler());
$api->setAuthHandlers(new BasicHandler());

return new Client($api);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Entity/IterableAPICollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,7 @@ protected function fetchPage($absoluteUri): void

$request = new Request($requestUri, 'GET');

if ($this->getApiResource()->getAuthHandler()) {
if ($this->getApiResource()->getAuthHandlers()) {
$request = $this->getApiResource()->addAuth($request);
}

Expand Down
Loading

0 comments on commit 060cbfe

Please sign in to comment.