Managing tokens a shared google account for file storage - node.js

I am creating an application in Node.js, hosted by Heroku. Using passport.js, I have already implemented sign-in authentication with a google account, integrated with a mySQL database.
In part of the application, I ask the applicant to upload a set of files. The way I would like to handle the uploading is through google drive APIs. Essentially, the user will be able to select files, which the back-end would then upload to a single-account google drive. Note: This will be a separate account from any of the applicant's accounts.
While I understand the process of uploading and retrieving the files, I am still unsure how the tokens work. From my research online I know:
Access tokens expire after a certain time
Refresh Tokens do not expire and are to be stored in the database
My question is how to manage these tokens in the backend. My current plan is to use the google oauth playground to get the access tokens for the app under the shared account. Then, every time I need to upload or access a file, I get a new access token using the refresh token, and then use that access token to do my API calls.
However, after doing some implementation testing, I have some confusion. I went through the Google Node.js Quickstart Guide and then modified the code to do a file-upload instead of a file reader. The modified code is below:
function fileUpload(auth) {
var drive = google.drive({version: 'v2'});
drive.files.insert({
auth: auth,
resource: {
title:'Test',
mimeType: 'text/plain'
},
media: {
mimeType: 'text/plain',
body: 'TEST'
}
}, function(err, response) {
if (err) {
console.log('The API returned an error ' + err);
return;
} else {
console.log('Inserted')
}
});
}
From my understanding, after the access token is expired, you cannot use it anymore. However, after I ran the code after the access token expired, it was still able to complete the process. Further, the access token did not change either. Hence, my confusion is how to manage these access tokens, specifically if I need to worry about access tokens expiring, or if they are valid once they are used once.

Related

Is there is a way to grant my nodejs application access to my google calendar?

so I have a nodejs application that sends invites to an event in google calendar to some people, for now I am using my gmail account and OAuth Playground to get a temporary Access token and it works, but the access token is just available for minutes and each time I need to refresh the Access token manually and give access to my google account calendar and this is the problem, now I want to make something dynamic without me interfering in the process.
This application is hosted in wix.
any suggestions ?
Thanks
IF you are only letting them access a calendar that you own and control then you can use a service account.
let google = require('googleapis');
let privatekey = require("./privatekey.json");
  Now let’s use the private key to configure a JWT auth client and authenticate our request.
// configure a JWT auth client
let jwtClient = new google.auth.JWT(
privatekey.client_email,
null,
privatekey.private_key,
['https://www.googleapis.com/auth/calendar']);
//authenticate request
jwtClient.authorize(function (err, tokens) {
if (err) {
console.log(err);
return;
} else {
console.log("Successfully connected!");
}
});
Create service account credentials in google developer console. then take the service account email address and share the calendar with it via the google calendar website. The rest of the code you have should be the same just swap out the auth section with this.
To access the calendar API please follow the Quickstart
The code provided creates a refresh token that will automatically generate a new access token for you, whenever the old one expires.
Be careful with unnecessary using service accounts, especially for adding invitees to a calendar event - there are currently issues with this feature.

Google service account: The API returned an error: TypeError: source.hasOwnProperty is not a function after an hour

