I'm using the javascript api for Google Auth 2.0 . I'm running into the problem where the users email is not showing up, even though I request with https://www.googleapis.com/auth/userinfo.email .
My code looks like this:
gapi.auth.authorize({
client_id : 'xxxxxxxxxx.apps.googleusercontent.com',
scope : ['https://www.googleapis.com/auth/plus.me', 'https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile'],
immediate : false
}, function(result) {
if (result != null) {
gapi.client.load('plus', 'v1', function() {
var request = gapi.client.plus.people.get({
'userId' : 'me'
});
request.execute(function(resp) {
console.log(resp);
});
});
}
});
What am I missing to get the user's email?
While the userinfo.email role gives you access to the information, the plus v1 client doesn't provide it. You will need to make an additional call to a different endpoint to get the info.
You will need the oauth2 v2 endpoint, which you can request with gapi.client.load('oauth2', 'v2', callback). The endpoint itself that you want is gapi.client.oauth2.userinfo.get(). This is untested, but the code might look something like:
gapi.client.load('oath2','v2',function(){
gapi.client.oauth2.userinfo.get().execute(function(resp){
console.log(resp);
});
});
See How can I get the user's email address in google apps? and Why can't I retrieve the users email from Google Plus API after getting permission for some related questions and https://developers.google.com/accounts/docs/OAuth2 for more details from the official doc.
Here's how I did it:
function tryAuth() {
var clientId = CLIENT_ID;
var configString = {
client_id: clientId,
scope: SCOPE,
immediate: 'false'
};
gapi.auth.authorize(configString, handleAuthResult);
}
Where SCOPE = 'https://www.googleapis.com/auth/fusiontables email';
Replace https://www.googleapis.com/auth/fusiontables scope with your scope but keep ' email' .
function handleAuthResult(authResult) {
if (authResult && !authResult.error) {
var access_token = authResult.access_token;
alert('Successfully logged in.' + access_token);
tryGetEmail(access_token);
}
And then
function tryGetEmail(access_token) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET", 'https://www.googleapis.com/oauth2/v1/userinfo?access_token=' + access_token, false );
xmlHttp.send( null );
if(xmlHttp.status == 200) {
var strJSON = xmlHttp.responseText;
var objJSON = eval("(function(){return " + strJSON + ";})()");
email = objJSON.email;
alert('got email ' + email);
}
}
The userinfo endpoint and oauth2 v2 are being deprecated. The older answers are for the old system. All the details for migration are here:
https://developers.google.com/+/api/auth-migration#email
In short: put 'email' instead of 'h ttps://www.googleapis.com/auth/userinfo.email' for your scope, and the G+ email will be included as the first entry of the 'emails' property in the 'person' object you're fetching. There's also apparently an option described in the link to pull it out of the ID token, referenced in the link above.
Full example
Related
I am trying to setup a small application based on Google App Script and Google sheets
So far I am able to get the facebook page feed and able to connect to facebook app through Oauth2 library and got the tokens
So I am struggling to write the code for posting to facebook ,
Currently I got the facebook page feeds but the post format documentation from facebook is referring to javascript SDK , and here I am not using SDK and graph API refers with POST and GET requests I think that also didn't work here .
So this is my Codes for getting facebook token and page feed .
Some one referred this as a duplicate but those question and answer doesn't fit in my question , I want facebook post format if it is with payload kindly include those payload options or someone can edit below code for POST
I am able to post to pages with the page access token not user access token
and pasting that page access token also throws the same error .
Updated Code But error appears as follows
Request failed for https://graph.facebook.com/v3.2/PAGE/feed?message=hello?access_token=TOKEN returned code 403. Truncated server response: {"error":{"message":"(#200) If posting to a group, requires app being installed in the group, and \\n either publish_to_groups permission... (use muteHttpExceptions option to examine full response) (line 53, file "FB postinf")
code updated
function example() {
var service = getService();
if (service.hasAccess())
var data = {
"message" : "Me Testing",
//"slug" : "me-testing",
// "text_to_subscribe" : "true"
};
var payload = JSON.stringify(data);
var options = {
"method" : "POST",
"contentType" : "application/json",
"payload" : payload
};
var url = "https://graph.facebook.com/v3.2/PAGENAME/feed?message=hello?access_token=ManuallyPastedTOKEN";
// + '?access_token=' + encodeURIComponent(service.getAccessToken());
var response = UrlFetchApp.fetch(url, options);
}
This is using OAuth2 GET FEED
function sasublish(){
var service= getService()
if (service.hasAccess()) {
var url = 'https://graph.facebook.com'
+ '/pagename/feed'
+'?fields='+ encodeURIComponent("name,full_picture,message,attachments{subattachments,url}")
+'&access_token='+ service.getAccessToken();
var response = UrlFetchApp.fetch(url, {'muteHttpExceptions': true});
var json = response.getContentText();
var jsondata = JSON.parse(json);
}
Need oAuth2 And Facebook.gs snippet you can use
.setScope('publish_pages,manage_pages,pages_show_list')
should be added to facebook.gs link to facebook.gs snippet
function example() {
var service = getService();
if (service.hasAccess())
var urls ='https://graph.facebook.com/v2.6/PAGEID?fields=access_token'; //specified page token
// var url = 'https://graph.facebook.com/v2.6/me/accounts?'; //tokens along with pages
var response = UrlFetchApp.fetch(urls, { //make url and urls changes
headers: {
'Authorization': 'Bearer ' + service.getAccessToken()
}
});
var result = JSON.parse(response.getContentText());
Logger.log(JSON.stringify(result , null, 2));
//Logger.log(JSON.stringify(result.data[0].access_token))
var datas = {
"message" : "Me Testing",
//"slug" : "me-testing",
// "text_to_subscribe" : "true"
};
var payload = JSON.stringify(datas);
var options = {
"method" : "POST",
"contentType" : "application/json",
"payload" : payload
};
var url = "https://graph.facebook.com/v3.2/PAGEID/feed"
+ '?access_token=' + encodeURIComponent(result.data[0].access_token);
// + '?access_token=' + encodeURIComponent(result.access_token);//direct pagetoken
var response = UrlFetchApp.fetch(url, options);
Logger.log('successfully posted to facebook page ',response)
}
I'm trying to update a PFUser in my Parse database from a node.js app running on Heroku. I'm calling the Parse cloud function from an iOS app.
Here's the part of the code I use to update the user on parse as well as creating the user on Stripe (the Stripe part works fine):
Parse.Cloud.define("createCustomerWithCreditCardToken", function(request, response) {
var userId = request.user.id;
var sourceId = request.params.creditCardToken;
var customerId;
var userSessionToken = request.user.getSessionToken();
console.log('userId: ' + userId + ' source: ' + sourceId + ' userSessionToken: ' + userSessionToken);
stripe.customers.create({
source: sourceId,
description: userId
}, function(error, customer) {
if (error !== null) {
response.error('error creating customer: ' + error);
}else {
var userQuery = new Parse.Query('User');
userQuery.equalTo('objectId', userId);
userQuery.first({sessionToken: userSessionToken}).then(function(user) {
console.log('user from parse query: ' + user.get("username"));
user.set("stripeCustomerId", customer.id);
user.save(null, {
success: function(parseCustomer) {
console.log('customer saved to parse: ' + parseCustomer);
},
error: function(error, parseCustomer) {
console.log('customer save failed: ' + JSON.stringify(error, null, 2) + ' with error: ' + JSON.stringify(parseCustomer,null, 2));
}
});
});
customerId = customer.id;
console.log('customerId: '+ customerId);
// response.success(customer);
response.success('Customer: ' + JSON.stringify(customer, null, 2) + 'error: ' + error);
}
});
});
I get the following error log output when I run this:
error log output
error: { "code": 206, "message": "Parse::UserCannotBeAlteredWithoutSessionError" }
In this post the current user concept in a node.js app context is discussed by a Parse engineer.
Also in Cloud Code, the concept of a method that returns the current
user makes sense, as it does in JavaScript on a web page, because
there’s only one active request and only one user. However in a
context like node.js, there can’t be a global current user, which
requires explicit passing of the session token.
Essentially he advises to do this:
Parse.Cloud.define('findBacon', function(req, res) {
var token = req.user.getSessionToken();
var query = new Parse.Query('Bacon');
// Pass the session token to the query
query.find({ sessionToken: token }).then( ... );
});
I have also tried using {useMasterKey:true} instead of {sessionToken:userSessionToken} which produces the same error.
I might just be missing some obvious detail, but I just can't seem to find it. Any ideas on how to solve this are greatly appreciated.
Turns out there's a third way of handling credentials:
Parse.Cloud.useMasterKey();
I placed this line in the beginning of the entire method, that did it for me. I'm not sure of the implications of giving the whole function these credentials though.
I'm not sure when you would use the other options either.
If someone comes across this and would like to elaborate, I'll be happy to give the right answer to a good explanation of when to grant which credentials.
I'm having a problem of using variables between functions. As you can see down below User.username is available and good at the sign up page, but when you go to the login page I told it to first alert the value of User.username, and it alerts undefined? I'm confused here. I'm pretty sure I'm missing a concept here. Anyways Thank you so much!:
Here is a plunkr: http://plnkr.co/edit/qB3Gkeq5ji1YQyy0kpGH
Here is my script.js:
app.controller("AuthCtrl", ["$scope", "Auth","$rootScope",
function($scope, Auth, $rootScope) {
var User = {}
$scope.createUser = function(username, email, password) {
$rootScope.usernames = username
User.username = username
$scope.message = null;
$scope.error = null;
var ref2 = new Firebase("https://uniquecoders.firebaseio.com/");
ref2.createUser({
email: $scope.email,
password: $scope.password
}, function(error, userData) {
if (error) {
switch (error.code) {
case "EMAIL_TAKEN":
alert("The new user account cannot be created because the email is already in use. Try to login");
break;
case "INVALID_EMAIL":
alert("The specified email is not a valid email.");
break;
case "INVALID_PASSWORD":
alert("The Specified Passowrd Is not valid.")
break;
default:
alert("Error creating user:", error);
}
} else {
alert("Successfully created user account with username" + User.username);
window.location.hash = "/User"
}
});
};
$scope.logIn = function(){
alert(User.username)
$rootScope.usernames = User.username
$scope.message = null;
$scope.error = null;
var ref2 = new Firebase("https://uniquecoders.firebaseio.com/");
ref2.authWithPassword({
"email" : $scope.logInemail,
"password" : $scope.logInemailpassword
}, function(error, userData){
if(error){
alert("Login Failed Because : " + error)
}
else{
alert("Logged In!")
window.location.hash = "/User"
}
})
}
/* $scope.removeUser = function() {
$scope.message = null;
$scope.error = null;
Auth.$removeUser({
email: $scope.email,
password: $scope.password
}).then(function() {
$scope.message = "User removed";
}).catch(function(error) {
$scope.error = error;
});
};*/
}
]);
When page reloads the entire javascript is re executed. So user object populated in registration process is no longer available in login function because page has reloaded on clicking on signup button.
So we need to store the user name in cookie so that we can access it any time. Even when its removed from javascript runtime it will be there in the browser cache.
Its risky to store passwords in cookies for security issues.
On successful registration save the username in cookie.
Some thing like this
document.cookie ="username =" +username + "; " +"email ="+ email;
In login function get cookie values and check if email of user is same as entered
function getCookie(name ) {
var pairs = document.cookie.split("; "),
count = pairs.length, parts;
while ( count-- ) {
parts = pairs[count].split("=");
if ( parts[0] === name )
return parts[1];
}
return false;
}
var username=getCookie("username");
var email=getCookie("username");
if($scope.logInemail ==email)
{
alert(username);
}
You have to remember that a web application is a communication between the browser the the web server. The browser and the server can be on different machines (even if they aren't when you are testing.)
There are two ways to make this work
While the browser and the server communicate some information is saved and passed back and forth on both ends -- this is called a cookie. This is the most common way to do save data between pages. The browser requests a page. When it get the reply from the server it contains a cookie. The next time the browser requests a page it includes the cookie in it's request. In this way the server knows the request is related to the prior request.
The server is smart enough to keep track of all "sessions" by different browsers. It then saves session data about that communication -- when it gets the next request from the same browser it goes to the session data and retrieves information about that that browser and what it was doing. (Often this is done with cookies.)
I'm working on wrapping my $resource requests in a simple wrapper. The main idea
is to be able to add some logic before the request is made. I've followed the nice article written by Nils.
Here you can see a service definition to access the REST API module.
resources.factory('Device', ['RequestWrapper', '$resource', 'lelylan.config', function(RequestWrapper, $http, config) {
var resource = $resource(config.endpoint + '/devices/:id', { id: '#id' });
return RequestWrapper.wrap(resource, ['get', 'query', 'save', 'delete']);
}]);
And here you can see the request wrapper definition.
resources.factory('RequestWrapper', ['AccessToken', function(AccessToken) {
var requestWrapper = {};
var token;
requestWrapper.wrap = function(resource, actions) {
token = AccessToken.initialize();
var wrappedResource = resource;
for (var i=0; i < actions.length; i++) { request(wrappedResource, actions[i]); };
return wrappedResource;
};
var request = function(resource, action) {
resource['_' + action] = resource[action];
resource[action] = function(param, data, success, error) {
(AccessToken.get().access_token) ? setAuthorizationHeader() : deleteAuthorizationHeader()
return resource['_' + action](param, data, success, error);
};
};
var setAuthorizationHeader = function() {
$http.defaults.headers.common['Authorization'] = 'Bearer ' + token.access_token;
};
var deleteAuthorizationHeader = function() {
delete $http.defaults.headers.common['Authorization']
};
return requestWrapper;
}]);
Everything works just fine for the GET and DELETE methods (the ones that does not returns
a body seems), but I can't get $save working. What happens is that when the JSON of the
created resources returns it is not added. I have only the data I've set on the creation
phase. Let me make an example.
In this case we use the wrapped resource. If I try to get the #updated_at attribute I can't
see it. In the Chrome inspector I can see how the resource is successfully created.
$scope.device = new Device({ name: 'Angular light', type: 'http://localhost:9000/types/50bf5af4d033a95486000002' });
$scope.device.$save(function(){ console.log('Device Wrapped', $scope.device.created_at) });
# => undefined
If I use $resource everything works fine.
// Suppose authorization is already set
var Resource = $resource('http://localhost\\:9000/devices/:id');
$scope.resource = new Resource({ name: 'Angular light', type: 'http://localhost:9000/types/50bf5af4d033a95486000002' });
$scope.resource.$save(function(){ console.log('Device Base', $scope.resource.created_at); });
# => 2013-02-09T12:26:01Z
I started to check the angular-resource.js code but after few hours I couldn't really figure
it out. I can't get why the body is returned, but in the wrapper resource it is not accessible.
Any idea or help would be appreciated. Thanks.
While diving into AngularJS source code I've found the solution.
The problem was that the wrapper was returning a function instead of an object and this was giving some problems. The solution is to change the following row in the Wrapper:
return resource['_' + action](param, data, success, error);
with this one:
return resource['_' + action].call(this, params, data, success, error);
Why? The fast answer is because in the source code of angular-resource they use it. Actually #call run the function sending this to the calling object. It is often used to initialize an object. Learn more here.
I'm trying to do a implementation of the google login on our website. I have read the documentation and setup a application on the apis console.
I prefer that the signup dialogue is shown in a popup and after the users logins in and accepts the permissions that I would get an javascript callback. This the api also supports according to the documentation. So I build the below with help of the documentation ;-)
This first part is to load the google client script async and the init the script with the correct clientid and apikey.
$gp = new googlePlus('#Trustpilot.Web.Social.Google.ClientID', '#Trustpilot.Web.Social.Google.ApiKey');
(function () {
var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
po.src = 'https://apis.google.com/js/client.js?onload=googlePlusClientLoad';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
})();
The next part it the part that uses the google client api. handleClientLoad() is called when the client.js is loaded. The method checks if the use is authenticated. If the user is, the idea is that I want to login the user.
If the user is not already authenticated there will be a button and when clicking handleAuthClick() is called, it basic does the same as handleClientLoad() but there will be an popup where the user most login (with google account) and accept permissions. After login handleAuthResult() is called which logins the user.
function googlePlus(clientId, apiKey) {
this.clientId = clientId;
this.apiKey = apiKey;
this.scopes = 'https://www.googleapis.com/auth/plus.me https://www.googleapis.com/auth/userinfo.email';
/// This method is called when the javascript is loaded async
this.handleClientLoad = function() {
gapi.client.setApiKey(this.apiKey);
window.setTimeout(this.authorize(true), 1);
};
this.handleAuthResult = function (authResult) {
console.log(authResult);
if (authResult && !authResult.error) {
var token = gapi.auth.getToken();
console.log(token);
}
else if (authResult && authResult.error) {
alert(authResult.error);
}
};
this.handleAuthClick = function(event) {
this.authorize(false);
return false;
};
this.makeApiCall = function() {
gapi.client.load('plus', 'v1', function () {
var request = gapi.client.plus.people.get({
'userId': 'me'
});
request.execute(function (resp) {
console.log(resp);
});
});
};
this.authorize = function (immediate) {
gapi.auth.authorize({ client_id: this.clientId, scope: this.scopes, immediate: immediate }, this.handleAuthResult());
//gapi.auth.authorize({ client_id: this.clientId, scope: this.scopes, immediate: immediate }, this.handleAuthResult());
};
}
var googlePlusClientLoad = function() {
$gp.handleClientLoad();
};
So now to the problem:
When handleClientLoad is called the user is never authenticated even
if the user already has autehnticated.
If the user uses handleAuthClick() the popup shows and login, permissions and
callback to handleAuthResult() work. But the parameter authResult is
always nothing (should be something accouring to the documentation).
If I try multiple times without reloading the page I can sometimes
get the makeApiCall() and gapi.auth.getToken() to work and get the information I need.
There are two issues in your code :
The API key is not required, you can remove it. You get the user token through OAuth2 and that is enough.
In authorize(), the handleAuthResult method is not correctly called, remove the parenthesis at the end of the function name. You do not want to execute the function, just pass its reference. Here's what the authorize method must look like :
gapi.auth.authorize({ client_id: this.clientId, scope: this.scopes, immediate: immediate }, this.handleAuthResult);
Note the difference in parenthesis.