Skip to content

Commit cdd11dd

Browse files
committed
Bulk of work pre-testing
1 parent 94b7fa2 commit cdd11dd

File tree

15 files changed

+433
-26
lines changed

15 files changed

+433
-26
lines changed

src/Client/Credentials/Gnp.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
class Gnp extends Keypair
88
{
9+
protected ?string $code = null;
10+
protected ?string $state = null;
11+
protected ?string $redirectUri = null;
12+
913
public function __construct(protected string $msisdn, protected string $key, $application = null)
1014
{
1115
parent::__construct($key, $application);
@@ -22,4 +26,37 @@ public function setMsisdn(string $msisdn): Gnp
2226

2327
return $this;
2428
}
29+
30+
public function getCode(): ?string
31+
{
32+
return $this->code;
33+
}
34+
35+
public function setCode(?string $code): Gnp
36+
{
37+
$this->code = $code;
38+
return $this;
39+
}
40+
41+
public function getState(): ?string
42+
{
43+
return $this->state;
44+
}
45+
46+
public function setState(?string $state): Gnp
47+
{
48+
$this->state = $state;
49+
return $this;
50+
}
51+
52+
public function getRedirectUri(): ?string
53+
{
54+
return $this->redirectUri;
55+
}
56+
57+
public function setRedirectUri(?string $redirectUri): Gnp
58+
{
59+
$this->redirectUri = $redirectUri;
60+
return $this;
61+
}
2562
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
namespace Vonage\Client\Credentials\Handler;
4+
5+
use Psr\Http\Message\RequestInterface;
6+
use Vonage\Client;
7+
use Vonage\Client\APIResource;
8+
use Vonage\Client\Credentials\CredentialsInterface;
9+
use Vonage\Client\Credentials\Gnp;
10+
11+
/**
12+
* This handler is for Vonage GNP APIs that require the CAMARA standard OAuth Flow
13+
*/
14+
class NumberVerificationGnpHandler extends SimSwapGnpHandler
15+
{
16+
use Client\ClientAwareTrait;
17+
use Client\ScopeAwareTrait;
18+
19+
protected ?string $baseUrl = null;
20+
protected ?string $tokenUrl = null;
21+
22+
public function getBaseUrl(): ?string
23+
{
24+
return $this->baseUrl;
25+
}
26+
27+
public function setBaseUrl(?string $baseUrl): NumberVerificationGnpHandler
28+
{
29+
$this->baseUrl = $baseUrl;
30+
return $this;
31+
}
32+
33+
public function getTokenUrl(): ?string
34+
{
35+
return $this->tokenUrl;
36+
}
37+
38+
public function setTokenUrl(?string $tokenUrl): NumberVerificationGnpHandler
39+
{
40+
$this->tokenUrl = $tokenUrl;
41+
return $this;
42+
}
43+
44+
public function __invoke(RequestInterface $request, CredentialsInterface $credentials): RequestInterface
45+
{
46+
/** @var Gnp $credentials */
47+
$credentials = $this->extract(Gnp::class, $credentials);
48+
49+
// submit the code to CAMARA endpoint
50+
$api = new APIResource();
51+
$api->setAuthHandlers(new KeypairHandler());
52+
$api->setClient($this->getClient());
53+
$api->setBaseUrl('https://api-eu.vonage.com/oauth2/token');
54+
55+
$tokenRequest = $api->submit([
56+
'grant_type' => 'authorization_code',
57+
'code' => $credentials->getCode(),
58+
'redirect_uri' => $credentials->getRedirectUri()
59+
]);
60+
61+
$token = $tokenRequest['access_token'];
62+
63+
// Add CAMARA Access Token to request and return to make API call
64+
return $request->withHeader('Authorization', 'Bearer ' . $token);
65+
}
66+
}

src/Client/Credentials/Handler/GnpHandler.php renamed to src/Client/Credentials/Handler/SimSwapGnpHandler.php

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,35 @@
1111
/**
1212
* This handler is for Vonage GNP APIs that require the CAMARA standard OAuth Flow
1313
*/
14-
class GnpHandler extends AbstractHandler
14+
class SimSwapGnpHandler extends AbstractHandler
1515
{
1616
use Client\ClientAwareTrait;
1717
use Client\ScopeAwareTrait;
1818

19-
protected const VONAGE_GNP_AUTH_BACKEND_URL = 'https://api-eu.vonage.com/oauth2/bc-authorize';
20-
protected const VONAGE_GNP_AUTH_TOKEN_URL = 'https://api-eu.vonage.com/oauth2/token';
21-
protected const VONAGE_GNP_AUTH_TOKEN_GRANT_TYPE = 'urn:openid:params:grant-type:ciba';
19+
protected ?string $baseUrl = null;
20+
protected ?string $tokenUrl = null;
21+
22+
public function getBaseUrl(): ?string
23+
{
24+
return $this->baseUrl;
25+
}
26+
27+
public function setBaseUrl(?string $baseUrl): SimSwapGnpHandler
28+
{
29+
$this->baseUrl = $baseUrl;
30+
return $this;
31+
}
32+
33+
public function getTokenUrl(): ?string
34+
{
35+
return $this->tokenUrl;
36+
}
37+
38+
public function setTokenUrl(?string $tokenUrl): SimSwapGnpHandler
39+
{
40+
$this->tokenUrl = $tokenUrl;
41+
return $this;
42+
}
2243

2344
public function __invoke(RequestInterface $request, CredentialsInterface $credentials): RequestInterface
2445
{
@@ -31,7 +52,7 @@ public function __invoke(RequestInterface $request, CredentialsInterface $creden
3152
$api = new APIResource();
3253
$api->setAuthHandlers(new KeypairHandler());
3354
$api->setClient($this->getClient());
34-
$api->setBaseUrl(self::VONAGE_GNP_AUTH_BACKEND_URL);
55+
$api->setBaseUrl($this->getBaseUrl());
3556

3657
// This handler requires an injected client configured with a Gnp credentials object and a configured scope
3758
$response = $api->submit([
@@ -44,9 +65,9 @@ public function __invoke(RequestInterface $request, CredentialsInterface $creden
4465
$authReqId = $decoded['auth_req_id'];
4566

4667
// CAMARA Access Token
47-
$api->setBaseUrl(self::VONAGE_GNP_AUTH_TOKEN_URL);
68+
$api->setBaseUrl($this->getTokenUrl());
4869
$response = $api->submit([
49-
'grant_type' => self::VONAGE_GNP_AUTH_TOKEN_GRANT_TYPE,
70+
'grant_type' => 'urn:openid:params:grant-type:ciba',
5071
'auth_req_id' => $authReqId
5172
]);
5273

src/Client/Credentials/Keypair.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*/
1717
class Keypair extends AbstractCredentials
1818
{
19-
public function __construct(protected string $key, $application = null)
19+
public function __construct(protected string $key, protected ?string $application = null)
2020
{
2121
$this->credentials['key'] = $key;
2222

@@ -42,6 +42,17 @@ public function getKeyRaw(): string
4242
return $this->key;
4343
}
4444

45+
public function getApplication(): ?string
46+
{
47+
return $this->application;
48+
}
49+
50+
public function setApplication(mixed $application): Keypair
51+
{
52+
$this->application = $application;
53+
return $this;
54+
}
55+
4556
public function generateJwt(array $claims = []): Token
4657
{
4758
$generator = new TokenGenerator($this->application, $this->getKeyRaw());

src/NumberVerification/Client.php

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,15 @@
44

55
namespace Vonage\NumberVerification;
66

7+
use GuzzleHttp\Psr7\Request;
8+
use Psr\Http\Client\ClientExceptionInterface;
79
use Vonage\Client\APIClient;
810
use Vonage\Client\APIResource;
11+
use Vonage\Client\Credentials\CredentialsInterface;
12+
use Vonage\Client\Credentials\Gnp;
13+
use Vonage\Client\Exception\Credentials;
14+
use Vonage\Client\Exception\Exception;
15+
use Vonage\Webhook\Factory;
916

1017
class Client implements APIClient
1118
{
@@ -18,8 +25,81 @@ public function getAPIResource(): APIResource
1825
return $this->api;
1926
}
2027

21-
public function verify(): bool
28+
/**
29+
* You are expected to call this code when you are consuming the webhook that has been
30+
* received from the frontend call being made
31+
*
32+
* @param string $phoneNumber
33+
* @return bool
34+
* @throws ClientExceptionInterface
35+
* @throws Exception
36+
*/
37+
public function verifyNumber(string $phoneNumber): bool
2238
{
23-
return false;
39+
$webhook = Factory::createFromGlobals();
40+
41+
if (!isset($webhook['code'])) {
42+
throw new Exception('Required field code not found in webhook');
43+
};
44+
45+
/** @var Gnp $credentials */
46+
$credentials = $this->getAPIResource()->getClient()->getCredentials();
47+
$credentials->setCode($webhook['code']);
48+
$credentials->setState($webhook['state']);
49+
50+
$phoneNumberKey = 'phoneNumber';
51+
52+
if ($this->isHashedPhoneNumber($phoneNumber)) {
53+
$phoneNumberKey = 'hashedPhoneNumber';
54+
}
55+
56+
// This request will now contain a valid CAMARA token
57+
$response = $this->getAPIResource()->create([
58+
$phoneNumberKey => $phoneNumber
59+
]);
60+
61+
return $response['devicePhoneNumberVerified'];
62+
}
63+
64+
public function isHashedPhoneNumber(string $phoneNumber): bool
65+
{
66+
return (strlen($phoneNumber) >= 13);
67+
}
68+
69+
/**
70+
* This method is the start of the process of Number Verification
71+
* It builds the correct Front End Auth request for OIDC CAMARA request
72+
*
73+
* @param string $phoneNumber
74+
* @param string $redirectUrl
75+
* @param string $state
76+
* @return string
77+
* @throws Credentials
78+
*/
79+
public function buildFrontEndUrl(string $phoneNumber, string $redirectUrl, string $state = ''): string
80+
{
81+
/** @var Gnp $credentials */
82+
$credentials = $this->getAPIResource()->getClient()->getCredentials();
83+
$this->enforceCredentials($credentials);
84+
85+
$applicationId = $credentials->getApplication();
86+
87+
$query = http_build_query([
88+
'client_id' => $applicationId,
89+
'redirect_uri' => $redirectUrl,
90+
'state' => $state,
91+
'scope' => 'openid dpv:FraudPreventionAndDetection#number-verification-verify-read',
92+
'response_type' => 'code',
93+
'login_hint' => $phoneNumber
94+
]);
95+
96+
return 'https://oidc.idp.vonage.com/oauth2/auth' . $query;
97+
}
98+
99+
protected function enforceCredentials(CredentialsInterface $credentials): void
100+
{
101+
if (!$credentials instanceof Gnp) {
102+
throw new Credentials('You can only use GNP Credentials with the Number Verification API');
103+
}
24104
}
25105
}

src/NumberVerification/ClientFactory.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,24 @@
44

55
use Psr\Container\ContainerInterface;
66
use Vonage\Client\APIResource;
7-
use Vonage\Client\Credentials\Handler\GnpHandler;
7+
use Vonage\Client\Credentials\Handler\SimSwapGnpHandler;
88

99
class ClientFactory
1010
{
1111
public function __invoke(ContainerInterface $container): Client
1212
{
13-
$handler = new GnpHandler();
13+
$handler = new SimSwapGnpHandler();
14+
$handler->setBaseUrl('https://oidc.idp.vonage.com/oauth2/auth');
15+
$handler->setTokenUrl('https://api-eu.vonage.com/oauth2/token');
16+
$handler->setScope('openid+dpv:FraudPreventionAndDetection#number-verification-verify-read');
17+
1418
$client = $container->get(\Vonage\Client::class);
1519
$handler->setClient($client);
1620

1721
/** @var APIResource $api */
1822
$api = $container->make(APIResource::class);
1923
$api
20-
->setBaseUrl('https://api-eu.vonage.com/camara/sim-swap/v040')
24+
->setBaseUrl('https://api-eu.vonage.com/camara/number-verification/v031')
2125
->setIsHAL(false)
2226
->setErrorsOn200(false)
2327
->setAuthHandlers($handler);

src/SimSwap/Client.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
use Vonage\Client\APIClient;
88
use Vonage\Client\APIResource;
9-
use Vonage\Client\Credentials\Handler\GnpHandler;
9+
use Vonage\Client\Credentials\Handler\SimSwapGnpHandler;
1010

1111
class Client implements APIClient
1212
{
@@ -22,10 +22,10 @@ public function getAPIResource(): APIResource
2222

2323
public function checkSimSwap(string $number, ?int $maxAge = null)
2424
{
25-
/** @var GnpHandler $handler */
25+
/** @var SimSwapGnpHandler $handler */
2626
$handler = $this->getAPIResource()->getAuthHandlers()[0];
2727

28-
if (!$handler instanceof GnpHandler) {
28+
if (!$handler instanceof SimSwapGnpHandler) {
2929
throw new \RuntimeException('SimSwap Client has been misconfigured. Only a GNP Handler can be used');
3030
}
3131

@@ -46,10 +46,10 @@ public function checkSimSwap(string $number, ?int $maxAge = null)
4646

4747
public function checkSimSwapDate(string $number): string
4848
{
49-
/** @var GnpHandler $handler */
49+
/** @var SimSwapGnpHandler $handler */
5050
$handler = $this->getAPIResource()->getAuthHandlers()[0];
5151

52-
if (!$handler instanceof GnpHandler) {
52+
if (!$handler instanceof SimSwapGnpHandler) {
5353
throw new \RuntimeException('SimSwap Client has been misconfigured. Only a GNP Handler can be used');
5454
}
5555

src/SimSwap/ClientFactory.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,17 @@
44

55
use Psr\Container\ContainerInterface;
66
use Vonage\Client\APIResource;
7-
use Vonage\Client\Credentials\Handler\GnpHandler;
7+
use Vonage\Client\Credentials\Handler\SimSwapGnpHandler;
88

99
class ClientFactory
1010
{
1111
public function __invoke(ContainerInterface $container): Client
1212
{
13-
$handler = new GnpHandler();
13+
$handler = new SimSwapGnpHandler();
14+
$handler->setBaseUrl('https://api-eu.vonage.com/oauth2/bc-authorize');
15+
$handler->setTokenUrl('https://api-eu.vonage.com/oauth2/token');
16+
$handler->setScope('openid dpv:FraudPreventionAndDetection#check-sim-swap');
17+
1418
$client = $container->get(\Vonage\Client::class);
1519
$handler->setClient($client);
1620

0 commit comments

Comments
 (0)