gapi login state not retained in users browser after page refresh - javascript

I've dove into a small project aiming to utilize the YouTube API. I've got some basic code in place, that I initially thought was, working properly.
Using Chrome, I can login through multiple machines on my own network without any issues using the source below. I've also tethered my machines to my cellular network to ensure that the process also works properly outside of my own network.
Unfortunately, I'm having issues finding out why a colleague (from a remote location) is unable to retain his login state between page refreshes after successfully logging in through Chrome, although no issues are apparent when using Firefox.
For an example of a successful scenario, when loading the page for the first time, the callback assigned to authInstance.isSignedIn.listen is called with a value of true if the user has already logged in.
On my colleagues machine, the authInstance.isSignedIn.listen listener is not called after refreshing the page after successfully logging in, indicating no login.
I'm currently not using an SSL certificate, if that information helps any.
Additional information includes my colleagues ability to authenticate and retain login state without any issues on other sites that support YouTube authentication. I've been unable to look through the source of those sites, as the source is obfuscated.
Here's my own source:
<html lang="en">
<head>
<script src="https://apis.google.com/js/platform.js?onload=onLoad" async defer></script>
</head>
<body>
<button id="sign-in">Sign In</button>
<button id="sign-out">Sign Out</button>
<script>
var authInstance;
var signInBtn = document.getElementById('sign-in');
var signOutBtn = document.getElementById('sign-out');
function onLoad() {
gapi.load('auth2', function() {
console.log('loadAuth2()');
auth2 = gapi.auth2.init({
client_id: '[CLIENT_ID]',
});
authInstance = gapi.auth2.getAuthInstance();
authInstance.isSignedIn.listen(function () {
console.log('authUpdate()');
console.log(arguments);
});
});
}
signInBtn.onclick = function () {
console.log('signInBtn onclick()');
/*
Signing in this way produces the same results.
auth2.grantOfflineAccess({
scope: "https://www.googleapis.com/auth/youtube.readonly",
redirect_uri: "postmessage"
}).then(function () {
console.log("accessGranted() (?)");
console.log(arguments);
});
*/
auth2.signIn({
scope: "https://www.googleapis.com/auth/youtube",
}).then(function () {
console.log('signedIn()');
console.log(arguments);
});
};
signOutBtn.onclick = function () {
console.log('signOutBtn onclick()');
authInstance.signOut();
};
</script>
</body>
</html>

I'm not an expert in Google Sign-In, in my experience, however, the following code did not work.
authInstance = gapi.auth2.getAuthInstance();
console.log(authInstance.isSignedIn.get());
Instead, you should do like:
authInstance = gapi.auth2.getAuthInstance();
authInstance.then(function() { // onInit
console.log(authInstance.isSignedIn.get());
}, function() { // onError
});
You should wait until the GoogleAuth object is fully initialized.

This is how I got it to work with a custom button which you attach the event to. Yes its very important to await some of these functions as mentioned by other answers, as perhaps not everything has loaded untill they have been awaited
async function initializeGoogleStuff() {
window.gapi.load('auth2', async () => {
window.auth2 = await window.gapi.auth2.init({
prompt: "select_account",
})
var authInstance = await window.gapi.auth2.getAuthInstance();
var signedIn = authInstance.isSignedIn.get()
if (signedIn) {
// handleLogin is my own function for calling our backend server
// and doing other login related stuff
await handleLogin(authInstance.currentUser.get())
}
// I am using a custom button with attachClickHandler()
// https://developers.google.com/identity/sign-in/web/reference#googleauthattachclickhandlercontainer_options_onsuccess_onfailure
let signInElement = window.document.getElementById("signInButton")
if (signInElement) {
attachElementToCallback(signInElement, handleLogin)
}
})
}

Related

How to deep copy a page of puppeteer in javascript?

I'm using puppeteer to navigate my website. I want to wait for an api that sometimes gets called and sometimes not. I'm using
await page.waitForResponse((response =>response.url().includes(myurl)), { timeout: 1000 });
to wait for that api. This works fine when the api gets called, but whenever the api doesn't get called, it crashes and the page isn't same anymore. So, I want to deep copy the page so that I can just check for the api via it's copy and even if that page gets damaged. I will have another that I can use.
I think you don't need to copy your page. That's probably not doable very easy and seems like a bit of overkill. Instead, preventing the page from crashing would be a simpler approach.
Try something like this:
async function waitForApi(url, timeoutMs) {
try {
console.log('waiting ', timeoutMs+'ms for special API. url:', url);
const opts = { timeout: timeoutMs || 1000 };
await page.waitForResponse(response => response.url().includes(url), opts);
console.log('Special API was called!.');
return true;
} catch(err) {
console.log('Special Api was appearantly not called. (Or may be failed.. Error:', err);
return false;
}
}
// example call of waitForApi ..
const myUrl = '...'
const apiCalled = await waitForApi(myUrl, 1000)
if(apiCalled) {
// do stuff if you want to..
} else {
// do stuff if you want to..
}
This should now log if the api was called or not and when needed you can handle the cases differently.

