Google-plus sign in: the code runs twice, user was logged out right after he logs in - google-plus

this is a page built on example
<html>
<head>
<title>Demo: Getting an email address using the Google+ Sign-in button</title>
<style type="text/css">
html, body { margin: 0; padding: 0; }
.hide { display: none;}
.show { display: block;}
</style>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js" ></script>
<!--<script src="https://apis.google.com/js/plusone.js" type="text/javascript"></script>-->
<script src="https://apis.google.com/js/client:plusone.js" type="text/javascript"></script>
<script type="text/javascript">
/*
* Triggered when the user accepts the sign in, cancels, or closes the
* authorization dialog.
*/
function loginFinishedCallback(authResult) {
if (authResult) {
console.log('authResult : ',authResult);
if (authResult['error'] == undefined){
gapi.auth.setToken(authResult); // Store the returned token.
toggleElement('signin-button'); // Hide the sign-in button after successfully signing in the user.
getEmail(); // Trigger request to get the email address.
} else {
console.log('An error occurred');
}
} else {
console.log('Empty authResult'); // Something went wrong
}
}
/*
* Initiates the request to the userinfo endpoint to get the user's email
* address. This function relies on the gapi.auth.setToken containing a valid
* OAuth access token.
*
* When the request completes, the getEmailCallback is triggered and passed
* the result of the request.
*/
function getEmail(){
// Load the oauth2 libraries to enable the userinfo methods.
gapi.client.load('oauth2', 'v2', function() {
var request = gapi.client.oauth2.userinfo.get();
request.execute(getEmailCallback);
});
}
function getEmailCallback(obj){
var el = document.getElementById('email');
var email = '';
console.log("OBJ = ",obj)
if (obj['email']) {
email = 'Email: ' + obj['email'];
}
//console.log(obj); // Uncomment to inspect the full object.
el.innerHTML = email;
toggleElement('email');
}
function toggleElement(id) {
var el = document.getElementById(id);
if (el.getAttribute('class') == 'hide') {
el.setAttribute('class', 'show');
} else {
el.setAttribute('class', 'hide');
}
}
</script>
</head>
<body>
<div id="signin-button" class="show">
<div class="g-signin" data-callback="loginFinishedCallback"
data-approvalprompt="auto"
data-clientId="751931329576.apps.googleusercontent.com"
data-scope="https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/userinfo.email"
data-height="short"
data-cookiepolicy="http://semicon-equip.com"
>
</div>
<!-- In most cases, you don't want to use approvalprompt=force. Specified
here to facilitate the demo.-->
</div>
<div id="email" class="hide"></div>
</body>
</html>
Question 1: It always fails with "Uncaught TypeError: Cannot read property 'load' of undefined",
until I use
<script src="https://apis.google.com/js/client:plusone.js" type="text/javascript"></script>
instead of the example code:
<script src="https://apis.google.com/js/plusone.js" type="text/javascript"></script>
What's the difference between plusone.js and client:plusone.js ?
Question 2: Why the code run twice per page loads ?
Qestion 3: the user was logged out after he just signed in, how to fix ?
error demo page for the above (all the errors are in the background console).

