Google Drive SDK OAuth2 with multiple user account - javascript

If a user has several accounts in their Google, the user has to choose an account as it doesn't remember which account the user chose previously.
I have managed to make an OAuth2 authentication using these codes and the configurations guided from https://developers.google.com/api-client-library/javascript/features/authentication#specifying-your-client-id-and-scopes
this.authenticate = function(){
gapi.load('client:auth2',authorize);
}
function authorize(){
gapi.client.setApiKey(API_KEY);
gapi.auth2.init({
client_id: CLIENT_ID,
scope: SCOPES
}).then(function(authResult){
var auth2 = gapi.auth2.getAuthInstance();
auth2.signIn().then(afterSignIn);
});
}
function afterSignIn(){
console.log('authenticated successfully');
$rootScope.authenticated = true;
gapi.client.load('drive', 'v3',function(){
$rootScope.$broadcast('authenticated');
});
}
I have tried these options of GoogleAuth.signIn():
auth2.signIn({
'prompt': '**promtOption**'
})...
none : it doesn't authenticate
login: does the same as select_account
consent: does the same as select_account, it additionally asks for offline use permission...
select_account: same problem as signing in without options
How can I make the program remember the user selection?

Calling auth2.signIn() will always prompt the user to sign in, even if they are already signed in. Before doing that, check to see if they are already signed in using auth2.currentUser.get().isSignedIn(). Here's a modified version of your code:
function authorize(){
gapi.client.setApiKey(API_KEY);
gapi.auth2.init({
client_id: CLIENT_ID,
scope: SCOPES
}).then(function(authResult){
var auth2 = gapi.auth2.getAuthInstance();
var user = auth2.currentUser.get();
if (user.isSignedIn()) {
afterSignIn();
} else {
auth2.signIn().then(afterSignIn);
}
});
}

Related

How to get Profile info from Google Signin with redirect mode (no-popup)?

Here's how I do it, after getting the signin's client file :
// HTML
<script type='text/javascript' src="https://apis.google.com/js/api:client.js" async defer></script>
I called gapi.load() function into a HTML button
// load the startApp function after the page loads
jQuery(function () {
$(window).load(function () {
startApp()
})
})
var startApp = function () {
gapi.load('auth2', function () {
// Retrieve the singleton for the GoogleAuth library and set up the client.
auth2 = gapi.auth2.init({
client_id: 'xxxxxxxx-xxxxxxxxxx.apps.googleusercontent.com',
cookiepolicy: 'single_host_origin',
ux_mode: 'redirect', // I don't want it to display a pop-up
scope: 'profile email' // I just need to get user's name, profile picture and email address
});
// attach this function into a button element with id = "customBtn"
attachSignin(document.getElementById('customBtn'));
});
};
function attachSignin(element) {
auth2.attachClickHandler(element, {},
function (googleUser) {
// it never calls this block of code.
// this never runs
console.log(googleUser.getBasicProfile().getName())
var gProfile = googleUser.getBasicProfile();
var name = gProfile.getName();
var email = gProfile.getEmail();
var imgUrl = gProfile.getImageUrl();
}, function (error) {
return alert("Google Sign in error!")
});
}
It load the necessary functions into a button. If user click on that button, user will be redirected into Google's signin page. After user manages to sign in then Google will redirect the URL back into my website.
It should also send the user's profile info into my attachClickHandler() function within the attachSignin(). But it never happens since it reloads the page before the handler function gets called.
It only works if I change the ux_mode: 'redirect' into default' popup
The best I can do right now is just to get the email address from the token_id parameter that Google give in URL after signin. The id_token field from the URL is a jwt that can be decoded
http://localhost:3006/login#scope=email%20profile%20https://www.googleapis.com/auth/userinfo.email%20https://www.googleapis.com/auth/userinfo.profile%20openid&id_token=xxxxxxxxx&client_id=xxxxxxxxxxxx-xxxxxx.apps.googleusercontent.com
So How to get the user's profile information with ux_mode set to redirect ?
I modified your code so it works:
var startApp = function () {
gapi.load('auth2', function () {
// Retrieve the singleton for the GoogleAuth library and set up the client.
auth2 = gapi.auth2.init({
client_id: 'xxxxxxxx-xxxxxxxxxx.apps.googleusercontent.com',
cookiepolicy: 'single_host_origin',
ux_mode: 'redirect', // I don't want it to display a pop-up
scope: 'profile email' // I just need to get user's name, profile picture and email address
});
// attach this function into a button element with id = "customBtn"
attachSignin(document.getElementById('customBtn'));
// START NEW CODE
auth2.currentUser.listen(function(googleUser) {
if (googleUser && (gProfile = googleUser.getBasicProfile())) {
var name = gProfile.getName();
var email = gProfile.getEmail();
var imgUrl = gProfile.getImageUrl();
console.log({name, email, imgUrl});
}
});
// END NEW CODE
});
};
// Can remove callbacks if not using pop-up
function attachSignin(element) {
auth2.attachClickHandler(element, {});
}
Explanation:
When using redirect instead of pop-up, listen on currentUser instead of the attachClickHandler() callbacks. The Google API will detect and consume the redirect parameters, firing the currentUser.listen handler.
Sources:
https://github.com/google/google-api-javascript-client/issues/477#issuecomment-430299619
https://developers.google.com/identity/sign-in/web/listeners