I have added google cloud service account in a project and its working. But the problem is that after an hour(i think), I get this error:
The API returned an error: TypeError: source.hasOwnProperty is not a function
Internal Server Error
and I need to restart the application to make it work.
Here in this StackOverflow post, I found this:
Once you get an access token it is treated in the same way - and is
expected to expire after 1 hour, at which time a new access token will
need to be requested, which for a service account means creating and
signing a new assertion.
but didn't help.
I'm using Node js and amazon secret service:
the code I have used to authorize:
const jwtClient = new google.auth.JWT(
client_email,
null,
private_key,
scopes
);
jwtClient.authorize((authErr) =>{
if(authErr){
const deferred = q.defer();
deferred.reject(new Error('Google drive authentication error, !'));
}
});
Any idea?
hint: Is there any policy in AWS secret to access a secret or in google cloud to access a service account? for example access in local or online?
[NOTE: You are using a service account to access Google Drive. A service account will have its own Google Drive. Is this your intention or is your goal to share your Google Drive with the service account?]
Is there any policy in AWS secret to access a secret or in google
cloud to access a service account? for example access in local or
online?
I am not sure what you are asking. AWS has IAM policies to control secret management. Since you are able to create a Signed JWT from stored secrets, I will assume that this is not an issue. Google does not have policies regarding accessing service accounts - if you have the service account JSON key material, you can do whatever the service account is authorized to do until the service account is deleted, modified, etc.
Now on to the real issue.
Your Signed JWT has expired and you need to create a new one. You need to track the lifetime of tokens that you create and recreate/refresh the tokens before they expire. The default expiration in Google's world is 3,600 seconds. Since you are creating your own token, there is no "wrapper" code around your token to handle expiration.
The error that you are getting is caused by a code crash. Since you did not include your code, I cannot tell you where. However, the solution is to catch errors so that expiration exceptions can be managed.
I recommend instead of creating the Google Drive Client using a Signed JWT that you create the client with a service account. Token expiration and refresh will be managed for you.
Very few Google services still support Signed JWTs (which your code is using). You should switch to using service accounts, which start off with a Signed JWT and then exchange that for an OAuth 2.0 Access Token internally.
There are several libraries that you can use. Either of the following will provide the features that you should be using instead of crafting your own Signed JWTs.
https://github.com/googleapis/google-auth-library-nodejs
https://github.com/googleapis/google-api-nodejs-client
The following code is an "example" and is not meant to be tested and debugged. Change the scopes in this example to match what you require. Remove the section where I load a service-account.json file and replace with your AWS Secrets code. Fill out the code with your required functionality. If you have a problem, create a new question with the code that you wrote and detailed error messages.
const {GoogleAuth} = require('google-auth-library');
const {google} = require('googleapis');
const key = require('service-account.json');
/**
* Instead of specifying the type of client you'd like to use (JWT, OAuth2, etc)
* this library will automatically choose the right client based on the environment.
*/
async function main() {
const auth = new GoogleAuth({
credentials: {
client_email: key.client_email,
private_key: key.private_key,
},
scopes: 'https://www.googleapis.com/auth/drive.metadata.readonly'
});
const drive = google.drive('v3');
// List Drive files.
drive.files.list({ auth: auth }, (listErr, resp) => {
if (listErr) {
console.log(listErr);
return;
}
resp.data.files.forEach((file) => {
console.log(`${file.name} (${file.mimeType})`);
});
});
}
main()

GCP Consume a REST API after OAuth in Node.js

I am working to implement a Node.js webapp to be deployed on GCP App Engine.
Following the Node.js Bookshelf App sample, I did manage to implement a basic user authentication flow using the passport-google-oauth20 and retrieve basic profile information. I basically just got rid of what was not needed for my purposes
My custom code is available at: gist.github.com/vdenotaris/3a6dcd713e4c3ee3a973aa00cf0a45b0.
However, I would now like to consume a GCP Cloud Storage API to retrieve all the storage objects within a given buckets with the logged identity.
This should be possible by:
adding a proper scope for the request.
authenticating the REST requests using the user session token obtained via OAuth.
About the post-auth handler, the documentation says:
After you obtain credentials, you can store information about the
user. Passport.js automatically serializes the user to the session.
After the user’s information is in the session, you can make a couple
of middleware functions to make it easier to work with authentication.
// Middleware that requires the user to be logged in. If the user is not logged
// in, it will redirect the user to authorize the application and then return
// them to the original URL they requested.
function authRequired (req, res, next) {
if (!req.user) {
req.session.oauth2return = req.originalUrl;
return res.redirect('/auth/login');
}
next();
}
// Middleware that exposes the user's profile as well as login/logout URLs to
// any templates. These are available as `profile`, `login`, and `logout`.
function addTemplateVariables (req, res, next) {
res.locals.profile = req.user;
res.locals.login = `/auth/login?return=${encodeURIComponent(req.originalUrl)}`;
res.locals.logout = `/auth/logout?return=${encodeURIComponent(req.originalUrl)}`;
next();
}
But I do not see where the token is stored, how can I retrieve it and how to use it to consume a web-service (in my case, GCP storage).
I am not at all a node.js expert, so it would be nice having a bit more clarity on that: could someone explain me how to proceed in consuming a REST API using the logged user credentials (thus IAM/ACL privileges)?
If you want to access Cloud Storage through the use of a token obtained with OAuth, when the application requires user data, it will prompt a consent screen, asking for the user to authorize the app to get some of their data. If the user approves, an access token is generated, which can be attached to the user's request. This is better explained here.
If you plan to run your application in Google App Engine, there will be a service account prepared with the necessary authentication information, so no further setup is required. You may need to generate the service account credentials (generally in JSON format), that have to be added to the GOOGLE_APPLICATION_CREDENTIALS environment variable in gcloud.
Here is an example of how to authenticate and consume a REST API with the token that was obtained in the previous step. This, for example, would be a request to list objects stored in a bucket:
GET /storage/v1/b/example-bucket/o HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer [YOUR_TOKEN]

Can I use chrome.identity with Firebase custom authentication?