This is not really an answer to the question, but a step by step procedure to reproduce it.
Below the simple html page I'm using to test (similar to the example from Ray C Lin).
I've made it as simple as possible to avoid interactions with other part of the code :
<html>
<head>
<script type="text/javascript"
src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
</head>
<body>
<input type="button" id="signOut" value="Sign out"></button>
<span id="signinButton">
<span class="g-signin"
data-accesstype="offline"
data-callback="signinCallback"
data-clientid="YOUR_CLIENT_ID_HERE"
data-cookiepolicy="single_host_origin"
data-scope="email"
</span>
</span>
<script type="text/javascript">
$('#signOut').on('click', function() {
gapi.auth.signOut();
});
function signinCallback(authResult) {
console.log("signinCallback: ", authResult);
}
(function() {
var po = document.createElement('script');
po.type = 'text/javascript';
po.async = true;
po.src = 'https://apis.google.com/js/client:plusone.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(po, s);
})();
</script>
</body>
</html>
You will have to update data-clientid with your own google client id, and display this page from an authorized javascript origin.
Please note that this might not work from localhost, as Ian suggested in a comment to this post https://plus.google.com/102746521318753162868/posts/Z5Gkro9YXVs
First, sign in using your Google account : you will see a successful callback in the console.
If you click on Sign out, you will see a callback with 'user_signed_out' in the console.
So far so good.
Sign-in again, and wait 1 hour, until the token expires (this is awful to test, as I don't know how to reduced the token lifetime).
After one hour, click on the sign out button : no callback is called.
Click on the sign-in button again :
you get a successfull callback with an authorization code and access token
immediately after, you get a 'user_signed_out' callback.
Once a session has expired, there is no way to return to a "normal" situation, you always get this second callback with 'user_signed_out'.
Actually, there is one way to return to a "normal" situation: revoke the access to the app from the google dashboard.
This is not really an issue for me as I'm using Google+ only to sign-in the user to my app using the one time authorization code, and I'm not using the access token from the client.
But this prevent automatic login from working, as the user is immediately considered as "signed out" from a google perspective.

Q1: client:plusone.js is just telling the loader to automatically load the "client" module. It's basically automatically doing gapi.load("client"), except it is already packaged up for you in one download. You could do it the other way around as well, plusone:client.js!
Q2: Not sure, it may be some JS quirk. In general, try and make your code able to handle multiple callbacks, you may get another if the state changes (e.g. the user logs out of their google account).
Q3: I don't see that on the test page - I am signed OK, and still signed in on refresh! Check you're not blocking third party cookies in your browser or similar?

Related

Google SignIn State

I'm trying to build a Google signin button into my website. I'm trying to avoid using their built-in button. The code below works to sign in a user, but I can't figure out how to make my webpage remember that they're signed in when the user refreshes the page, or leaves the site and comes back.
Using Chrome's developer tools, I can see that there's an entry for https://accounts.google.com under both Local Storage and Session Storage. They seem to more or less contain the same information, including the user's validated token.
What I don't understand is how to get the gapi.auth2.init() function to recognize and use this token. The documentation doesn't seem to cover it.
<html>
<head>
<title>Login Test</title>
<script src="http://code.jquery.com/jquery-2.1.4.js"></script>
<script src="https://apis.google.com/js/platform.js?onload=renderButton" async defer></script>
</head>
<script>
var googleUser = {};
function renderButton() {
gapi.load('auth2', function(){
auth2 = gapi.auth2.init({
client_id: 'MY_CREDENTIALS.apps.googleusercontent.com',
});
attachSignin(document.getElementById('customBtn'));
});
};
function attachSignin(element) {
auth2.attachClickHandler(element, {},
function(googleUser) {
document.getElementById('name').innerText = "Signed in: " +
googleUser.getBasicProfile().getName();
}, function(error) {
alert(JSON.stringify(error, undefined, 2));
}
);
}
</script>
<body>
<div id="gSignInWrapper">
<span class="label">Sign in with:</span>
<input type="button" id="customBtn" value="Google"></input>
</div>
<p id="name"></p>
</body>
</html>
You can use listeners. This is the relevant part:
// Listen for sign-in state changes.
auth2.isSignedIn.listen(signinChanged);
// Listen for changes to current user.
auth2.currentUser.listen(userChanged);
You can also get up to date values by
var isSignedIn = auth2.isSignedIn.get();
var currentUser = auth2.currentUser.get();
To strictly detect returning users only you can do:
var auth2 = gapi.auth2.init(CONFIG);
auth2.then(function() {
// at this point initial authentication is done.
var currentUser = auth2.currentUser.get();
});
When it comes to your code I would do:
auth2 = gapi.auth2.init(CONFIG);
auth2.currentUser.listen(onUserChange);
auth2.attachClickHandler(element, {});
This way all changes in sign-in state are passed to onUserChange (this includes returning users, new sign-ins from attachClickHandler, new sign-ins from different tab).

Apps Script webapp: how to authenticate bigquery?

I am trying to get my apps script webapp to execute as "user accessing the webapp", but its bigquery should run as me, the developer. (If I run the webapp as me, everything works...) I looked at the documentation at https://developers.google.com/bigquery/docs/authorization. There is no apps script example, so I tried to get the javascript example working.
<html>
<head>
<script src="https://apis.google.com/js/client.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script>
function auth() {
gapi.auth.authorize(config, function() {
gapi.client.load('bigquery', 'v2');
$('#client_initiated').html('BigQuery client authorized');
$('#auth_button').fadeOut();
$('#dataset_button').fadeIn();
});
}
// User Submitted Variables
var projectNumber = 'XXXXXXXXXX';
var clientId = 'XXXXXXXXXX.apps.googleusercontent.com';
var config = {
'client_id': clientId,
'scope': 'https://www.googleapis.com/auth/bigquery'
};
function listDatasets() {
var request = gapi.client.bigquery.datasets.list({
'projectId':projectNumber
});
request.execute(function(response) {
$('#result_box').html(JSON.stringify(response.result.datasets, null));
});
}
</script>
</head>
<body>
<button id="auth_button" onclick="auth();">Authorize</button>
<div id="client_initiated"></div>
<button id="dataset_button" style="display:none;" onclick="listDatasets();">Show datasets</button>
<div id="result_box"></div>
</body>
</html>
I generated a client id as a browser app with https://script.google.com as the server address. With the code above, I get this error: Cannot read property 'authorize_m___' of undefined.
My question is twofold: 1) Would an apps script webapp authenticate in the same way as the javascript app authenticates? I.e. can I use that code as a guide for my apps script?
And 2) any suggestions about how to debug the javascript sample code? Note that I ran this code as an apps script webapp.... That is probably an error....
The answer... or workaround answer is given here: How to pass parameters from one Google-Apps-Script to another and execute?
I can use two stage authentication in place of direct authentication: the user logs in as him/herself, I get the user's name to find their files, then switch to a webapp that uses BigQuery and execs as me, the developer.
A good workaround for advanced services authentication under apps scripts....

