2
votes

I was following Instagram API Basic Display documentation. I've created Facebook App, configured Instagram Basic Display, added Test User, Authenticated the Test User using GET request:

https://api.instagram.com/oauth/authorize
  ?app_id={app-id}
  &redirect_uri={redirect-uri}
  &scope=user_profile,user_media
  &response_type=code

But when I try to request access_token using POST request from the documentation: I receive ERROR 400 with message "You must provide a client_id". However, documentation says nothing about client_id and Instagram Basic Display doesn't provide client_id.

enter image description here

What am I doing wrong? Has any of you had similar problem?

2
Why do you have the app_id in the URL and in your POST parameters? (App id and client id should be the same thing, they are just not consistent with the phrasing AFAIK.) - 04FS
@04FS, its just how Postman works, when u enter Query Params, it displays them in URI. But my question was, why "Instagram API Basic Display" want this client_id at all? There is no client_id if u create instagram app in at developers.facebook.com And if u ready their documentation, it clearly says just to send app_id and app_secret. - VitFL
As I said, both app id and client id get used as synonyms by Facebook. - 04FS
You are simply not sending the POST parameters properly here. These should not be entered on the Params tab (that actually creates query string parameters, and that is why they are shown as part of the URL), but on the Body tab instead, with encoding x-www-form-urlencoded - 04FS
Thanks :) You are right! However, now I'm getting ""error_message": "Matching code was not found or was already used"". And I am using freshly generated code. Now i'm trying to figure out why. - VitFL

2 Answers

3
votes

You should make a POST request to https://api.instagram.com/oauth/access_token with the parameters in the body, NOT the Params. Make sure that the "x-www-form-urlencoded" option is enable.

enter image description here

See a more detailed answer here: https://stackoverflow.com/a/60851414/1908112

1
votes

I managed to get mine working by using GuzzleHttp\Client like this.

Step 1. get the Authorization Code $code

Step 2. Get the short-lived AccessToken

Short-Lived Access tokens are valid for just 1 hour.

$aAccessToken = $this->fetchAccessToken( $code );
$short_lived_access_token = $aAccessToken[ 'access_token' ];
$user_id                  = $aAccessToken[ 'user_id' ];

Step 3 (optional)

If you want the Long-Lived token, which is valid for 60days, you can immediately exchange the $short_lived_access_token.

$aLongLivedTokenResult   =           = $this->GetLongLivedToken( $short_lived_access_token );
$long_lived_access_token = $aLongLivedTokenResult[ 'access_token' ];
$expires_in = $aLongLivedTokenResult[ 'expires_in' ];

long_lived_access_token and expires_in can be saved and when the token has expired after 60 days you can refresh it.

Step 4 Now you can fetch the user media like this.

Bear in mind that the long_lived_access_token expires and before you FETCH you should actually check if the token has expired and if it has, exchange it to get a new one. And the token recycling begins.

    $aQueryString = [
        'fields'       => 'id,media_url,permalink,timestamp,caption',
        'access_token' => $long_lived_access_token,

    ];
    $uri = 'https://graph.instagram.com/{$user_id}/media?' . http_build_query( $aQueryString ) );

//functions

Because the fetchAccessToken function uses the POST method, Adding content-type = application/x-www-form-urlencoded on the headers alone didn't really work. form_params on the options did the trick for me.

private function fetchAccessToken(){
    $aOptions = [
      'app_id'       => $this->provider->AppID,
      'app_secret'   => $this->provider->AppSecret,
      'grant_type'   => 'authorization_code',
      'redirect_uri' => $this->provider->getRedirectUri(),
      'code'         => $accessCode,       
    ];

    $client   = new Client( [
        'base_uri' => 'https://api.instagram.com',
        'headers'  => [
            'content-type' => 'application/x-www-form-urlencoded',
        ],
    ] );


    $response = $client->request( 'POST', 'oauth/access_token', [
        'form_params' => $aOptions,
    ] );
    return json_decode( $response->getBody(), true );

}

private function GetLongLivedToken( $access_token )
{

    $aOptions = [
        'grant_type'    => 'ig_exchange_token',
        'client_secret' => $this->provider->AppSecret,
        'access_token'  => $access_token,

    ];

    $response =  new Client( [
        'base_uri' => 'https://graph.instagram.com',
    ] )->request( 'GET', 'access_token?' . http_build_query( $aOptions ) );

    $stream   = $response->getBody();
    $contents = $stream->getContents();
    return json_decode( $contents, true ); 

}