diff --git a/README.md b/README.md
index 2eebab78..c23d5d09 100644
--- a/README.md
+++ b/README.md
@@ -82,6 +82,7 @@ Included service implementations
- LinkedIn
- Mailchimp
- Microsoft
+ - Microsoft Graph
- Mondo
- Nest
- Netatmo
diff --git a/composer.lock b/composer.lock
index 42ceafef..5f941acb 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,8 +4,59 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "hash": "734ee27aca2b4b8a33857520f518ef0c",
- "packages": [],
+ "content-hash": "4707edb722c13e038d3a84b1c38c2b64",
+ "packages": [
+ {
+ "name": "vlucas/phpdotenv",
+ "version": "v2.5.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/vlucas/phpdotenv.git",
+ "reference": "8abb4f9aa89ddea9d52112c65bbe8d0125e2fa8e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/8abb4f9aa89ddea9d52112c65bbe8d0125e2fa8e",
+ "reference": "8abb4f9aa89ddea9d52112c65bbe8d0125e2fa8e",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.9"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.8.35 || ^5.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.5-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Dotenv\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Vance Lucas",
+ "email": "vance@vancelucas.com",
+ "homepage": "http://www.vancelucas.com"
+ }
+ ],
+ "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
+ "keywords": [
+ "dotenv",
+ "env",
+ "environment"
+ ],
+ "time": "2018-07-29T20:33:41+00:00"
+ }
+ ],
"packages-dev": [
{
"name": "phpunit/php-code-coverage",
@@ -66,7 +117,7 @@
"testing",
"xunit"
],
- "time": "2014-09-02 10:13:14"
+ "time": "2014-09-02T10:13:14+00:00"
},
{
"name": "phpunit/php-file-iterator",
@@ -113,7 +164,7 @@
"filesystem",
"iterator"
],
- "time": "2015-06-21 13:08:43"
+ "time": "2015-06-21T13:08:43+00:00"
},
{
"name": "phpunit/php-text-template",
@@ -154,7 +205,7 @@
"keywords": [
"template"
],
- "time": "2015-06-21 13:50:34"
+ "time": "2015-06-21T13:50:34+00:00"
},
{
"name": "phpunit/php-timer",
@@ -195,7 +246,7 @@
"keywords": [
"timer"
],
- "time": "2015-06-21 08:01:12"
+ "time": "2015-06-21T08:01:12+00:00"
},
{
"name": "phpunit/php-token-stream",
@@ -245,7 +296,7 @@
"keywords": [
"tokenizer"
],
- "time": "2014-03-03 05:10:30"
+ "time": "2014-03-03T05:10:30+00:00"
},
{
"name": "phpunit/phpunit",
@@ -318,7 +369,7 @@
"testing",
"xunit"
],
- "time": "2014-10-17 09:04:17"
+ "time": "2014-10-17T09:04:17+00:00"
},
{
"name": "phpunit/phpunit-mock-objects",
@@ -367,7 +418,7 @@
"mock",
"xunit"
],
- "time": "2013-01-13 10:24:48"
+ "time": "2013-01-13T10:24:48+00:00"
},
{
"name": "predis/predis",
@@ -491,19 +542,19 @@
"phpcs",
"standards"
],
- "time": "2015-09-09 00:18:50"
+ "time": "2015-09-09T00:18:50+00:00"
},
{
"name": "symfony/http-foundation",
"version": "v2.7.4",
"source": {
"type": "git",
- "url": "https://github.com/symfony/HttpFoundation.git",
+ "url": "https://github.com/symfony/http-foundation.git",
"reference": "7253c2041652353e71560bbd300d6256d170ddaf"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/7253c2041652353e71560bbd300d6256d170ddaf",
+ "url": "https://api.github.com/repos/symfony/http-foundation/zipball/7253c2041652353e71560bbd300d6256d170ddaf",
"reference": "7253c2041652353e71560bbd300d6256d170ddaf",
"shasum": ""
},
@@ -544,7 +595,7 @@
],
"description": "Symfony HttpFoundation Component",
"homepage": "https://symfony.com",
- "time": "2015-08-27 06:45:45"
+ "time": "2015-08-27T06:45:45+00:00"
},
{
"name": "symfony/yaml",
@@ -593,7 +644,7 @@
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
- "time": "2015-08-24 07:13:45"
+ "time": "2015-08-24T07:13:45+00:00"
}
],
"aliases": [],
diff --git a/examples/bootstrap.php b/examples/bootstrap.php
index f02da414..3d43d28c 100644
--- a/examples/bootstrap.php
+++ b/examples/bootstrap.php
@@ -18,10 +18,10 @@
/**
* Create a new instance of the URI class with the current URI, stripping the query string
- */
+
$uriFactory = new \OAuth\Common\Http\Uri\UriFactory();
$currentUri = $uriFactory->createFromSuperGlobalArray($_SERVER);
-$currentUri->setQuery('');
+$currentUri->setQuery('');*/
/**
* Load the credential for the different services
diff --git a/examples/init.example.php b/examples/init.example.php
index 6ab1cdb0..2aadd85c 100644
--- a/examples/init.example.php
+++ b/examples/init.example.php
@@ -206,6 +206,11 @@
'key' => '',
'secret' => ''
),
+ 'microsoft_grapht' => array(
+ 'CLIENT_ID' => '',
+ 'SECRET_CLIENT' => '',
+ 'CALLBACK_URL' => ''
+ )
);
/** @var $serviceFactory \OAuth\ServiceFactory An OAuth service factory. */
diff --git a/examples/microsoftGraph.php b/examples/microsoftGraph.php
new file mode 100644
index 00000000..65ab6bc9
--- /dev/null
+++ b/examples/microsoftGraph.php
@@ -0,0 +1,38 @@
+setHttpClient(new CurlClient);
+$microsofttGraphtnService = $serviceFactory->createService('microsoftgraph', $credentials, $storage);
+
+
+if (!empty($_GET['code'])) {
+ // Obtiene el token a partir del codigo que se obtiene del callback
+ $token = $microsofttGraphtnService->requestAccessToken($_GET['code']);
+
+ // Send a request para obtener las licencias que tiene el tenant
+ $result = json_decode($microsofttGraphtnService->request('/subscribedSkus?api-version=1.6'), true);
+
+ // Se muestra el resultado
+ echo "
";
+ var_dump($result);
+ echo "
";
+
+} else {
+ $url = $microsofttGraphtnService->getAuthorizationUri();
+ header('Location: ' . $url);
+}
diff --git a/phpunit.xml b/phpunit.xml
new file mode 100644
index 00000000..e3e3e85a
--- /dev/null
+++ b/phpunit.xml
@@ -0,0 +1,45 @@
+
+
+
+
+ tests/Unit
+
+
+
+
+ src/
+
+ src/OAuth/bootstrap.php
+ src/OAuth/Common/Exception/Exception.php
+ src/OAuth/Common/Http/Exception/TokenResponseException.php
+ src/OAuth/Common/Storage/Exception/StorageException.php
+ src/OAuth/Common/Storage/Exception/TokenNotFoundException.php
+ src/OAuth/Common/Token/Exception/ExpiredTokenException.php
+ src/OAuth/OAuth1/Signature/Exception/UnsupportedHashAlgorithmException.php
+ src/OAuth/OAuth2/Service/Exception/InvalidScopeException.php
+ src/OAuth/OAuth2/Service/Exception/MissingRefreshTokenException.php
+ src/OAuth/OAuth2/Token/StdOAuth2Token.php
+
+
+
+
+
+
+
+
diff --git a/src/OAuth/Common/Http/Client/CurlClient.php b/src/OAuth/Common/Http/Client/CurlClient.php
index eae1be3e..04045425 100644
--- a/src/OAuth/Common/Http/Client/CurlClient.php
+++ b/src/OAuth/Common/Http/Client/CurlClient.php
@@ -87,13 +87,13 @@ public function retrieveResponse(
curl_setopt($ch, CURLOPT_URL, $endpoint->getAbsoluteUri());
- if ($method === 'POST' || $method === 'PUT') {
+ if ($method === 'POST' || $method === 'PUT' || $method === 'PATCH') {
if ($requestBody && is_array($requestBody)) {
$requestBody = http_build_query($requestBody, '', '&');
}
- if ($method === 'PUT') {
- curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
+ if ($method === 'PUT' || $method === 'PATCH') {
+ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
} else {
curl_setopt($ch, CURLOPT_POST, true);
}
@@ -102,7 +102,6 @@ public function retrieveResponse(
} else {
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
}
-
if ($this->maxRedirects > 0) {
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_MAXREDIRS, $this->maxRedirects);
diff --git a/src/OAuth/OAuth2/Service/MicrosoftGraph.php b/src/OAuth/OAuth2/Service/MicrosoftGraph.php
new file mode 100644
index 00000000..2bd78fe2
--- /dev/null
+++ b/src/OAuth/OAuth2/Service/MicrosoftGraph.php
@@ -0,0 +1,137 @@
+
+ */
+class MicrosoftGraph extends AbstractService
+{
+ private $resources = array(
+ 'graph' => 'https://graph.windows.net/'
+ );
+ public function __construct(CredentialsInterface $credentials, ClientInterface $httpClient, TokenStorageInterface $storage, array $scopes = array(), UriInterface $baseApiUri = null, $stateParameterInAutUrl = false, $apiVersion = "")
+ {
+ parent::__construct($credentials, $httpClient, $storage, $scopes, $baseApiUri, $stateParameterInAutUrl, $apiVersion);
+ if(is_null($baseApiUri)){
+ $this->baseApiUri = new Uri($this->resources['graph'].'myorganization/');
+ }
+ }
+
+ /**
+ * Parses the access token response and returns a TokenInterface.
+ *
+ *
+ * @param string $responseBody
+ *
+ * @return TokenInterface
+ *
+ * @throws TokenResponseException
+ */
+ protected function parseAccessTokenResponse($responseBody)
+ {
+ $data = json_decode($responseBody, true);
+ if (null === $data || !is_array($data)) {
+ throw new TokenResponseException('Unable to parse response.');
+ } elseif (isset($data['error_description'])) {
+ throw new TokenResponseException('Error in retrieving token: "' . $data['error_description'] . '"');
+ } elseif (isset($data['error'])) {
+ throw new TokenResponseException('Error in retrieving token: "' . $data['error'] . '"');
+ }
+
+ $token = new StdOAuth2Token();
+ $token->setAccessToken($data['access_token']);
+ $token->setLifeTime($data['expires_in']);
+ if (isset($data['refresh_token'])) {
+ $token->setRefreshToken($data['refresh_token']);
+ unset($data['refresh_token']);
+ }
+ unset($data['access_token']);
+ unset($data['expires_in']);
+ $token->setExtraParams($data);
+ return $token;
+ }
+
+ /**
+ * Returns the authorization API endpoint.
+ *
+ * @return UriInterface
+ */
+ public function getAuthorizationEndpoint()
+ {
+ return new Uri('https://login.microsoftonline.com/common/oauth2/authorize');
+ }
+
+ /**
+ * Returns the access token API endpoint.
+ *
+ * @return UriInterface
+ */
+ public function getAccessTokenEndpoint()
+ {
+ return new Uri('https://login.microsoftonline.com/common/oauth2/token');
+ }
+
+ /**
+ * @param $code
+ * @param null $state
+ * @return TokenInterface
+ * @throws Exception\InvalidAuthorizationStateException
+ * @throws TokenResponseException
+ */
+ public function requestAccessToken($code, $state = null)
+ {
+ if (null !== $state) {
+ $this->validateAuthorizationState($state);
+ }
+ $bodyParams = array(
+ 'code' => $code,
+ 'client_id' => $this->credentials->getConsumerId(),
+ 'client_secret' => $this->credentials->getConsumerSecret(),
+ 'redirect_uri' => $this->credentials->getCallbackUrl(),
+ 'grant_type' => 'authorization_code',
+ 'resource' => $this->resources['graph']
+ );
+ $responseBody = $this->httpClient->retrieveResponse(
+ $this->getAccessTokenEndpoint(),
+ $bodyParams,
+ $this->getExtraOAuthHeaders()
+ );
+ $token = $this->parseAccessTokenResponse($responseBody);
+ $this->storage->storeAccessToken($this->service(), $token);
+ return $token;
+ }
+
+ /**
+ * Devuelve los heades que tendra obtener el AccessToken
+ * @return array
+ */
+ protected function getExtraOAuthHeaders()
+ {
+ return array(
+ 'Content-Type'=>'application/x-www-form-urlencoded'
+ );
+ }
+
+ /**
+ * Returns a class constant from ServiceInterface define el metodo e autorizacion del API
+ * Header is the sane default.
+ *
+ * @return int
+ */
+ protected function getAuthorizationMethod()
+ {
+ return static::AUTHORIZATION_METHOD_HEADER_BEARER;
+ }
+}
\ No newline at end of file
diff --git a/tests/Unit/OAuth2/Service/MicrosoftGraphTest.php b/tests/Unit/OAuth2/Service/MicrosoftGraphTest.php
new file mode 100644
index 00000000..3c7c8100
--- /dev/null
+++ b/tests/Unit/OAuth2/Service/MicrosoftGraphTest.php
@@ -0,0 +1,92 @@
+
+ */
+class MicrosoftGraphTest extends \PHPUnit_Framework_TestCase
+{
+
+ /**
+ * Test para correcta construccion con interfaces
+ */
+ public function testConstructCorrectInterfaceWithoutCustomUri()
+ {
+ $service = new MicrosoftGraph(
+ $this->getMock('\\OAuth\\Common\\Consumer\\CredentialsInterface'),
+ $this->getMock('\\OAuth\\Common\\Http\\Client\\ClientInterface'),
+ $this->getMock('\\OAuth\\Common\\Storage\\TokenStorageInterface')
+ );
+
+ $this->assertInstanceOf('\\OAuth\\OAuth2\\Service\\ServiceInterface', $service);
+ }
+
+ /**
+ * Test para correcta construccion de la instancia como servicio abstracto
+ */
+ public function testConstructCorrectInstanceWithoutCustomUri()
+ {
+ $service = new MicrosoftGraph(
+ $this->getMock('\\OAuth\\Common\\Consumer\\CredentialsInterface'),
+ $this->getMock('\\OAuth\\Common\\Http\\Client\\ClientInterface'),
+ $this->getMock('\\OAuth\\Common\\Storage\\TokenStorageInterface')
+ );
+
+ $this->assertInstanceOf('\\OAuth\\OAuth2\\Service\\AbstractService', $service);
+ }
+
+ /**
+ * Test para correcta construccion de la instancia con scopes y uriApi interface
+ */
+ public function testConstructCorrectInstanceWithCustomUri()
+ {
+ $service = new MicrosoftGraph(
+ $this->getMock('\\OAuth\\Common\\Consumer\\CredentialsInterface'),
+ $this->getMock('\\OAuth\\Common\\Http\\Client\\ClientInterface'),
+ $this->getMock('\\OAuth\\Common\\Storage\\TokenStorageInterface'),
+ array(),
+ $this->getMock('\\OAuth\\Common\\Http\\Uri\\UriInterface')
+ );
+
+ $this->assertInstanceOf('\\OAuth\\OAuth2\\Service\\AbstractService', $service);
+ }
+
+ /**
+ * Test de obtenecion de url de autorizacion
+ */
+ public function testGetAuthorizationEndpoint()
+ {
+ $service = new MicrosoftGraph(
+ $this->getMock('\\OAuth\\Common\\Consumer\\CredentialsInterface'),
+ $this->getMock('\\OAuth\\Common\\Http\\Client\\ClientInterface'),
+ $this->getMock('\\OAuth\\Common\\Storage\\TokenStorageInterface')
+ );
+
+ $this->assertSame('https://login.microsoftonline.com/common/oauth2/authorize', $service->getAuthorizationEndpoint()->getAbsoluteUri());
+ }
+
+ /**
+ * Test para obtencion de url de endpoint del token
+ */
+ public function testGetAccessTokenEndpoint()
+ {
+ $service = new MicrosoftGraph(
+ $this->getMock('\\OAuth\\Common\\Consumer\\CredentialsInterface'),
+ $this->getMock('\\OAuth\\Common\\Http\\Client\\ClientInterface'),
+ $this->getMock('\\OAuth\\Common\\Storage\\TokenStorageInterface')
+ );
+
+ $this->assertSame('https://login.microsoftonline.com/common/oauth2/token', $service->getAccessTokenEndpoint()->getAbsoluteUri());
+ }
+
+
+}
\ No newline at end of file