Any way to carry the gapi.client object on multiple pages? - javascript

I want to have two flows where in on the first page I just ask the user to signin.
Then if he/she wants to invite friends, I want to fire the gapi.client.people.list method and retrieve the visible circle list.
As I am doing this on two seperate pages, I am not able to carry through the gapi object which has been authenticated. I don't want to trigger the signin flow again but if I directly do gapi.client.load, it gives me an error saying Daily unauthenticated usage limit exceeded.
function getFriends(e)
{
gapi.client.load('plus','v1', function(){
//gapi.client.setApiKey("myKey");
var request = gapi.client.plus.people.list({
'userId': 'me',
'collection': 'visible'
});
request.execute(function(people) {
console.log(people);
$('#googleFriends').empty();
$('#googleFriends').append('Number of people visible to this app: ' +
people.totalItems + '<br/>');
for (var personIndex in people.items) {
person = people.items[personIndex];
$('#googleFriends').append('<img src="' + person.image.url + '">');
}
});
});
}
Is there a solution here? I tried using gapi.client.setApiKey but that keeps returning an invalid key error.
I have read and tried to understand google quickstart tutorial but there they call the people.list as part of signin flow.

If the user already signed-in/authorized your app, you can use the gapi.auth.authorize method with the same client_id and scope parameters you have used for the Sign-in, and immediate set to true.
gapi.auth.authorize({
client_id: 'your_client_id',
scope: 'your_scopes',
immediate: true
}, function (authResult) {
// check authresult and then call getFriends
});
This will go through the auth process without any user interaction necessary and gapi.client will then be able to make authorized calls. You will have to check the response in the callback though, to make sure you are actually authorized.
Docs: https://developers.google.com/api-client-library/javascript/reference/referencedocs#gapiauthauthorize

Related

How can I not authenticate everytime in StackExchange API calls using JS client?

I am using this code from the StackExchange App Documentation to get the user information from StackOverflow.
// For simplicity, we're using jQuery for some things
// However, the library has no jQuery dependency
$(function(){
// Initialize library
SE.init({
// Parameters obtained by registering an app, these are specific to the SE
// documentation site
clientId: 1,
key: 'U4DMV*8nvpm3EOpvf69Rxw((',
// Used for cross domain communication, it will be validated
channelUrl: 'https://api.stackexchange.com/docs/proxy',
// Called when all initialization is finished
complete: function(data) {
$('#login-button')
.removeAttr('disabled')
.text('Run Example With Version '+data.version);
}
});
// Attach click handler to login button
$('#login-button').click(function() {
// Make the authentication call, note that being in an onclick handler
// is important; most browsers will hide windows opened without a
// 'click blessing'
SE.authenticate({
success: function(data) {
alert(
'User Authorized with account id = ' +
data.networkUsers[0].account_id + ', got access token = ' +
data.accessToken
);
},
error: function(data) {
alert('An error occurred:\n' + data.errorName + '\n' + data.errorMessage);
},
networkUsers: true
});
});
});
This code works fine but I noticed that everytime it fires and gives the response access_token changes. How I can I just get user information using the access token. Plus this is returning user's data with all the sites he is part of. How can I limit it to just StackOverflow. I am unable to find proper documentation for this.
Can anyone please point me to the JS methods for making API calls from StackExchange API?

OAuth 2.0 authentication silently failing in Google Blogger JavaScript API (v3)