How to signOut from an app using gapi without needing to click on a button

I'm developping a web app for an association, and for many reasons we wanted to used the Auth2 api of Google.
So, here's how our loggin process works:
The user clicks on the google sign-in button
The token is sent to a page to check if it's valid, and if the user already exists in the DB
If the token is valid, then autorise the user
If the user doesn't exist, then logout the user from the Google App
Here's a simplified version of my token verification page, assuming that the user doesn't exist (i skipped the php to check that, and just inserted what the php would echo):
<script>
function signOut() {
var auth2 = gapi.auth2.getAuthInstance();
auth2.signOut().then(function () {
console.log('User signed out.');
});
}
function loadGapi() {
gapi.load('auth2', function() {
gapi.auth2.init();
});
}
</script>
Sign out
<script src="https://apis.google.com/js/platform.js"></script>
<script>
loadGapi();
signOut();
</script>
When the page loads, I get this error message:
Uncaught TypeError: Cannot read property 'getAuthInstance' of undefined
at signOut // On line 3
note that the logoutButton is just for test purposes, and works perfectly fine when clicked, but I really can't figure out how to disconnect the user without needing him to interact with anything.
(Thanks for taking the time to read this, i've been stuck on this for like 3 hours ^^)
Found the answer in case anyone have the same issue, just define a function to execute at the end of loading as an attribute in body
<body onload="functionToCall()">
</body>

How do you return a javascript object? it keeps throwing a "Converting circular structure to JSON"

I am working on an Ionic app, and I need to use firebase's phone authentication, to use that, I have to use google's recaptcha, which doesn't work in Ionic apps, so...
I made a web page that held the code for rendering the reCaptcha, and used cordova's inAppBrowser to navigate to it from inside my app.
after hours, I got everything working, and finally got the "recaptchaVerifier" object, that you need to pass to firebase.auth().signInWithPhoneNumber(phoneNumber,recaptchaVerifier)
.
I just need to pass the "recaptchaVerifier" object from the inAppBrowser, to my ionic app, I tried everything, and got stuck on a simple javascript function that would "return recaptchaVerifier" and I would grab it in Ionic, but it kept giving me Uncaught TypeError: Converting circular structure to JSON
What do I do? I just need to pass that object so I can send the verification sms from my app and continue registration/signing in.
the code for the static site that renders the recaptcha from inside the inAppBrowser:
<script>
var recaptchaVerifier;
function lol() {
alert("done");
};
$(document).ready(function() {
recaptchaVerifier = new firebase.auth.RecaptchaVerifier('gre', {
'size': 'invisible',
'callback': lol
});
recaptchaVerifier.render();
});
function getIt() {
console.log(recaptchaVerifier);
return recaptchaVerifier;
};
</script>
The code for opening the inAppBrowser and grabbing the recaptchaVerifier Object:
var myInterval;
var myValue : firebase.auth.ApplicationVerifier ;
const browser = this.inAppBrowser.create("https://demo.firebaseapp.com","_blank",
{"location":"no",clearcache:"yes",clearsessioncache:"yes"});
browser.on("loadstop").subscribe(x=>{
myInterval = setInterval(function(){
browser.executeScript({code:"getIt()"}).then(data=>{myValue = JSON.parse(data)});
},200);
});
browser.on("exit").subscribe(x=>{
clearInterval(myInterval);
this.presentAlert("Test Alert",myValue,"OK");
console.log("Adnan12 is error "+ myValue);
firebase.auth().signInWithPhoneNumber("+123456789",myValue)
.then(function (confirmationResult) {
this.presentAlert("alert title","sms sent","ok");
}).catch(function (error) {
this.presentAlert("alert title","sms NOT sent","ok");
});
});

Facebook Login not working on phone Angular Satellizer

Problem
I'm working on angular and have implemented facebook login through Satellizer. It works well on desktop, but when accessed on mobile, it sometimes work, and sometimes don't. The login pop-up is never closed when it is not working, and it opens the home page in login pop-up, while the main screen which requested login is still waiting for the pop-up to close. Any suggestion what might be wrong here?
Sample Code
This is how I'm authenticating from facebook
var commonConfig = {
popupOptions: {
location: 'no',
toolbar: 'yes',
width: window.screen.width,
height: window.screen.height
}
};
var permissions = ["public_profile","email"];
$authProvider.facebook(angular.extend({}, commonConfig, {
clientId: FacebookAppId,
url: ApiUrl+'api/v1/facebook/auth',
scope:permissions,
scopeDelimiter:','
}));
This code is called when "connect using facebook" is pressed
$scope.loginSocial = function () {
$scope.disableSubmitBtn = true;
$scope.showSpinner = true;
$auth.authenticate('facebook')
.then(function (result) {
result = angular.fromJson(result.data.code)
return AuthServerProvider.loginFb(result.facebookCode);
},function (error) {
$scope.status = 'facebook-error';
$scope.disableSubmitBtn = false;
$scope.showSpinner = false;
})
.then(function(){
$scope.disableSubmitBtn = true;
$scope.showSpinner = true;
})
This code works perfectly on desktop web browsers, but when accessed through phone browsers "$auth.authenticate('facebook')" fails most of the time.
We were facing similar problem with implementing Satellizer. It turned out that the problem was with handling the redirectUri within angular ui.router.
if you go back to the library documentation you will notice this qoute:
To avoid potential 404 errors, create server routes for each redirectUri > URL that return 200 OK. Or alternatively, you may render a custom template with a loading spinner. For the moment, a popup will not stay long > enough to see that custom template, due to 20ms interval polling, but in > the future I may add support for overriding this polling interval value.
In the above code you have mentioned, you didn't assign the redirectUri, so it will be defaulted to '/'. If you already have this route handled in a different way it might sometimes get caught by angular routes and being redirected before Satellizer can fetch it. In order to solve this just assign a redirectUri and handle it through angular routes.
For further details please read this post.

Google api auth2 signOut not working

First off, I am following this guide https://developers.google.com/identity/sign-in/web/ and this reference https://developers.google.com/identity/sign-in/web/reference.
But instead of having a callback declared in window, I used gapi.signin2.render function to render the button and attach a handler to it in my Angular controller.
Logging in works just fine, problem is, when I try to log out by calling gapi.auth2.getAuthInstance().signOut(), it just doesn't do it.
I noticed that sessionStorage for accounts.google.com is still there, and because of that Google automatically signs me back in when I render the button again on the login screen.
I tried seeing what happens after signOut() is complete:
gapi.auth2.getAuthInstance().signOut().then(function() {
console.log(gapi.auth2.getAuthInstance().isSignedIn.get());
// prints 'true'
});
I also tried calling disconnect() instead of signOut(), knowing that it will revoke access, and it does remove token from the sessionStorage, but user session is still there. It is only gone once I reload the page.
Here is my complete code:
$scope.logout = function() {
//...
gapi.auth2.getAuthInstance().signOut().then(function() {
console.log(gapi.auth2.getAuthInstance().isSignedIn);
});
};
$scope.processAuth = function(authResult) {
console.log("success");
if(authResult.getAuthResponse().id_token) {
// backend calls
}
};
$scope.renderSignInButton = function() {
console.log(gapi.auth2);
gapi.signin2.render('signInButton',
{
'onsuccess': $scope.processAuth, // Function handling the callback.
'onfailure': $scope.signinFailed, // Function handling the callback.
'clientid': 'asdasdasd.apps.googleusercontent.com',
'scope': 'https://www.googleapis.com/auth/userinfo.email',
'cookiepolicy': 'single_host_origin'
}
);
};
$scope.renderSignInButton();
I tried as following and it worked. It is required to call auth2.disconnect() when the auth2.signOut() success.
<script type="text/javascript">
function signOut() {
var auth2 = gapi.auth2.getAuthInstance();
auth2.signOut().then(function () {
auth2.disconnect();
});
}
function onLoad() {
gapi.load('auth2', function() {
gapi.auth2.init();
});
}
</script>
<script src="https://apis.google.com/js/platform.js?onload=onLoad" async defer></script>
It can happen that the auth2 object is not loaded.
( which would cause both issues presented in the question ).
This following snippet resolves that particular issue.
if(!gapi.auth2){
gapi.load('auth2', function() {
gapi.auth2.init();
});
}
for me the problem was only in chrome because of the one profile mode.
profile mode means that you only connected to one account, in order to see the list of accounts you need to be connected to multiple accounts, but with the profile mode ON, you only connected to one account, so there is no list to show - there is only that one account.
do a test on firefox whene you connected to at list 2 google accounts, and you will see the list or exist of profile mode in chrome(but i have no idea how to do that, i stack in profile mode in chrome for the moment)

Categories

Resources