Google Sheets API throwing 401 error while append rows to spreadsheet without login

Objective: Submit a form and store data to google spreadsheet
documentation: link
What I've done so far:
var CLIENT_ID = 'my_client_id.apps.googleusercontent.com';
var API_KEY = 'MY_API_KEY';
var DISCOVERY_DOCS = ["https://sheets.googleapis.com/$discovery/rest?version=v4"];
var SCOPES = "https://www.googleapis.com/auth/spreadsheets";
var authorizeButton = document.getElementById('authorize_button');
var signoutButton = document.getElementById('signout_button');
function handleClientLoad() {
gapi.load('client:auth2', initClient);
}
function initClient() {
gapi.client.init({
apiKey: API_KEY,
clientId: CLIENT_ID,
discoveryDocs: DISCOVERY_DOCS,
scope: SCOPES
})
function updateSigninStatus(isSignedIn) {
if (isSignedIn) {
//authorizeButton.style.display = 'none';
//signoutButton.style.display = 'block';
//listMajors();
} else {
//authorizeButton.style.display = 'block';
//signoutButton.style.display = 'none';
}
}
function appendPre(message) {
var pre = document.getElementById('content');
var textContent = document.createTextNode(message + '\n');
pre.appendChild(textContent);
}
function update_docs(data) {
var params = {
spreadsheetId: '1YXMlr_-I45AWM2b9QnLkuLQoI6dq6wEuVOcttOMv9hU',
range: 'A:I', // TODO: Update placeholder value.
valueInputOption: 'RAW',
insertDataOption: 'INSERT_ROWS',
};
var valueRangeBody = {
"range": 'A:I', // 9 cols
"majorDimension": 'ROWS',
"values": [
[
data[0].value,//nom,
data[1].value,//prenom,
data[2].value,//email,
data[3].value,//user_phone,
data[4].value,//company_name,
data[5].value,//user_type,
data[6].value,//account_name,
data[7].value,//password,
data[8].value,//comptes_sources,
]
]
};
var request = gapi.client.sheets.spreadsheets.values.append(params, valueRangeBody);
request.then(function(response) {
console.log(response.result);
}, function(reason) {
console.error('error: ' + reason.result.error.message);
});
}
I can successfully append rows to the spreadsheet if I'm logged in to my google account.
Question: Can I append row without logging in(if yes please provide some docs/code)?
Because if I submit the form from a private window it throws 401 error.
error message: error: Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential.
I think before you start working on this you need to understand a few things.
There is a difference between private and public data.
Public data, Searching publicly uploaded youtube videos
Private data, My person gmail account, drive account, calendar account.
Even setting the sheet to public will not help you as with it public using an api key you will only be allowed to read the sheet not update it.
Answer: No you can not append a row without the application being authenticated and having access to the data.
Assuming that this is a sheet that you personally own you could set up a service account authenticate and grant the service account access to the sheet it will then be able to make the changes for you without you having to login. However this depends upon how your application works and what language you are using. I dont think that javascript supports service account authentication.

Google Calendar API getting 'Cannot read property 'signIn' of null'

I'm trying to use Google's Calendar API to get info on specific calendar. I keep getting this error Cannot read property 'signIn' of null. I'm using the example online here https://developers.google.com/google-apps/calendar/quickstart/js.
Any idea why this is happening? And can someone give me a more simple way to get calendar data? Basically I want to know which rooms in my calendar feed are free to book. Can I do this using the calendar API?
const CLIENT_ID = '418414126140-u5u3r2ampke8egh7d2tk50ferkl8ri26.apps.googleusercontent.com';
const SCOPES = ["https://www.googleapis.com/auth/calendar.readonly"];
app.factory('getCalendar', ['$q', function($q){
return function checkAuth() {
var deferred = $q.defer();
function handleClientLoad() {
gapi.load('client:auth2', initClient);
}
handleClientLoad();
function initClient() {
gapi.client.init({
"client_id": CLIENT_ID,
"scope": SCOPES
}).then(function () {
gapi.auth2.getAuthInstance().signIn();
});
}
function updateSigninStatus(isSignedIn) {
if (isSignedIn) {
console.log("You are authorized");
listUpcomingEvents();
} else {
console.log("You are not authorized");
}
}
function listUpcomingEvents() {
gapi.client.calendar.events.list({
'calendarId': 'primary',
'timeMin': (new Date()).toISOString(),
'showDeleted': false,
'singleEvents': true,
'maxResults': 10,
'orderBy': 'startTime'
}).then(function(response) {
console.log(response);
deferred.resolve(response);
});
}
return deferred.promise;
} //return ends here
}]);
Try using gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus); instead of gapi.auth2.getAuthInstance().signIn();. You may refer with this sample code on how to use the Google JavaScript Client API Library to authenticate and interact with the API.
Also check this related SO thread and see if it helps.
UPDATE:
Any idea on how to check which rooms on your calendar are available or not?
You may check on this link: Google Calendar Api, Is meeting Room available?
You will need to be authenticated as a user that has read-access to the room. Then you can take the resourceEmail field of the Calendar Resource API and use it as a calendarId in the events.list() Calendar API call.