Until recently I was using the javascript Blogger API v3 to fetch posts from a blog to build an automated index, using the gapi.client.blogger.posts.list() method.
However, it suddenly stopped working. The response now simply includes an error object with the message "We're sorry, but the requested resource could not be found.", as if the blog I'm trying to fetch info does not exist at all. I'm pretty sure my blog ID did not change during my sleep.
So I started digging around, and I found that the OAuth authentication token returned says I'm not actually logged in Google. In the status object from the token, the signed_in property is true but the google_logged_in one is false. Yet the Google login page showed up correctly upon execution and I allowed it, and the token does not have an error property. Why does it says I'm not logged in?
I'm under the assumption that Google blocked my application from making new requests due repeated use (even thought I never reached a dent of the daily quota) and now since the OAuth 2 ID authentication does not work, the API tries to use only the web browser API key, which does not work either since it's a private blog and user permission is required. But I really don't know.
I could not find a similar situation on the internet. I tried deleting both my API and OAuth keys and making new ones, deleting the project from the Google Console Developers and creating a new one under a different account, removing the application from the "allowed apps" in my account and adding it again, not using the browser API key. No game.
I would appreciate any inputs in solving this issue. And before anyone suggests: the "conventional" way of creating an automated index using the blog's feed does not work in my case, since the blog is private and has no feed.
Bellow is the returned access token (redacted a bit):
{
_aa: "1"
access_token: "[Redacted]"
client_id: "[Redacted]"
cookie_policy: undefined
expires_at: "1460999668"
expires_in: "3600"
g_user_cookie_policy: undefined
issued_at: "1460996068"
response_type: "token"
scope: "https://www.googleapis.com/auth/blogger.readonly"
state: ""
status: {
google_logged_in: false
method: "AUTO"
signed_in: true
}
token_type: "Bearer"
}
And bellow an example code of my application:
var apiKey = "myWebBrowserApiKey";
var clientId = "myOAuth2ClientID";
var blogId = "myBlogID";
var bloggerReadOnly = "https://www.googleapis.com/auth/blogger.readonly";
var fetchParam = {
blogId: blogId,
fetchBodies: false,
fetchImages: false,
maxResults: 500,
status: "live",
view: "READER"
};
var authParamIm = {
client_id: clientId,
scope: bloggerReadOnly,
immediate: true
};
var authParam = {
client_id: clientId,
scope: bloggerReadOnly,
immediate: false
};
//Executed as soon the client:blogger.js loads
function handleClientLoad() {
gapi.client.setApiKey(apiKey);
window.setTimeout(checkAuth,1);
}
//Check if the user is already authenticated for immediate access
function checkAuth() {
gapi.auth.authorize( authParamIm, handleAuthResult );
}
function handleAuthResult(authResult) {
//If the user does already have authorization, proceed with the API call
if (authResult && !authResult.error) {
makeApiCall();
}
//If he does not, shows the authorization button
else {
authorizeButton.css('visibility', 'visible');
authorizeButton.click(handleAuthClick);
}
}
//The authorization button calls this function, that shows a Google login page
function handleAuthClick(event) {
gapi.auth.authorize( authParam, handleAuthResult );
return false;
}
//Loads the Blogger API, version 3, and runs the fetchPosts() function with a callback
function makeApiCall(){
gapi.client.load('blogger', 'v3', fetchPosts);
}
function fetchPosts(){
//Creates a request to get a list of posts from the blog, using the fetch parameters
var request = gapi.client.blogger.posts.list(fetchParam);
//Execute the request and treats the response with a callback function
request.execute(function(response){
//Do Stuff
}
}

Google Cloud Storage Javascript anonymous upload

I want to use GCS to store pictures taken by users in my Cordova app. I have a first major issue : I can't figure out how to use GCS properly for that purpose. Every usable example in the documentation ( here or there for the auth process alone ) needs to give credentials from a Google account on the client side to be able to use the JSON API. I don't want that. Basically I want every people connected to my app to be able to upload freely to the wanted bucket. I thought about issuing tokens for every user of my app, etc... But first I need to be able to upload something anyhow, right ?
My current status is this :
function init() {
gapi.client.setApiKey(apiKey);
gapi.auth.authorize({
client_id: clientId,
scope: 'https://www.googleapis.com/auth/devstorage.full_control',
immediate: true
}, initializeApi);
}
function initializeApi() {
gapi.client.load('storage', 'v1').then(makeRequest);
}
function makeRequest() {
var request = gapi.client.storage.buckets.list({
'project': PROJECT
});
request.then(function(response) {
console.log(response);
}, function(reason) {
console.log('Error: ' + reason.result.error.message);
});
}
And well, the log just gives me the error : "Error: Login Required" along with a 401 unauthorized on
https://content.googleapis.com/storage/v1/b?project=PROJECT&key=apiKey
Well, since I provided everything I had, I guess I need some sort of authentication token. I simply didn't find anywhere how to do that.
The only lead I have would be this : service accounts and it absolutely doesn't sound like something fit for client side code.
Thanks ahead !

Defer creation of controllers/services on app run angularjs

every time the route change, I check if is set a variable with the current logged user details, if this variable is not set, I simply redirect to the login page.
Now I'm trying to achieve some kind of "remember me" functionality, so every time the route changes, if the variable user doesn't exist instead of redirecting to the login page, I check the local storage for an "authtoken", if is set I call a check function passing the authtoken to the server, that returns the user and the app will works the same way as after the manual login (that returns the same user as the check function).
I'm pretty sure this is not the best way to do that.
If I reload the page, first thing I run the check function that sends the authtoken to the server and wait for a response, if the user exists that value is assigned to a variable in the rootscope.
I have different services that use the variable in the rootscope, for example
angular.module('myApp')
.service('AdminService', function AdminService($rootScope, $http, StorageService) {
var authtoken = StorageService.get('authtoken');
var clientId = $rootScope.loggedUser.id;
and of course when the check function runs it waits for the response, but the service is being instantiated and $rootScope.loggedUser.id does not exists.
How can I tell the app to wait until the check function receive the response?
This is my code
....
}).run(function($rootScope, $location, AuthService, StorageService) {
var intended = $location.path();
AuthService.check().success(function(data) {
if(data.user) {
$rootScope.loggedUser = data.user;
$location.path(intended);
} else $location.path('login');
});
$rootScope.$on('$routeChangeStart', function() {
if($rootScope.loggedUser) {
....
For example if the user bookmarks the page "myapp.com/#/admin/users", I don't want to redirect to the login, if I have in local storage the authtoken, but this causes the controller to be instantiated, that uses the service, that needs the $rootScope.loggedUser.id that is not yet populated.
And I want to run the function check only when the page (re)loads (not every time the user change route).
I would advise to re-examine your design if you need a service call to check your auth token. Auth tokens typically have expiration time stored in them, so you could just check whether you are within the expiration period without calling the server. You are not compromising security, since auth tokens are signed by the server, and validated when you make server calls to do anything useful. So, under normal circumstances, no separate check call is needed.
But, if you insist, this use case is best handled with the resolve property of the route. This means, however, that every route that cares about the user's logged-in state would have to have a resolve property defined. This does not mean that you have to call the service on each route change.
Instead of using $rootScope.loggedUser to store the user, have it be cached by the AuthService and injected via the resolve parameter.
So, you could do the following:
$routeProvider
.when("some/secure/route", {
controller: "SecureCtrl",
resolve: {
user: AuthService.checkUser
}
});
And in your AuthService:
...
checkUser: function(){
var deferred = $q.defer();
if (cachedUser){
deferred.resolve(cachedUser);
} else {
AuthService.check().success(
function(data){
// cachedUseris defined at AuthService's scope
cachedUser = data.user;
deferred.resolve(data.user);
});
}
return deferred.promise;
}
Then, in your controllers:
.controller("SecureCtrl", function(user){
$scope.userId = user.id;
}

Google Javascript API (gapi) - problems with .load

I am trying to use the Google plus API (via googie-api-javascript) implementation like so (omitting full code):
var clientId = '7454475891XxxxxxXom4c6n.apps.googleusercontent.com'; //fake client
var apiKey = '-uTH_p6NokbrXXXXXXXXXXXXX'; //Fake Key
var scopes = 'https://www.googleapis.com/auth/plus.me';
function handleClientLoad() {
gapi.client.setApiKey(apiKey);
window.setTimeout(checkAuth,1);
}
function checkAuth() {
gapi.auth.authorize({client_id: clientId, scope: scopes, immediate: true}, handleAuthResult);
}
function handleAuthResult(authResult) {
if (authResult && !authResult.error) {
makeApiCall();
} else {
//handle user-approval
}
}
// Load the API and make an API call. Display the results on the screen.
function makeApiCall() {
gapi.client.load('plus', 'v1', function() {
var o = gapi.client.plus;
alert(o);
});
}
The code works well upto the point of gapi.client.load (including the user allowing access) - this callback gets called but alert(o) will return undefined.
Upon inspecting the HTTP request I see the .load issues a request to:
https://content.googleapis.com/discovery/v1/apis/plus/v1/rpc?fields=methods%2F*%2Fid&pp=0&key=-uTH_p6NokbrXXXXXXXX
This returns HTTP 400 with the following message:
{"error":{"errors":[{"domain":"usageLimits","reason":"keyInvalid","message":"Bad Request"}],"code":400,"message":"Bad Request"}}
My question is - what do I need to change to make this work?
Is there some secret setting I need to enable ? Google+ is enabled in the google-developer-console under the APIs list.
Thanks for the help,
Alon
Problem:
.load issues a request to the google discovery service to load the .JS. The service will error out if the request it receives contains an api-key. (I don't know why the library works like this, it seems like a bug?)
Fix:
gapi.client.setApiKey(""); //NEW
gapi.client.load('plus', 'v1', function()
//re-add the key later if you need it
From Discovery Service docs:
requests you make to the Discovery Service API should not include an API key. If you do provide a key, the requests will fail.
Weird... :P
A little update & more of an explanation. The current Discovery Service page is a little more specific now. They indicate that if the app has an Oauth2 token, then the API Key value is not required. However, I also found that if I have an authenticated user and thus an Oauth2 token (access_token) present, the Discovery Service fails with the error noted in the OP. This seems to contradict the documentation.
You can see the token in the developer tools console with:
console.log(gapi.auth2.getAuthInstance().currentUser.get().getAuthResponse());
Embed that somewhere in a <script>...</script>in your HTML or in a .js file that is called otherwise. Must be after gapi.load(...). It'll stop the script if executed before gapi.load(...) is called.
To get a current user this has to be after the user is authenticated, of course. It does return an object if a user has not been authenticated however. If you are in Chrome, you can expand The Object in the developer tools console window to get a nice outline format of all the stuff in the auth response.
Note that currentUser is undefined prior to a successful authentication. Since this 'fails silently' you should use a conditional statement to verify either the sign in status or that a current user exists in your real code.
For completeness the object instantiation process in my app goes like this, at a high level:
1.) gapi.load(...) - After this gapi.client, gapi.auth2 and other objects are available.
2.) gapi.client.setApiKey("") would be called to 'clear' the api key if it had been set previously for some other purpose.
3.) gapi.auth2.init(...) - After this the auth instance is available via gapi.auth2.getAuthInstance .
4.) Then the login is kicked off using the .signIn() method of the auth instance. The auth instance would be instantiated with something like auth_instance = gapi.auth2.getAuthInstance(); If that's how you do it then the sign in would be auth_instance.signIn().
(...) - means there are several parameters needed.
I also found the Google tictactoe example useful as an example and a simple base for further work.
I hope this is helpful to someone!
you need to call the method
function handleAuthClick(event) {
gapi.auth.authorize({client_id: clientId, scope: scopes, immediate: false} handleAuthResult);
return false;
}
function makeApiCall() {
gapi.client.load('plus', 'v1', function () {
var request = gapi.client.plus.people.get({
'userId': 'me'
});
request.execute(function (resp) {
'method ajax with you application'
});
});
}
you can see what this do here

Categories

Resources