pgrimaud / instagram-user-feed

This is a scrapper to easily fetch any feed and interact with Instagram (like, follow, etc.) without OAuth for PHP.
MIT License
880 stars 137 forks source link

The new loginWithCookies doesnt seem to work #319

Open m33ts4k0z opened 2 years ago

m33ts4k0z commented 2 years ago

Version(s) affected: 6.16.0

I implemented the following code:

$cachePool = new FilesystemAdapter('Instagram', 0, config('cache.stores.file.path'));       
$api = new Api($cachePool);         
$api->login(env('INSTAGRAM_USERNAME'), env('INSTAGRAM_PASSWORD'));
$sessionData = $cachePool->getItem(Session::SESSION_KEY . '.' . CacheHelper::sanitizeUsername(env('INSTAGRAM_USERNAME')));
$cookies = $sessionData->get();
$api = new Api();    
// Optionals for set user agent and language 
$api->setUserAgent('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.57 Safari/537.36');            
$api->loginWithCookies($cookies);
$profile = $api->getProfile(env('INSTAGRAM_PROFILE'));

However the loginWithCookies method says:

Please login with instagram credentials. #0 E:\source\test\testite\vendor\pgrimaud\instagram-user-feed\src\Instagram\Api.php(135):

But if I comment out the loginWithCookies method, then I get the profile without issues.

nsmle commented 2 years ago

It should be noted that this login method with cookies is optional when you don't want to use the login method with your instagram credentials then you can use the alternative which is loginWithCookies.

the login method with cookies actually only requires an Instagram sessionid cookie to get another cookie, you can get an Instagram session id cookie by following the steps here or here

sample code if you want to use login method with sessionid cookie

$instagram = new \Instagram\Api();

$cookie = new \GuzzleHttp\Cookie\SetCookie([
    "Name"     => "sessionid",
    "Value"    => "YOUR_INSTAGRAM_SESSIONID",
    "Domain"   => ".instagram.com",
    "Path"     => "/",
    "Expires"  => YOUR_INSTAGRAM_SESSIONID_EXPIRES,
    "Max-Age"  => "31536000",
    "Secure"   => true,
    "Discard"  => false,
    "HttpOnly" => true,
]);
// Generate CookieJar from instagram cookie 'sessionid'
$cookieJar = new \GuzzleHttp\Cookie\CookieJar(false, [$cookie]);

// Login with cookies
$instagram->loginWithCookies($cookieJar);
$profile = $instagram->getProfile('robertdowneyjr');
m33ts4k0z commented 2 years ago

It should be noted that this login method with cookies is optional when you don't want to use the login method with your instagram credentials then you can use the alternative which is loginWithCookies.

the login method with cookies actually only requires an Instagram sessionid cookie to get another cookie, you can get an Instagram session id cookie by following the steps here or here

sample code if you want to use login method with sessionid cookie

$instagram = new \Instagram\Api();

$cookie = new \GuzzleHttp\Cookie\SetCookie([
    "Name"     => "sessionid",
    "Value"    => "YOUR_INSTAGRAM_SESSIONID",
    "Domain"   => ".instagram.com",
    "Path"     => "/",
    "Expires"  => YOUR_INSTAGRAM_SESSIONID_EXPIRES,
    "Max-Age"  => "31536000",
    "Secure"   => true,
    "Discard"  => false,
    "HttpOnly" => true,
]);
// Generate CookieJar from instagram cookie 'sessionid'
$cookieJar = new \GuzzleHttp\Cookie\CookieJar(false, [$cookie]);

// Login with cookies
$instagram->loginWithCookies($cookieJar);
$profile = $instagram->getProfile('robertdowneyjr');

Thanks for the clarification. It works fine now but the change in the Login class, that is in your PR, is needed otherwise the same error occurs:

Please login with instagram credentials.

This is my complete code for Laravel / Statamic 2. This will first check for a valid cookie. If none is present, it will login normally, and next time it will use that cookie for login:

$cachePool = new FilesystemAdapter('Instagram', 0, config('cache.stores.file.path'));
$api = new Api($cachePool); 
if($cachePool->getItem(Session::SESSION_KEY . '.' . CacheHelper::sanitizeUsername(env('INSTAGRAM_USERNAME')))
->get() !== null)
{
   $sessionId = $cachePool->getItem(Session::SESSION_KEY . '.' . CacheHelper::sanitizeUsername(env('INSTAGRAM_USERNAME')))
                ->get()
                ->getCookieByName('sessionId'); 
   $cookieJar = new CookieJar(false, [$sessionId]);           
   $api->loginWithCookies($cookieJar);
} 
else
    $api->login(env('INSTAGRAM_USERNAME'), env('INSTAGRAM_PASSWORD')); 