I'm building a Chrome extension and would like to use Firebase to persist state shared between users. Firebase authentication doesn't work within Chrome extension because there's no origin domain. The chrome.identity API can be used to ensure that the user is authenticated and to get the access token for OAuth requests.
A couple of considerations:
Use chrome.storage to store a token and use that to authenticate with Firebase. The storage area is not encrypted, so it would be trivial to read a user's token from their disk.
I assume the token returned by chrome.identity.getAuthToken is an OAuth access token and therefore transient - it wouldn't be suitable for a permanent unique identifier for a user.
I could make a request to a Google OAuth API to exchange the access token for the user's profile (https://www.googleapis.com/userinfo/v2/me), which contains an id field, but this is public.
I came across this question on my quest to solve a similar problem. I am sure the question is outdated but maybe my solution helps someone else stumbling over this question.
It is indeed possible to use chrome.identity for Firebase authentication... But the way is not through the custom authentication method. There is another method which accepts the OAuth2 token from chrome.identity.getAuthToken.
Here is everything I did following this tutorial:
(It also mentions a solution for non-Google auth providers that I didn't try)
Identity Permission
First you need permission to use the chrome identity API. You get it by adding this to your manifest.json:
{
...
"permissions": [
"identity"
],
...
}
Consistent Application ID
You need your application ID consistent during development to use the OAuth process. To accomplish that, you need to copy the key in an installed version of your manifest.json.
To get a suitable key value, first install your extension from a .crx file (you may need to upload your extension or package it manually). Then, in your user data directory (on macOS it is ~/Library/Application\ Support/Google/Chrome), look in the file Default/Extensions/EXTENSION_ID/EXTENSION_VERSION/manifest.json. You will see the key value filled in there.
{
...
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgFbIrnF3oWbqomZh8CHzkTE9MxD/4tVmCTJ3JYSzYhtVnX7tVAbXZRRPuYLavIFaS15tojlRNRhfOdvyTXew+RaSJjOIzdo30byBU3C4mJAtRtSjb+U9fAsJxStVpXvdQrYNNFCCx/85T6oJX3qDsYexFCs/9doGqzhCc5RvN+W4jbQlfz7n+TiT8TtPBKrQWGLYjbEdNpPnvnorJBMys/yob82cglpqbWI36sTSGwQxjgQbp3b4mnQ2R0gzOcY41cMOw8JqSl6aXdYfHBTLxCy+gz9RCQYNUhDewxE1DeoEgAh21956oKJ8Sn7FacyMyNcnWvNhlMzPtr/0RUK7nQIDAQAB",
...
}
Copy this line to your source manifest.json.
Register your Extension with Google Cloud APIs
You need to register your app in the Google APIs Console to get the client ID:
Search for the API you what to use and make sure it is activated in your project. In my case Cloud Firestore API.
Go to the API Access navigation menu item and click on the Create an OAuth 2.0 client ID... blue button.
Select Chrome Application and enter your application ID (same ID displayed in the extensions management page).
Put this client ID in your manifest.json. You only need the userinfo.email scope.
{
...
"oauth2": {
"client_id": "171239695530-3mbapmkhai2m0qjb2jgjp097c7jmmhc3.apps.googleusercontent.com",
"scopes": [
"https://www.googleapis.com/auth/userinfo.email"
]
}
...
}
Get and Use the Google Auth Token
chrome.identity.getAuthToken({ 'interactive': true }, function(token) {
// console.log("token: " + token);
let credential = firebase.auth.GoogleAuthProvider.credential(null, token);
firebase.auth().signInWithCredential(credential)
.then((result) => {
// console.log("Login successful!");
DoWhatYouWantWithTheUserObject(result.user);
})
.catch((error) => {
console.error(error);
});
});
Have fun with your Firebase Service...

NodeJS Google Api client: how do I know the access token is expired?

I am working on Nodejs Google Api client Oauth process. I follow what the code example for oauth, https://github.com/google/google-api-nodejs-client/blob/master/examples/oauth2.js.
I have one question. How do I check if the access token is expired and how do I use the refresh token to get another access token again?
To be more specific, let's say get access to google+ user profile, so I use the access token to get user profile:
getAccessToken(oauth2Client, function() {
// retrieve user profile
getUserProfile(client, oauth2Client, 'me', function(err, profile) {
if (err) {
console.log('An error occured', err);
return;
}
console.log(profile.displayName, ':', profile.tagline);
});
});
In addition, in the client side of the application(backbonejs), if I am attempting to use google api JS client to access the google drive api (not google plus), I am not sure if I can use the access token I get from server side of the application (nodejs) or I have to do another OAuth using google api JS client.
Best practice to determine if an access token is expired is to try and use it. Although the bundle returned includes the *expires_in* parameter, indicating the number of seconds until the access token expires, this isn't reliable, since it may be revoked and replaced for other reasons at any time.
The procedure then typically is
Attempt to make the call using the access token
If you get an "unauthorized" response, use the referesh token to get a new access token. If this fails, your permission has been revoked
Attempt to make the call using the new access token again
If you're using the library to do other Google API calls - this will be handled for you automatically.

Resources