Persistent Firebase OAuth Authentication

I'm trying to persist the Firebase user authentication state across multiple pages.
$scope.loginGoogle = function() {
console.log("Got into google login");
ref.authWithOAuthPopup("google", function(error, authData) {
$scope.loggedIn = true;
$scope.uniqueid = authData.google.displayName;
}, {
remember: "sessionOnly",
scope: "email"
});
};
function checkLogin() {
ref.onAuth(function(authData) {
if (authData) {
// user authenticated with Firebase
console.log("User ID: " + authData.uid + ", Provider: " + authData.provider);
} else {
console.log("Nope, user is not logged in.");
}
});
};
However, when the checkLogin function is called in another page, authData is not defined, even though the user has logged in on the login page. What seems to be the matter?
There are two things to know here.
First, you're using the JS Client auth methods in conjunction with AngularFire. While this is not a bad thing, you need to be aware of a few gotchas.
Second, you can use the $firebaseAuth module in AngularFire 0.9 to not deal with all of the crazy stuff below.
When using Firebase JS client level functions, Angular will not always pick up on them due to its digest loop. This is true for any external JS library. The way to get around this is to use the $timeout service.
CodePen
// inject the $timeout service
app.controller("fluttrCtrl", function($scope, $firebase, $timeout) {
var url = "https://crowdfluttr.firebaseio.com/";
var ref = new Firebase(url);
$scope.loginGoogle = function() {
console.log("Got into google login");
ref.authWithOAuthPopup("google", function(error, authData) {
// wrap this in a timeout to allow angular to display it on the next digest loop
$timeout(function() {
$scope.loggedIn = true;
$scope.uniqueid = authData.google.displayName;
});
}, {
remember: "sessionOnly",
scope: "email"
});
});
});
By wrapping the $scope properties in the $timeout another cycle will be run and it will display on the page.
Ideally, you don't want to deal with this yourself. Use $firebaseAuth module built into AngularFire. You need to upgrade to the 0.9 version to use the module.

Google app engine authorization returning null

I have the following code, which I am using successfully in another program
index.html
Eat Fresh
</html>
init.js
function init() {
CLIENT_ID = "927885761089.apps.googleusercontent.com"
SCOPES = ["https://www.googleapis.com/auth/userinfo.email"]
gapi.client.load('oauth2', 'v2', signin)
}
function signin() {
gapi.auth.authorize({client_id: CLIENT_ID, scope: SCOPES, immediate: true}, userAuthed)
}
function userAuthed() {
console.log(gapi.auth.getToken())
console.log(gapi.auth.getToken().accessToken)
gapi.client.oauth2.userinfo.get().execute(function(resp) {
checkEmail(resp)
})
}
function checkEmail(user) {
var validEmail = (whiteList.indexOf(user.email) !== -1)
if (!user.code && validEmail) {
startApp()
} else {
displayError(user.email)
}
}
gapi.auth.getToken() is returning null and to my knowledge that means that my I am not logged in. I don't know how to refresh the login or force a log out. Any help would be appreciated.
Changing the the following
gapi.auth.authorize({client_id: CLIENT_ID, scope: SCOPES, immediate: false}, userAuthed)
will prompt the user to give permission to the app, thus allowing for an authorized access token and for the remainder of the code to work.

Categories

Resources