Meteor Google oAuth to refresh accessToken without re-login - meteor

This is my first post. I will try and be complete. I am creating a app in Meteor which uses google oAuth. I am using the following packages:
accounts-ui
accounts-google
service-configuration
google-config-ui
In my login.js I have:
Template.login.events({
'click #googleLoginButton': function() {
Meteor.loginWithGoogle(
{ requestPermissions: ['email', 'profile'],
requestOfflineToken: 'true'
}
);
}
});
I get the following in mongo:
db.users.find({}).pretty();
{
"_id" : "9TjGEjEj4ocFhwHtS",
"createdAt" : ISODate("2017-10-11T17:38:07.400Z"),
"services" : {
"google" : {
"accessToken" : "ya29.-REDACTED-T9z",
"idToken" : "eyJhbGcj-REDACTED-LTg",
"scope" : [
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/userinfo.profile"
],
"id" : "107113228066746203535",
"email" : "XXXXXXy#XXXX.com",
"verified_email" : true,
"name" : "XXX XXX",
"given_name" : "XXX",
"family_name" : "XXX",
"picture" : "https://lh4.googleusercontent.com/.../photo.jpg",
"locale" : "en",
"gender" : "male",
"refreshToken" : "1/HG-REDACTED-oLq0USutc"
},
"resume" : {
"loginTokens" : [
{
"when" : ISODate("2017-10-11T21:47:38.471Z"),
"hashedToken" : "2lmOK-REDACTED-ptAyDnWo="
}
]
}
},
"profile" : {
"name" : "XXX XXX"
}
}
In the Server/init.js I have: (I am passing the google project info via settings.json)
configureGoogle = function(config) {
ServiceConfiguration.configurations.remove({
service: "google"
});
ServiceConfiguration.configurations.insert({
service: "google",
clientId: googleConfig.clientId,
secret: googleConfig.secret
});
return;
};
if (googleConfig) {
console.log('Got settings for google', googleConfig)
configureGoogle(googleConfig);
}
So things seem to be working. I can login / out and when I login I get new tokens. I use the accessToken for api calls in other places. The problem I have is that after a hour the token expires and the API calls start to fail.
I would like to refresh the accessToken just before it expires and cant seem to figure out how. I would also like to force a logout after 24 hours so the token does not refresh forever.
Any assistance if appreciated.

Update #1
Thanks to Derek Brown below for pointing me in the right direction. That got me to looking for a meteor google api. I found this one: percolate:google-api
and according to it doc it does what I was looking for:
If the user's access token has expired, it will transparently call the exchangeRefreshToken method to get a new refresh token.
I then ran into a error where the expiresAt Didnt exist in my mongodb and things were not working. I then found this post: Google-API which suggested:
server/publish.js
Meteor.publish(null, function() {
return Meteor.users.find(this.userId, { fields: {
'services.google.accessToken': 1,
'services.google.expiresAt': 1
}});
});
I also made one change from code above:
client/login.js
Template.login.events({
'click #googleLoginButton': function() {
Meteor.loginWithGoogle(
{ requestPermissions: ['email', 'profile'],
requestOfflineToken: 'true',
forceApprovalPrompt: 'true' //<==== This is the change
}
);
}
});
This added with no additional code changed this added the expiresAt and populated it.
Im doing final testing now. I will update.

Related

Accounts can't connect to the users in my mongo DB user collections

I'm building a custom account system with meteor accounts package when i register a new user accounts is loggin him directly (a behavior that i does'nt asked) and meteor toys shows one user in collection (despite the others that i have added previously) this is because i didn't publish users. When i'm creating a new user with existing username it returns an error but also redirect and the user is logged in please i need help with this
// Meteor server side method for register
Meteor.methods({
'users.register'( data ) {
try {
user = Accounts.createUser({
username: data.username,
password: data.password,
});
return {
"userId": user
};
} catch (e) {
throw e;
}
},
});
// register call on register.js (client side register page)
Meteor.call('users.register', registerable, ( error ) => {
if( error ){
if( error.error == 403 ){
usernameExistMessage.style.display = "inline";
} else {
usernameExistMessage.style.display = "none";
}
}
});
// Users in the mongo collection
{ "_id" : "7re7XPXoxrs6oYckN", "createdAt" : ISODate("2019-01-18T16:51:31.742Z"), "services" : { "password" : { "bcrypt" : "$2a$10$8NZwd1gPMpJgMs8P47DF.uHWrvBLilTBcp6D0q6877HcDJOfJKaz6" }, "resume" : { "loginTokens" : [ ] } }, "username" : "test" }
{ "_id" : "96rc3NYXr35HNB6uZ", "createdAt" : ISODate("2019-01-18T16:52:07.421Z"), "services" : { "password" : { "bcrypt" : "$2a$10$Y3Bb6B/o3MwnFevNGpqtROMcH833qtHF.OEx6Qg5xbJrwXRU2u.2q" }, "resume" : { "loginTokens" : [ ] } }, "username" : "username" }
User auto logged in after creation of a new account even if the user already exist in mongo user collection
My meteor package file
meteor-base#1.4.0 # Packages every Meteor app needs to have
mobile-experience#1.0.5 # Packages for a great mobile UX
mongo#1.6.0 # The database Meteor supports right now
blaze-html-templates#1.0.4 # Compile .html files into Meteor Blaze views
reactive-var#1.0.11 # Reactive variable for tracker
tracker#1.2.0 # Meteor's client-side reactive programming library
standard-minifier-css#1.5.2 # CSS minifier run for production mode
standard-minifier-js#2.4.0 # JS minifier run for production mode
es5-shim#4.8.0 # ECMAScript 5 compatibility for older browsers
ecmascript#0.12.4 # Enable ECMAScript2015+ syntax in app code
shell-server#0.4.0 # Server-side component of the `meteor shell` command
accounts-password
kadira:flow-router
kadira:blaze-layout
fastclick
less#2.8.0 # Leaner CSS language
aldeed:simple-schema
aldeed:collection2
alanning:roles
meteortoys:allthings
I solved my issue there was some wrong imports and a template events bad handling

