Authenticating Google Gmail API using access and refresh token - node.js

I have searched for an answer to this specifically but can't seem to find it. Apologies if it's a repeat.
I have the access token and the refresh token and want to send the emails using these tokens. The tokens are generated using the passport.js library and I am storing them in the DB along with other profile data. Like this
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_APP_CLIENT_ID,
clientSecret: process.env.GOOGLE_APP_CLIENT_SECRET,
callbackURL: process.env.GOOGLE_APP_CALLBACK_URL,
passReqToCallback: true
},(request:any,accessToken: string, refreshToken: string, profile: any, done: any)=>{
//save credentials in database
}));
Now using the access token and the refresh token I want to access the Gmail API. Contrary to the example (https://developers.google.com/gmail/api/quickstart/nodejs) provided by Google. In which they are providing the Oauth client created from executing Oauth2 flow.
What is needed is something like this
const gmail = google.gmail({version: 'v1', auth:{accessToken,refreshToken});

In summary: Pretty much what DalmTo said in his comment. You need to also add the client ID, secret and redirect URL.
Explanation: It may be a little hard to see but the sample that you provided does show how to authenticate with locally stored information. I'll try to simplify it.
The most relevant parts are under the authorize() function:
//first create a google.auth.OAuth2 object
//for this you still have to use your app's client_id, client_secret and redirect_uri
const oAuth2Client = new google.auth.OAuth2(
client_id, client_secret, redirect_uris[0]);
The sample app creates a token.json file that it uses to reauthenticate. It's just a simple object with the tokens (there are a couple additional fields which I removed since they are not relevant to this):
{
"access_token":"ya29...",
"refresh_token":"1//..."
}
Then the sample shows that you have to call setCredentials() on the oAuth2Client object using this token file that was created.
oAuth2Client.getToken(code, (err, token) => {
if (err) return console.error('Error retrieving access token', err);
oAuth2Client.setCredentials(token);
Then once you have set the credentials to the oAuth2Client object you can use it to authorize the API:
const gmail = google.gmail({version: 'v1', oAuth2Client});
The sample works with a locally stored file, but since you store the tokens in your database in your case it might look more like this:
//get the tokens from somewhere in your database
const tokens = {
"access_token" : ACCESS_TOKEN,
"refresh_token" : REFRESH_TOKEN
}
const oAuth2Client = new google.auth.OAuth2(
process.env.GOOGLE_APP_CLIENT_ID,
process.env.GOOGLE_APP_CLIENT_SECRET,
process.env.GOOGLE_APP_CALLBACK_URL
);
oAuth2Client.setCredentials(tokens);
const gmail = google.gmail({version: 'v1', oAuth2Client});
You'll probably also need to handle reauthorization in case that the refresh token has expired or the user has revoked access but I hope this gives you a general idea.
More info: Google API's Node.js Client

Related

How to create a GoogleStrategy stateless authentication using nodejs and passport.js?

I'm working on a project and I'm evaluating different way to authenticate my app. After trying Firebase and see that is not ok for me I decided to authenticate my app using passport.js.
I've created LocalStrategy, JwtStrategy, Google and Facebook Strategy but I am stuck on the Google one. What I am trying to do is get an access_token that will be passed back on my client and will be used in any future request.
I would like to create a stateless authentication because my client side will be a web app and in the future an Android app.
All the example that I found for GoogleStrategy is using passport.session() to store the logged in user in the session.
First question is: using passport.js for GoogleStrategy do I have to use mandatory the passport.session() ?
By docs GoogleStrategy return an accessToken and a refreshToken..but actually it doesn't!! The refreshToken is always undefined and the accessToken is something different from a jwt token, I thought it was the authorization_code that need to be exchanged with an access_token, but I'm not sure because if I try to exchange that token with an access token I receive an error. Any ideas of what accessToken on GoogleStrategy is?
Second question: is there any way to get jwt token from google? if no, assuming google strategy successful login... should I generate inside my GoogleStrategy my own token using jsonwebtoken library and pass it back on my client?
This is an example of what I'am saying:
passport.use(
new GoogleStrategy(
googleConfig,
async (accessToken, refreshToken, profile, done) => {
// accessToken..contains something different from a JWT token.
// refreshToken..is undefined!!
try {
const existingUser = await User.findOne({ "email": profile.emails[0].value});
if (existingUser) {
// 1. update providers adding the google if not present on user record
.....
// 2. generate access_token and refresh_token
const token = user.generateAccessToken();
const refreshToken = user.generateRefreshToken();
return done(null, {token, refreshToken});
}
// Create a new user on MongoDb.
// generate tokens and returns...
done(null, {....here goes generated tokens});
} catch (error) {
done(error, false);
}
}
)
);
// The generate token function inside my User model
conts jwt = require('jsonwebtoken');
userSchema.methods.generateAccessToken = function () { // this is just an example.
return jwt.sign({
id: this._id,
email: this.email
}, jwtSecret, { expiresIn: "15m" })
}
If you have any advice it will be appreciated.
Thank you all!

Can't get user google account gender and birthday fields with google People API. (Using OAuth2client)

I am implementing authentication with google in my mobile flutter app. I get the access_token in my app, and then I send it to backend which is written with Node.js. And thene I need to fetch user basic info + birthday and gender. In Google Cloud Platform console I did all configs, I added certain scopes,'https://www.googleapis.com/auth/user.birthday.read', 'https://www.googleapis.com/auth/user.gender.read',. I enabled Google People API. But I still can not get birthday and gender. Here is backend part from.
const token =
"HARDCODED_ACCESS_TOKEN";
var google = require("googleapis").google;
var OAuth2 = google.auth.OAuth2;
var oauth2Client = new OAuth2();
oauth2Client.setCredentials({
access_token: token,
scope: "https://www.googleapis.com/auth/user.gender.read",
});
var oauth2 = google.oauth2({
auth: oauth2Client,
version: "v2",
});
oauth2.userinfo.get(function (err, res) {
if (err) {
console.log(err);
} else {
console.log(res.data);
}
});
And here what I got in response.
I tried almost everything, but still couldn't get gender and birthday.
In order to get information about gender and birthdays from the authenticated user, you can call People API's people.get with resourceName=people/me and personFields=genders,birthdays:
oauth2Client.setCredentials({
access_token: token,
});
const service = google.people({version: 'v1', auth: oauth2Client});
service.people.get({
resourceName: 'people/me',
personFields: 'genders,birthdays'
}, (err, res) => {
// Do your thing
});
Notes:
You didn't provide the code for most of the authentication process, but please note that the scopes have to be provided before retrieving the access_token, since the access token depends on those scopes. Also, I'd suggest you to set a refresh_token, since the access_token will expire in an hour. For more information about the OAuth process, please take a look at the Node.js quickstart.
It is assumed that both genders and birthdays are added to the authenticated user's account.

Post To Twitter on Behalf of Another User using Node.js, express, twit, and passport-twitter

Using the Twitter API, I'm trying to post a tweet on behalf of a Twitter user.
I'm using node and the passport-twitter and twit modules.
Some resources:
I followed this video tutorial NodeJS and ExpressJS: Using Twitter
authentication via Passport.
Sourcecode for the tutorial
here.
Passport-twitter documentation
twit documentation
I successfully authenticated with passport-twitter following the tutorial above.
I also successfully posted using twit on my twitter developer account.
However, I'm having trouble combining these two things; trying to post to twitter on behalf of another user. For that, I need to get the user's access token and access token secret. Then, I need to make a post request to the Twitter API with that information.
I'm not sure where to put the post request in the passport-twitter code. I tried putting it in the second route which is the URL to which Twitter redirects the user after they have signed in.
app.get('/twitter/login', passport.authenticate('twitter'))
app.get('/twitter/return', passport.authenticate('twitter', {
failureRedirect: '/'
}), function(req, res) {
//Post using twit
//grab access token and access token secret from the request query
const access_token = req.query.oauth_token;
const access_token_secret = req.query.oauth_verifier;
//set the configurations with the access token and access token secret that we just got
const config = {
consumer_key: <consumer key here>,
consumer_secret: <consumer secret here>,
access_token,
access_token_secret,
timeout_ms: 60*1000,
strictSSL: true
}
//pass in the configurations
var T = new Twit(config);
//post
T.post('statuses/update', { status: 'hello world!' }, function(err, data, response) {
if (err)console.log("oops, didn't tweet: ", err.message);
})
res.redirect('/');
})
But I got an error: Invalid or expired token.
I expected it to work because the authentication worked.
This is my first time using OAuth so perhaps I misunderstand how this all works.
Where am I supposed to put the post request?
UPDATE:
I tried posting to my dev account, using my dev account's access token and secret. It worked. Which lead me to believe there is something wrong with the access token and secret for the user.
I think I know partially what’s going.
I assumed that the property oauth_verifier found in the request query object was the access token secret.
const access_token_secret = req.query.oauth_verifier;
But now I don’t think oauth_verifier is the same as the access token secret. oauth_verifier has less characters than my dev account’s access token secret. So it seems that the datatypes are different.
But now I’m trying to figure out where the access token secret is? There are only two properties in the request query object (req.query);
oauth_token
oauth_verifier
Where’s the access token secret for the user?
I solved my issue. It was in the documentation for passport-twitter all along. Man, I spent days on this issue.
The strategy also requires a verify callback, which receives the access token and corresponding secret as arguments, as well as profile which contains the authenticated user's Twitter profile.
-from passport-twitter readMe
In the example in the docs, you can see token and tokenSecret in the params.
passport.use(new TwitterStrategy({
consumerKey: TWITTER_CONSUMER_KEY,
consumerSecret: TWITTER_CONSUMER_SECRET,
callbackURL: "http://127.0.0.1:3000/auth/twitter/callback"
},
function(token, tokenSecret, profile, cb) {
User.findOrCreate({ twitterId: profile.id }, function (err, user) {
return cb(err, user);
});
}
));
I read this and saw this before. But assumed this was the the consumer key and consumer secret. I didn't realize it was what I was looking for: the access token and access secret.
So your post to twit would go something like this:
passport.use(new Strategy({
consumerKey: process.env.CONSUMER_KEY,
consumerSecret: process.env.CONSUMER_SECRET,
callbackURL: 'http://localhost:3000/twitter/return'
}, function(token, tokenSecret, profile, callback) {
const configs = createConfigs(token, tokenSecret);
// Post to twitter
var Twit = require('twit')
var T = new Twit({
consumer_key: '...', //get this from developer.twitter.com where your app info is
consumer_secret: '...', //get this from developer.twitter.com where your app info is
access_token: token,
access_token_secret: tokenSecret,
timeout_ms: 60*1000, // optional HTTP request timeout to apply to all requests.
strictSSL: true, // optional - requires SSL certificates to be valid.
})
//
// tweet 'hello world!'
//
T.post('statuses/update', { status: 'hello world!' }, function(err,
data, response) {
console.log(data)
})
return callback(null, profile);
}));

How can I use the Google OAuth2 ID token with node.js / passport.js to verify if the user is valid and authenticated?

On my front end, I'm using vue.js (not that that matters) and with the Google OAuth flow, I get back an id_token from:
let googleAuthIdToken = await this.auth2.currentUser
.get()
.getAuthResponse().id_token;
I want to then pass that token to my node.js server (Express / Passport) to verify that the user is allowed to login.
I want to use passport and send back a JWT to the front end in my response.
Can someone please guide me as to how to accomplish this?
It is easier to make use of a node module called googleapis, After installation, import the module.
import { google } from 'googleapis';
Then you need to create an OAuthClient by specifying the CLIENT_ID, CLIENT_SECRET, REDIRECT_URL.
const oauth2Client = new google.auth.OAuth2(
CLIENT_ID,
CLIENT_SECRET,
REDIRECT_URL,
);
Then you can get the token from google by using the oauth2Client.
const {tokens} = await oauth2Client.getToken(code);
oauth2Client.setCredentials(tokens);
Inorder to obtain the neccessary user information to store in your own database, You need to call this method.
const plus = google.plus({ version: 'v1', oauth2Client });
const me = await plus.people.get({ userId: 'me' });
me will contain the user information that you are looking for, once you obtain the user information you can then store it using passport js.

Getting list of Google Calendar events using googleapis

Following googleapis documentation I've retrieved tokens including refresh_token:
{ access_token: 'ya29.Glv_LONG-STRING',
token_type: 'Bearer',
refresh_token: '1/7k6BLAH-BLAH-BLAH',
expiry_date: 1532141656948 }
How can I get list of Google Calendar events using this when access_token is not valid anymore, but I have the refresh_token?
You have already retrieved the refresh token. You want to retrieve an event list using Calendar API by the refresh token. If my understanding is correct, how about this sample script? In this script, it supposes the following points.
You have the refresh token which can use Calendar API.
The refresh token includes https://www.googleapis.com/auth/calendar or https://www.googleapis.com/auth/calendar.readonly for retrieving the event list using Calendar API in the scopes.
Calendar API has already been enabled at API console.
When you run the script, if the error occurs, please confirm above points.
Sample script :
const {google} = require('googleapis');
const calendar = google.calendar('v3');
var oauth2Client = new google.auth.OAuth2(
"### client ID ###",
"### client secret ###",
);
oauth2Client.setCredentials({
refresh_token: "### refresh token ###",
// access_token: "#####" // If you want to use access token, please use this.
});
calendar.events.list({
auth: oauth2Client,
calendarId: 'primary',
}, function(err, res) {
if (err) {
console.log(err);
} else {
console.log(res.data);
}
});
Note :
When you use this sample script, please set client ID, client secret, refresh token and calendarId for your environment.
I confirmed that this sample script works with the version 32.0.0 of googleapis.
Reference :
Calendar API
google-api-nodejs-client
If I misunderstand your question, I'm sorry.

Resources