$profile = $api->getProfile(env('INSTAGRAM_PROFILE'));
nsmle commented 2 years ago

Can you debug your code by adding the following command and attaching it here?

$sessionId = $cachePool->getItem(Session::SESSION_KEY . '.' . CacheHelper::sanitizeUsername(env('INSTAGRAM_USERNAME')))
                ->get()
                ->getCookieByName('sessionId'); 
$cookieJar = new CookieJar(false, [$sessionId]);           
$api->loginWithCookies($cookieJar);

// Get cache response after loginWithCoosi
$cacheResponse = \Instagram\Utils\CacheResponse::getResponse();

preg_match('/<script type="text\/javascript">window\._sharedData\s?=(.+);<\/script>/', (string) $cacheResponse->getBody(), $matches);

print_r($matches[1]);

Supposedly if your sessionid cookie is valid then the code above will return json like

{
    "config": {
        "csrf_token": "xxxxxxxxxxxxx",
        "viewer": {
            "biography": "",
            "business_address_json": null,
            "business_contact_method": "UNKNOWN",
            "business_email": null,
            "business_phone_number": null,
            "can_see_organic_insights": true,
            "category_name": "xxxxxxxxxxxxx",
            "external_url": "xxxxxxxxxxxxx",
            "fbid": "xxxxxxxxxxxxx",
            "full_name": "xxxxxxxxxxxxx",
            "has_phone_number": false,
            "has_profile_pic": true,
            "has_tabbed_inbox": true,
            "hide_like_and_view_counts": false,
            "id": "xxxxxxxxxxxxx",
            "is_business_account": false,
            "is_joined_recently": false,
            "is_supervised_user": false,
            "guardian_id": null,
            "is_private": false,
            "is_professional_account": true,
            "is_supervision_enabled": false,
            "profile_pic_url": "xxxxxxxxxxxxx",
            "profile_pic_url_hd": "xxxxxxxxxxxxx",
            "should_show_category": true,
            "should_show_public_contacts": true,
            "username": "xxxxxxxxxxxxx",
            "badge_count": "xxxxxxxxxxxxx"
        },
        "viewerId": "xxxxxxxxxxxxx"
    },
    "country_code": "EN",
    "language_code": "en",
    "locale": "en_US",
    "entry_data": "{ ... }",
    "hostname": "www.instagram.com",
    "is_whitelisted_crawl_bot": false,
    "connection_quality_rating": "EXCELLENT",
    "deployment_stage": "c2",
    "platform": "web",
    "nonce": "xxxxxxxxxxxxx",
    "mid_pct": 7.54441,
    "zero_data": {},
    "cache_schema_version": 3,
    "server_checks": {
        "hfe": true
    },
    "knobx": "{ ... }",
    "to_cache": "{ ... }",
    "device_id": "xxxxxxxxxxxxx",
    "browser_push_pub_key": "xxxxxxxxxxxxx",
    "encryption": "{ ... }",
    "is_dev": false,
    "is_on_vpn": false,
    "signal_collection_config": "{ ... }",
    "consent_dialog_config": "{ ... }",
    "privacy_flow_trigger": "{ ... }",
    "www_routing_config": "{ ... }",
    "rollout_hash": "xxxxxxxxxxxxx",
    "bundle_variant": "es6",
    "frontend_env": "prod"
}

This is what the loginWithCookies method does, checks whether the viewer and viewerId properties in the config are empty or not and throws an error Please login with instagram credentials if empty.

m33ts4k0z commented 2 years ago

@nsmle Sorry maybe I wasnt clear but the code I posted works fine. The code you posted works too but it requires that a cookie file is present on the server, so thats why I used the code I posted. It will create the cookie by logging in normally first if the cookie variable is null.

nsmle commented 2 years ago

@m33ts4k0z It's just a matter of how you validate the cookies on your server before using the loginWithCookies method.

Previously I have warned to use login method first in the sample file before using loginWithCookies method. Because the sessionid cookie will only be available when the user is authenticated.

This loginWithCookies method is optional. you can use it when you want and need to implement it. this method is made as an alternative if login always fails, checkpoint, ip is blocked, you don't want to use a proxy, and others. or if you prefer, you can also override existing methods in the \Instagram\Api by creating a new class that extends to the Api class. See how i implemented this on my laravel application, i know that its not best practice but it can customize as per requirement i want.

And don't forget to close your issue when your problem is solved.

m33ts4k0z commented 2 years ago

@nsmle The reason that Im trying to use the cookie method is to avoid checkpoints. At the moment I receive about 10 emails per day from Instagram that inform me of unusual login activity. There are no links to click in the emails but instead I need to go to instagram.com and check the login activity in order to confirm that it was me.

Funny thing that it always shows the same location that I need to confirm over and over again. I know its a bit offtopic but any idea why this is happening?