How to access Meteor.users() fields from the client?

I want to access users emails in from the client,
here is what I have done, in the server side:
Meteor.publish('userList', function (){
return Meteor.users.find({}, {fields:{emails: 1, profile: 1}});
});
In the client side:
Template.usersManagement.onCreated(function () {
var self = this;
self.autorun(function() {
self.subscribe('userList');
});
});
And the template helper to retrieve the users:
Template.usersManagement.helpers({
allUsers: function() {
console.log(Meteor.users.find({}).fetch())
return Meteor.users.find({}).fetch()
}
})
In the usersManagement template:
{{#each allUsers}}
<h1>{{this.emails.address}}</h1>
<h1>{{this.profile.name}}</h1>
{{/each}}
The users names are displayed but the email isn't and no errors showing in the console.
Here is the look on how the users stored in the database:
{
"_id" : "m7admvZc32Jr3SeiE",
"createdAt" : ISODate("2017-12-27T21:24:48.927Z"),
"services" : {
"password" : {
"bcrypt" : "$2a$10$wv6KsRp6s91A.0mHH89Q0eT3jrZmJjKJhw8SIH9c8c8OpwMrXyGMC"
}
},
"emails" : [
{
"address" : "222#222.com",
"verified" : false
}
],
"profile" : {
"name" : "222",
"createdAt" : ISODate("2017-12-27T21:24:48.707Z"),
"updatedAt" : ISODate("2017-12-27T21:24:48.707Z"),
"group" : "admin"
},
"status" : {
"online" : false
}
}
My question is, how can I retrieve the emails of the users?
EDIT
Here is how the emails are retrieved (from the console):
There is another field (0) below the emails. I tried this.emails.0.address it didn't work (desperate attempt)
Your desperate attempt almost nailed it, try this:
this.emails.[0].address
An item in the Meteor.users collection store emails as an array, for you to be able to store more than one email-address per user. So since it is so, the right way would be:
<h1>{{this.emails[0].address}}</h1>

Look up / join data with AngularFire

I've got a question on how to combine data from different Firebase database nodes before displaying it on the frontend. I've got a Firebase database with the following structure. (I'm new to a nosql setup, so this looks more relational):
{
"agents" : {
"-KPCmnwzjd8CeSdrU3As" : {
"contactNumber" : "12345",
"name" : "aaa"
},
"-KPCmw6dKuopDlsMVOlU" : {
"contactNumber" : "123",
"name" : "bbb"
},
"-KPCoWcLecpchcFV-vh_" : {
"contactNumber" : "123",
"name" : "ccc"
},
"-KPROMhPatLjVxMdvfLf" : {
"contactNumber" : "256342",
"name" : "blah"
},
"-KPWIFl5qp5FvAeC3YhG" : {
"contactNumber" : "123",
"name" : "eee"
}
},
"listings" : {
"-KPWKTvW3GzFEIT2hUNU" : {
"agent" : "-KPCoWcLecpchcFV-vh_",
"description" : "third",
"reference" : "REF1"
}
}
}
I'm using Firebase SDK 3.2.0 and AngularFire 2.0.1. In my Angular app I am able to get the list of listings, and for each one look up the agent information. The reason I'm not storing the agent info with the listing is I want the ability to update the agent and the change should reflect on all listings. I don't want to have to go and update all listings if the agent telephone number changes (as an example).
In my controller I have the following:
// get the listings
var listingsRef = firebase.database().ref().child('listings');
vm.listings = $firebaseArray(listingsRef);
// this will move to my ui-router as a resolve but for simplicity's sake
// I added it here...
vm.listings.$loaded().then(function(data){
// loop through the listings...
data.forEach(function(listing) {
if (listing.agent) {
// get the agent for the listing
listing.agent = AgentFactory.getAgent(listing.agent);
}
});
});
Right now the data is displaying correctly on the frontend. There is a slight delay with the agent data showing because of the need of the getAgent promise to resolve.
My questions are:
Is this the correct way of getting the agent data? Should I be looping through the listings and for each query the agent data? How do I wait / keep track of all of the getAgents to resolve?
Any help would be appreciated.
Thanks.
I've structured my data similarly. If you want to wait for all the getAgents to resolve you can use $q.all. I'm not entirely sure what your AgentFactory.getAgent is returning, but let's assume it's a $firebaseObject. If that's the case inject $q and then do the following:
vm.listings.$loaded().then(function (data) {
// loop through the listings...
var promises = [];
data.forEach(function (listing) {
if (listing.agent) {
// get the agent for the listing
listing.agent = AgentFactory.getAgent(listing.agent);
promises.push(listing.agent.$loaded());
}
});
return $q.all(promises);
}).then(function (agents) {
//all agents are loaded
});

Accessing the Youtube API using Meteor and percolate's google-api package

I've just started my first meteor project and I'm trying to access youtube's developer API.
I've created developer credentials with google, and I've included accounts-ui and percolate's google-api packages. I can successfully login with accounts-ui which means it appears my OAuth settings are working.
I then try to run something like this on the client:
GoogleApi.get('youtube/v3/search',{
part : 'snippet',
q : 'cats',
maxResults : 25
},
function(err,data) {
!err ? console.log(data) : console.log(err);
});
And I get the following error on the console:
Error: failed [403] { "error": { "errors": [ { "domain": "global", "reason": "insufficientPermissions", "message": "Insufficient Permission" } ], "code": 403, "message": "Insufficient Permission" } }
I'm not sure if I'm calling the function incorrectly, as I can't seem to find any usage examples of the GoogleApi.get() function (and I'm a meteor beginner), or whether my developer account is not properly setup, or what.
Any help or pointers you can pass along is greatly appreciated. Thanks!
Update:
Using FullStack's suggestion below, my final code ended up looking like this:
var url = "https://www.googleapis.com/youtube/v3/search";
var params = {
key: {Google API Key}
part: "snippet",
q: searchTerm,
maxResults: 25
};
Meteor.http.get(url, {params: params}, function (err, result) {
console.log(result.statusCode, result.data);
var retdata = result.data;
Session.set("youtubeSearchItems", retdata.items);
});
I recommend not using the google-api package and just doing the HTTP call yourself. Below is example code:
var url = "https://www.googleapis.com/youtube/v3/search";
var options = {
'headers' : {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + yourAccessToken,
'X-JavaScript-User-Agent': "Google APIs Explorer"
},
'params' : {
part : 'snippet',
q : 'cats',
maxResults : 25
}
};
var searchResult = HTTP.get(url, options);
Make sure you have the HTTP package installed: meteor add http
See official Meteor docs on HTTP for full details.
Percolate's package works just fine. The issue is that you have insufficient permissions as it states. To fix this, you have to add the scopes that are necessary for the APIs you wish to use.
if (Meteor.isClient){
var scopes = [
'https://www.googleapis.com/auth/youtube',
];
Accounts.ui.config({
requestPermissions: {google: scopes}
});
}
I didn't know which YouTube API you were referring to so I went with the Data one. There's also Analytics and Live Streaming. I got the above scope from this page:
https://developers.google.com/youtube/v3/guides/auth/client-side-web-apps

How to get Google+ profile with Meteor.loginWithGoogle?

I'm looking for a working example of Meteor.loginWithGoogle (with meteor 0.6.4.1).
I found this one for loginWithGitHub (https://www.eventedmind.com/posts/meteor-customizing-login) that works fine with GitHub.
It works fine without parameters as show here on client side :
Template.user_loggedout.events({
"click #login": function(e, tmpl){
Meteor.loginWithGoogle({
}, function (err) {
if(err) {
//error handling
alert('error : '+err.message);
} else {
}
});
}
});
with the Accounts params on server side :
Accounts.loginServiceConfiguration.remove({
service: 'google'
});
Accounts.loginServiceConfiguration.insert({
service: 'google',
clientId: 'XXXXXX',
secret: 'YYYYYY'
});
In this case how can i get currentUser information especially the mail ?
Is there a way to get the Google+ profile of the user (if he has one and allows this), the user's avatar for example ?
What are the needed parameters for requestPermissions: , what can i get with this ?
Thanks
After some research i build my own example available here : https://github.com/lc3t35/googlelogin
Thanks to :
https://github.com/m2web/githublogin
https://github.com/ananta-IO/marq
Meteor/MongoDB see available fields for publish?
https://github.com/mrtnbroder/meteor-snippets/blob/master/snippets/js/Accounts/loginWithGoogle.sublime-snippet
https://developers.google.com/accounts/docs/OAuth2Login#obtaininguserprofileinformation

Resources