AngularJS authentication - Incompatibility between directives?

I'm really new on Angular JS development and I'm trying to implement the following authentication module : https://github.com/witoldsz/angular-http-auth (more info at http://www.espeo.pl/2012/02/26/authentication-in-angularjs-application), to my project .
THE MODULE
The module has been thought to allow the following scenario :
user asks for: something.com/secured/formXyz,
server sends a login form,
user logs in, fills a long and complicated form, but they are doing it so long that theirs session expires,
user submits a form, but since the session is not valid anymore, login screen appears,
once user logs in, server can process the submitted form, **no need to re-enter everything again**.
The solution to do such a thing is :
server side behavior :
for every /resources/* call, if user is not authorized, response a 401 status. Otherwise, when user is authorized or when not a /resources/* request, send what client asked for.
client side behavior :
capture 401 response,
save the request parameters, so in the future we can reconstruct original request,
create and return new object representing server’s future answer (instead of returning the original failed response),
broadcast that login is required, so application can react, in particular login form can appear,
listen to login successful events, so we can gather all the saved request parameters, resend them again and trigger all the ‘future’ objects (returned previously).
MY PROJECT
My project is a basic one, which uses the $route service. When I try to add the directive which catch the events :
scripts/directives/login-directive.js
angular.module('myApp', ['http-auth-interceptor','content-mocks'])
/**
* This directive will find itself inside HTML as a class,
* It is responsible for showing/hiding login form.
*/
.directive('authDirective', function() {
return {
restrict: 'C',
link: function(scope, elem, attrs) {
var login = elem.find('#login');
var main = elem.find('#main');
login.hide();
main.show();
scope.$on('event:auth-loginRequired', function() {
login.show();
main.hide();
});
scope.$on('event:auth-loginConfirmed', function() {
main.show();
login.hide();
});
}
}
});
to my index.html :
<body ng-app='myApp' class='auth-directive'>
<!--[if lt IE 7]>
<p class="chromeframe">You are using an outdated browser. Upgrade your browser today or install Google Chrome Frame to better experience this site.</p>
<![endif]-->
<!--[if lt IE 9]>
<script src="components/es5-shim/es5-shim.js"></script>
<script src="components/json3/lib/json3.min.js"></script>
<![endif]-->
<div id="login" name="login">
<p>login</p>
</div>
<div id="main" name="main">
<p>main</p>
<div class="container" ng-view></div>
</div>
<script src="components/angular/angular.js"></script>
<script src="components/angular-resource/angular-resource.js"></script>
<script src="components/angular-cookies/angular-cookies.js"></script>
<script src="components/angular-sanitize/angular-sanitize.js"></script>
<script src="components/angular-http-auth/angular-mocks-1.0.1.js" type="text/javascript"></script>
<script src="components/angular-http-auth/http-auth-interceptor.js" type="text/javascript"></script>
<script src="scripts/app.js"></script>
<script src="scripts/controllers/main.js"></script>
<script src="scripts/directives/login-directive.js"></script>
<script src="scripts/mocks/content-mocks.js" type="text/javascript"></script>
</body>
The directive ngView doesn't work anymore. Nothing appears on console log. Is there any incompatibility between the two directives ?
By the look of your login-directive files it seems that you are (improperly) reinitialising you Angular app by passing the dependencies array as a second parameter to your angular.module call:
angular.module('myApp', ['http-auth-interceptor','content-mocks'])
Dependencies should be defines only once, when app is initialised (probably in app.js), and when you want to reference your application in another file you should call the angular.module without the second parameter:
angular.module('myApp')
.directive('authDirective', function() { ...

How to ask for permissions with Google+ Badge API?

I am using Google+ Badge API to get Followers and +1's. Also, i have the Signin button using the Google+ Signin API. When i login using the Signin button, it asks for the email permission. But, when i want to Follow or +1 the Google+ Page it asks me to login and never asks for the permissions.
<html>
<head>
<title>Demo: Getting an email address using the Google+ Sign-in button</title>
<style type="text/css">
.hide { display: none;}
.show { display: block;}
</style>
<script src="https://apis.google.com/js/plusone.js" type="text/javascript"></script>
<script type="text/javascript">
/*
* Triggered when the user accepts the the sign in, cancels, or closes the
* authorization dialog.
*/
function loginFinishedCallback(authResult) {
if (authResult) {
if (authResult['error'] == undefined){
gapi.auth.setToken(authResult); // Store the returned token.
toggleElement('signin-button'); // Hide the sign-in button after successfully signing in the user.
getEmail(); // Trigger request to get the email address.
} else {
console.log('An error occurred');
}
} else {
console.log('Empty authResult'); // Something went wrong
}
}
/*
* Initiates the request to the userinfo endpoint to get the user's email
* address. This function relies on the gapi.auth.setToken containing a valid
* OAuth access token.
*
* When the request completes, the getEmailCallback is triggered and passed
* the result of the request.
*/
function getEmail(){
// Load the oauth2 libraries to enable the userinfo methods.
gapi.client.load('oauth2', 'v2', function() {
var request = gapi.client.oauth2.userinfo.get();
request.execute(getEmailCallback);
});
// This sample assumes a client object has been created.
// To learn more about creating a client, check out the starter:
// https://developers.google.com/+/quickstart/javascript
gapi.client.load('plus','v1', function(){
var request = gapi.client.plus.people.list({
'userId': 'me',
'collection': 'visible'
});
request.execute(function(resp) {
console.log('Num people visible:' + resp.totalItems);
});
});
// This sample assumes a client object has been created.
// To learn more about creating a client, check out the starter:
// https://developers.google.com/+/quickstart/javascript
gapi.client.load('plus','v1', function(){
var request = gapi.client.plus.people.get({
'userId': 'me'
});
request.execute(function(resp) {
console.log('Retrieved profile for:' + resp.displayName);
console.log(resp);
});
});
}
function getEmailCallback(obj){
var el = document.getElementById('email');
var email = '';
if (obj['email']) {
email = 'Email: ' + obj['email'];
}
//console.log(obj); // Uncomment to inspect the full object.
el.innerHTML = email;
toggleElement('email');
}
function toggleElement(id) {
var el = document.getElementById(id);
if (el.getAttribute('class') == 'hide') {
el.setAttribute('class', 'show');
} else {
el.setAttribute('class', 'hide');
}
}
</script>
</head>
<body>
<div id="signin-button" class="show">
<div class="g-signin" data-callback="loginFinishedCallback"
data-clientid="MY_CLIENT_ID"
data-scope="https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email"
data-height="short"
data-cookiepolicy="single_host_origin"
>
</div>
</div>
<!-- Place this tag where you want the badge to render. -->
<div class="g-plus" data-width="210" data-href="//plus.google.com/MY_PAGE_ID" data-rel="publisher" data-theme="dark"></div>
<div id="email" class="hide"></div>
</body>
</html>
Is there a way around where i can ask users for permissions when they login using the Follow AND +1 button?
Also, if a user has set his/her profile fields (like birthday) privacy to 'ONLY ME', then can i request for those fields? If yes, how?
1- there isn't a Badges API.
2- You should distinguish between
- using Google functionality, like the badges or the +1 button, which requires the user to login to Google+
- using your service's functionality, like the sign-in button, which requires the user to connect their Google+ account to your service
In short the user has to sign-in to Google+ (which doesn't require any permissions because Google+ is the identity provider) and connect to your service/site/app which requires them to give you permission.

Can I make Custom FaceBook's Light Box of "Connect with Facebook to Continue" and "This site requires that you Connect with Facebook."?

can we change text in this lightbox
want to change "This site requires that you Connect with Facebook." and "Connect with Facebook to Continue"
Not sure about doing this with the JavaScript SDK, but I know you can do it with the PHP one.
You would have to create your box you want displayed in HTML and CSS. When you generate it in your page you have the server make the action of clicking on the button direct the user to the location provided by $facebook->getLoginUrl();
This would essentially do what you want. As i said i am unsure if the same thing is accomplish-able with the JavaScript SDK.
here what i am doing
in "facebooklogin.aspx" is first page for login after login and permissions user redirect to xxx.aspx page where i am fetching user details.
-- facebooklogin.aspx
<head runat="server">
<title>FaceBook Login</title>
<script src="http://static.ak.connect.facebook.com/js/api_lib/v0.4/FeatureLoader.js.php/en_US"
type="text/javascript"></script>
<script type="text/javascript">
function fblogin()
{
//<![CDATA[
//Replace API key with yours from Facebook
var api_key = 'XXX';
var channel_path = 'xd_receiver.htm';
FB_RequireFeatures(["XFBML"], function() {
// Create an ApiClient object, passing app's API key and
// a site relative URL to xd_receiver.htm
FB.Facebook.init(api_key, channel_path);
FB.ensureInit(function() {
FB.Connect.showPermissionDialog("email,user_birthday,user_location", function(perms) {
if (!perms) {
//alert("No");
} else {
//alert("yes");
window.location ="http://xxx.aspx"
}
});
});
});
//]]>
}
</script>
</head>
<body>
<form id="form1" runat="server">
<div id="divbtnfb">
<a onclick="javascript:fblogin();">login</a>
</div>
</form>
</body>
--CODE BEHIND in XXX.aspx
protected void Page_Load(object sender, System.EventArgs e)
{
if (ConnectAuthentication.isConnected())
{
//Create instance of REST api using current authanticated session
Api api = new Api(CurrentSession);
//Display user data captured from the Facebook API.
try
{
Facebook.Schema.user user = api.Users.GetInfo();
firstName.Text = user.first_name;
lastName.Text = user.last_name;
//for email use client code
}
catch
{
Response.Redirect("facebooklogin.aspx");
}
}
else
{
Response.Redirect("facebooklogin.aspx");
}
}
-- In facebooklogin.aspx its checking for permission, if user is not loggedin to facebook this script shows this lightbox.
I want to change this lightbox