Skip to content

Commit

Permalink
Merge pull request #12 from BDSU/instagram-basic-display-api
Browse files Browse the repository at this point in the history
Update to Instagram Basic Display API
  • Loading branch information
stevenmaguire committed Feb 25, 2020
2 parents 134cb1a + f3db8d5 commit ba5a5cc
Show file tree
Hide file tree
Showing 6 changed files with 103 additions and 65 deletions.
24 changes: 24 additions & 0 deletions CHANGELOG.md
@@ -1,6 +1,30 @@
# Changelog
All Notable changes to `oauth2-instagram` will be documented in this file

## 3.0.0 - 2020-02-25

### Added
- Support for Instagram Basic Display API
- get Resource Owner Details from https://graph.instagram.com/me
- changed default scopes to `['user_profile']`
- Custom host configuration for Graph API host

### Deprecated
- Nothing

### Fixed
- Nothing

### Removed
- Support for Instagram Legacy API (https://api.instagram.com/v1/...)
- Short-hand functions for now removed attributes
- `InstagramResourceOwner::getImageUrl()`
- `InstagramResourceOwner::getName()`
- `InstagramResourceOwner::getDescription()`

### Security
- Nothing

## 2.0.0 - 2017-01-25

### Added
Expand Down
15 changes: 7 additions & 8 deletions README.md
Expand Up @@ -27,7 +27,8 @@ $provider = new League\OAuth2\Client\Provider\Instagram([
'clientId' => '{instagram-client-id}',
'clientSecret' => '{instagram-client-secret}',
'redirectUri' => 'https://example.com/callback-url',
'host' => 'https://api.instagram.com' // Optional, defaults to https://api.instagram.com
'host' => 'https://api.instagram.com', // Optional, defaults to https://api.instagram.com
'graphHost' => 'https://graph.instagram.com' // Optional, defaults to https://graph.instagram.com
]);

if (!isset($_GET['code'])) {
Expand Down Expand Up @@ -58,7 +59,7 @@ if (!isset($_GET['code'])) {
$user = $provider->getResourceOwner($token);

// Use these details to create a new profile
printf('Hello %s!', $user->getName());
printf('Hello %s!', $user->getNickname());

} catch (Exception $e) {

Expand All @@ -78,19 +79,17 @@ When creating your Instagram authorization URL, you can specify the state and sc
```php
$options = [
'state' => 'OPTIONAL_CUSTOM_CONFIGURED_STATE',
'scope' => ['basic','likes','comments'] // array or string
'scope' => ['user_profile', 'user_media'] // array or string
];

$authorizationUrl = $provider->getAuthorizationUrl($options);
```
If neither are defined, the provider will utilize internal defaults.

At the time of authoring this documentation, the [following scopes are available](https://instagram.com/developer/authentication/#scope).
At the time of authoring this documentation, the [following scopes are available](https://developers.facebook.com/docs/instagram-basic-display-api/overview#permissions).

- basic
- comments
- relationships
- likes
- user_profile
- user_media

## Testing

Expand Down
8 changes: 4 additions & 4 deletions src/Provider/Exception/InstagramIdentityProviderException.php
Expand Up @@ -20,11 +20,11 @@ public static function clientException(ResponseInterface $response, $data)
$code = $response->getStatusCode();
$body = (string) $response->getBody();

if (isset($data['meta'], $data['meta']['error_message'])) {
$message = $data['meta']['error_message'];
if (isset($data['error'], $data['error']['message'])) {
$message = $data['error']['message'];
}
if (isset($data['meta'], $data['meta']['code'])) {
$code = $data['meta']['code'];
if (isset($data['error'], $data['error']['code'])) {
$code = $data['error']['code'];
}

return new static($message, $code, $body);
Expand Down
40 changes: 35 additions & 5 deletions src/Provider/Instagram.php
Expand Up @@ -11,14 +11,14 @@ class Instagram extends AbstractProvider
/**
* @var string Key used in a token response to identify the resource owner.
*/
const ACCESS_TOKEN_RESOURCE_OWNER_ID = 'user.id';
const ACCESS_TOKEN_RESOURCE_OWNER_ID = 'user_id';

/**
* Default scopes
*
* @var array
*/
public $defaultScopes = ['basic'];
public $defaultScopes = ['user_profile'];

/**
* Default host
Expand All @@ -27,6 +27,13 @@ class Instagram extends AbstractProvider
*/
protected $host = 'https://api.instagram.com';

/**
* Default Graph API host
*
* @var string
*/
protected $graphHost = 'https://graph.instagram.com';

/**
* Gets host.
*
Expand All @@ -37,6 +44,16 @@ public function getHost()
return $this->host;
}

/**
* Gets Graph API host.
*
* @return string
*/
public function getGraphHost()
{
return $this->graphHost;
}

/**
* Get the string used to separate scopes.
*
Expand Down Expand Up @@ -78,7 +95,7 @@ public function getBaseAccessTokenUrl(array $params)
*/
public function getResourceOwnerDetailsUrl(AccessToken $token)
{
return $this->host.'/v1/users/self?access_token='.$token;
return $this->graphHost.'/me?fields=id,username&access_token='.$token;
}

/**
Expand Down Expand Up @@ -127,7 +144,6 @@ protected function getDefaultScopes()
/**
* Check a provider response for errors.
*
* @link https://instagram.com/developer/endpoints/
* @throws IdentityProviderException
* @param ResponseInterface $response
* @param string $data Parsed response data
Expand All @@ -136,7 +152,7 @@ protected function getDefaultScopes()
protected function checkResponse(ResponseInterface $response, $data)
{
// Standard error response format
if (!empty($data['meta']['error_type'])) {
if (!empty($data['error'])) {
throw InstagramIdentityProviderException::clientException($response, $data);
}

Expand Down Expand Up @@ -171,4 +187,18 @@ public function setHost($host)

return $this;
}

/**
* Sets Graph API host.
*
* @param string $host
*
* @return string
*/
public function setGraphHost($host)
{
$this->graphHost = $host;

return $this;
}
}
36 changes: 3 additions & 33 deletions src/Provider/InstagramResourceOwner.php
Expand Up @@ -26,27 +26,7 @@ public function __construct(array $response = array())
*/
public function getId()
{
return $this->response['data']['id'] ?: null;
}

/**
* Get user imageurl
*
* @return string|null
*/
public function getImageurl()
{
return $this->response['data']['profile_picture'] ?: null;
}

/**
* Get user name
*
* @return string|null
*/
public function getName()
{
return $this->response['data']['full_name'] ?: null;
return $this->response['id'] ?: null;
}

/**
Expand All @@ -56,17 +36,7 @@ public function getName()
*/
public function getNickname()
{
return $this->response['data']['username'] ?: null;
}

/**
* Get user description
*
* @return string|null
*/
public function getDescription()
{
return $this->response['data']['bio'] ?: null;
return $this->response['username'] ?: null;
}

/**
Expand All @@ -76,6 +46,6 @@ public function getDescription()
*/
public function toArray()
{
return $this->response['data'];
return $this->response;
}
}
45 changes: 30 additions & 15 deletions tests/src/Provider/InstagramTest.php
Expand Up @@ -56,6 +56,29 @@ public function testSetHostAfterConfig()
$this->assertEquals($host, $this->provider->getHost());
}

public function testSetGraphHostInConfig()
{
$host = uniqid();

$provider = new \League\OAuth2\Client\Provider\Instagram([
'clientId' => 'mock_client_id',
'clientSecret' => 'mock_secret',
'redirectUri' => 'none',
'graphHost' => $host
]);

$this->assertEquals($host, $provider->getGraphHost());
}

public function testSetGraphHostAfterConfig()
{
$host = uniqid();

$this->provider->setGraphHost($host);

$this->assertEquals($host, $this->provider->getGraphHost());
}

public function testScopes()
{
$scopeSeparator = ' ';
Expand Down Expand Up @@ -87,7 +110,7 @@ public function testGetBaseAccessTokenUrl()
public function testGetAccessToken()
{
$response = m::mock('Psr\Http\Message\ResponseInterface');
$response->shouldReceive('getBody')->andReturn('{"access_token":"mock_access_token","user": {"id": "123","username": "snoopdogg","full_name": "Snoop Dogg","profile_picture": "..."}}');
$response->shouldReceive('getBody')->andReturn('{"access_token":"mock_access_token","user_id": "123"}');
$response->shouldReceive('getHeader')->andReturn(['content-type' => 'json']);

$client = m::mock('GuzzleHttp\ClientInterface');
Expand All @@ -105,17 +128,14 @@ public function testGetAccessToken()
public function testUserData()
{
$userId = rand(1000,9999);
$name = uniqid();
$nickname = uniqid();
$picture = uniqid();
$description = uniqid();

$postResponse = m::mock('Psr\Http\Message\ResponseInterface');
$postResponse->shouldReceive('getBody')->andReturn('{"access_token": "mock_access_token","user": {"id": "1574083","username": "snoopdogg","full_name": "Snoop Dogg","profile_picture": "..."}}');
$postResponse->shouldReceive('getBody')->andReturn('{"access_token": "mock_access_token","user_id": "1574083"}');
$postResponse->shouldReceive('getHeader')->andReturn(['content-type' => 'json']);

$userResponse = m::mock('Psr\Http\Message\ResponseInterface');
$userResponse->shouldReceive('getBody')->andReturn('{"data": {"id": "'.$userId.'", "username": "'.$nickname.'", "full_name": "'.$name.'", "bio": "'.$description.'", "profile_picture": "'.$picture.'"}}');
$userResponse->shouldReceive('getBody')->andReturn('{"id": "'.$userId.'", "username": "'.$nickname.'"}');
$userResponse->shouldReceive('getHeader')->andReturn(['content-type' => 'json']);

$client = m::mock('GuzzleHttp\ClientInterface');
Expand All @@ -129,23 +149,18 @@ public function testUserData()

$this->assertEquals($userId, $user->getId());
$this->assertEquals($userId, $user->toArray()['id']);
$this->assertEquals($name, $user->getName());
$this->assertEquals($name, $user->toArray()['full_name']);
$this->assertEquals($nickname, $user->getNickname());
$this->assertEquals($nickname, $user->toArray()['username']);
$this->assertEquals($picture, $user->getImageurl());
$this->assertEquals($picture, $user->toArray()['profile_picture']);
$this->assertEquals($description, $user->getDescription());
$this->assertEquals($description, $user->toArray()['bio']);
}

public function testExceptionThrownWhenErrorObjectReceived()
{
$this->expectException('League\OAuth2\Client\Provider\Exception\IdentityProviderException');
$message = uniqid();
$status = rand(400,600);
$traceId = uniqid();
$postResponse = m::mock('Psr\Http\Message\ResponseInterface');
$postResponse->shouldReceive('getBody')->andReturn('{"meta": {"error_type": "OAuthException","code": '.$status.',"error_message": "'.$message.'"}}');
$postResponse->shouldReceive('getBody')->andReturn('{"error": {"type": "IGApiException","code": '.$status.',"message": "'.$message.'","fbtrace_id":"'.$traceId.'"}}');
$postResponse->shouldReceive('getHeader')->andReturn(['content-type' => 'json']);
$postResponse->shouldReceive('getReasonPhrase');
$postResponse->shouldReceive('getStatusCode')->andReturn($status);
Expand Down Expand Up @@ -180,10 +195,10 @@ public function testExceptionThrownWhenAuthErrorObjectReceived()
public function testGetAuthenticatedRequest()
{
$method = 'GET';
$url = 'https://api.instagram.com/v1/users/self/feed';
$url = 'https://graph.instagram.com/me';

$accessTokenResponse = m::mock('Psr\Http\Message\ResponseInterface');
$accessTokenResponse->shouldReceive('getBody')->andReturn('{"access_token": "mock_access_token","user": {"id": "1574083","username": "snoopdogg","full_name": "Snoop Dogg","profile_picture": "..."}}');
$accessTokenResponse->shouldReceive('getBody')->andReturn('{"access_token": "mock_access_token","user_id": "1574083"}');
$accessTokenResponse->shouldReceive('getHeader')->andReturn(['content-type' => 'json']);

$client = m::mock('GuzzleHttp\ClientInterface');
Expand Down

0 comments on commit ba5a5cc

Please sign in to comment.