First step on Google API + GMAIL = bad request - node.js

I'm very new on Google API, Eventually I want to send emails using it. But for now I'm trying something a little bit simpler (and then build it up).
List all emails in the inbox.
From Gmail > API > Reference, I followed these steps:
On Google API Console:
Created my application.
Under API & Services > Credentials I created a user with Project/Owner role (just to make sure there is no permission problems in this step).
Then I created a key and download the json file.
At API & Services > Library I enabled Gmail.
And using the Reference I put together this snippet:
app.ts
import { google } from 'googleapis';
import credentials from './credentials';
async function main() {
const auth = new google.auth.GoogleAuth({
credentials,
scopes: [
'https://mail.google.com/',
'https://www.googleapis.com/auth/gmail.compose',
'https://www.googleapis.com/auth/gmail.modify',
'https://www.googleapis.com/auth/gmail.readonly',
'https://www.googleapis.com/auth/gmail.metadata'
]
});
const authClient = await auth.getClient();
const gmail = google.gmail({ version: 'v1', auth: authClient });
const data = await gmail.users.messages.list({ userId: 'me' });
console.log(data);
}
main().catch(console.log);
package.json
...
"dependencies": {
googleapis": "^48.0.0"
}
...
Every time I ran this snippet I got:
[01] GaxiosError: Bad Request
[02] at Gaxios._request (~\node_modules\gaxios\build\src\gaxios.js:85:23)
...
[06] response: {
...
[35] status: 400,
[36] statusText: 'Bad Request',
[37] request: {
[38] responseURL: 'https://www.googleapis.com/gmail/v1/users/me/messages'
...
I tried many diferent configurations on Google API Console. Tried to change the scope (list of urls from line 8 to 12) for many others, change the credentials.json to a .js and .ts format, put in a global variable (GOOGLE_APPLICATION_CREDENTIALS) instead of a direct import. But despite of all my attempts, I got aways the same error.
How can I fix that?

Check out example from here which does exactly what you want. They seem use a sampleclient instead of a credentials library.

Related

How do I create a user in the firebase emulator via cloud function?

When trying to create a user programmatically via a cloud function while using the emulators, I consistently get an error about credential implementation. I am failing to see any credential issues since it's the admin SDK in an emulator environment.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp({projectId: 'mytestapp' } );
exports.createuser = functions.https.onRequest((request, response) => {
admin.auth().createUser({
email: request.query.email,
emailVerified: false,
password: request.query.password,
disabled: false,
})
.then((userRecord) => {
// See the UserRecord reference doc for the contents of userRecord.
response.json({ success: userRecord })
return null
})
.catch((error) => {
response.json({ error: error })
});
})
And the error I get is this:
{"error":{"code":"app/invalid-credential","message":"Credential implementation provided to initializeApp() via the "credential" property failed to fetch a valid Google OAuth2 access token with the following error: "Error fetching access token: Error while parsing response data: "SyntaxError: Unexpected token < in JSON at position 0". Raw server response: "<meta http-equiv="refresh" content="0;url=http://dnserrorassist.att.net/search/?q=http://metadata.google.internal%2FcomputeMetadata%2Fv1%2Finstance%2Fservice-accounts%2Fdefault%2Ftoken%26akaCid%3Daaaaaaaa%26t%3D0%26bc%3D"/><script type="text/javascript">window.location="http://dnserrorassist.att.net/search/?q="+escape(window.location)+"&r="+escape(document.referrer)+"&t=0&akaCid=aaaaaaaa&bc=";". Status code: "200". Outgoing request: "GET http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token.\"\"."}}
My understanding is that when using the emulator, authentication is included.
As a note, I am calling the function from:
localhost:5001/newproject-1b38d/us-central1/createuser?email=Jared#test.com&password=123456
When I add a document using the code from the firestore documentation, I get no errors. Only when trying to create a Firebase authentication user.
Upon further research/reading (and updating to the most recent versions of firebase/firestore) and still being unsuccesssful, I was able to identify the need to set the application credentials vial the cli done so like this.
I download the service account file from the firestore console.
Then direct my cli to that file with this small code snippet:
export GOOGLE_APPLICATION_CREDENTIALS="Users/Jared/Downloads/newproject-1b38d-firebase-adminsdk-k9lmw-8070573114.json"
This gives me the following success message:
{"success":{"uid":"6yaEOww1PSU1G5smimLo7qMCfty2","email":"jaredgibb#gmail.com","emailVerified":false,"disabled":false,"metadata":{"lastSignInTime":null,"creationTime":"Sun, 29 Aug 2021 15:48:26 GMT"},"tokensValidAfterTime":"Sun, 29 Aug 2021 15:48:26 GMT","providerData":[]}}
[Update]
Weirdly, the users were created in the live backend. Not the emulator. But the errors went away. Only now I have a bunch of users I’ll need to delete.

Unable to upload file using googleapis on Google Drive

I have a problem with the file upload on google drive using the package googleapis (v.40):
In my web application (written in Vue js) i need to upload an image file of user 'A' into the google drive space of user 'B' using googleapis.
For this, i have created a "service-account" from the google console platform (of user B) and generated the credentials.json for the API access. (JWToken,service-to-service scenario)
In my web application, after getting the AccessToken by means the json credentials of the service account, i'm ready to upload the file. But, when i call the drive.files.create(...) api i get the following error:
Invalid multipart request with 0 mime parts.
Here some code:
...
// get google api
const {google} = require("googleapis");
const drive = google.drive("v3");
// get authorization token
let authToken = await getAuthToken() // it perfectly works
console.log(authToken)
let metadata = {
name: name,
parents: [idOfTheParentFolder]
}
let media = {
mimeType: 'image/jpeg',
body: file
}
let objImage = {
auth: authToken,
resource:metadata,
media: media,
fields: 'id',
}
drive.files.create(objImage, function (error, success) {
if (!error) {
...
}
else{
// here i got the error in question
}
})
I've tried exactly this code in a single node js file and it works fine, but it doesn't works in the web browser Vue application.
Let me say first that the API for showing the files (drive.files.list()) and the API for create a folder (drive.files.create()) they work just fine in my web application.
Any suggestion?

google-api-nodejs-client throws invalid_request error when exchanging code with tokens

I've got a simple client app using react-google-login with this settings:
<GoogleLogin
clientId={config.CLIENT_ID}
scope={config.SCOPES.join(' ')}
buttonText="Login With Google"
onSuccess={response => onSignIn('google', response)}
onFailure={this.failure.bind(this)}
accessType="offline"
responseType="code"
/>
It retrieves the code successfully and sends it to the backend server which is written with NodeJS.
The server-side code looks like this:
const { google } = require('googleapis');
const config = global.config;
const oauth2Client = new google.auth.OAuth2({
clientId: config.google.CLIENT_ID,
clientSecret: config.google.CLIENT_SECRET,
});
// code omitted for the sake of simplicity
var authCode = req.body.authCode; // the provided code by google
const { tokens } = await oauth2Client.getToken(authCode);
return tokens;
When I run the code, it throws the error:
{ error: 'invalid_request',
error_description: 'Missing parameter: redirect_uri' } },
code: '400' }
and if I add redirectUrl to Developer Console, client app and server-side app, I'll get redirect_uri_mismatch error.
I'm kind of stuck here and couldn't find anything useful on the web.
Any workaround would be appreciated.
Found the solution
Based on one of the replies (surprisingly, not the answer) on this post,
All I needed to do was put postmessage instead of the actual URL in my client react-google-login button and in oauth2Client config on the server.
no need for the redirect_uri on Developer Console at all.
<GoogleLogin
clientId={config.CLIENT_ID}
****redirectUri="postmessage"****
scope={config.SCOPES.join(' ')}
buttonText="Login With Google"
onSuccess={response => onSignIn('google', response)}
onFailure={this.failure.bind(this)}
accessType="offline"
responseType="code"
/>
const oauth2Client = new google.auth.OAuth2({
clientId: config.google.CLIENT_ID,
clientSecret: config.google.CLIENT_SECRET,
****redirectUri: 'postmessage'****
});
Did solve the issue. 5 hours of working, searching and beating my head to the desk. I wonder why there's no clear documentation on Google Developers website. Or maybe there are and I couldn't find them.

Chrome Web Store 'You don't have access to licensing data for App ID:'

I have an offline Chrome Web App I want to sell through the web store.
It has a server backend. I'd like the server to check license data. How do I do that?
Here's the API. Here's a Java HowTo. Here's a OAuth-JWT lib for nodejs. I made a new client-id as described in the lib's readme.
I get this response:
{ error:
{ errors: [ [Object] ],
code: 403,
message: 'You don\'t have access to licensing data for App ID: aaaaaaaaaaaaaaaaaaaaaaaaaaaa'
}
}
How do I access license data for my app, in my server?
Mike
Here's the code:
var appId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
var userId = '1111111111111111111111';
// obtain a JWT-enabled version of request
var request = require('google-oauth-jwt').requestWithJWT();
request({
url: 'https://www.googleapis.com/chromewebstore/v1.1/licenses/'+appId+'/'+userId,
jwt: {
// use the email address of the service account, as seen in the API console
email: '11111111111-aaaaaaaaaaaaaaaaaaaaaaaa#developer.gserviceaccount.com',
// use the PEM file we generated from the downloaded key
keyFile: 'config/keys/app-11111111111111.pem',
// specify the scopes you wish to access - each application has different scopes
scopes: ['https://www.googleapis.com/auth/chromewebstore.readonly']
}
}, function (err, res, body) {
console.log(JSON.parse(body));
});
I've spent way to much time on this and other small problems with the Chrome Webstore. Going with Stripe.com, Braintree, or similar.

Google+ insert moment with nodejs client

Has anyone been able to get the google-api-nodejs-client to successfully insert a moment?
Whatever I try, I get a generic 400 "Invalid value" error but am unable to narrow down the invalid value because the API Explorer doesn't work either.
Would it be because of the missing data-requestvisibleactions parameter? I'm using passport.js's require('passport-google-oauth').OAuth2Strategy for handling oauth access, and that part is working fine, but I have no idea how to incorporate requestvisibleactions into the oauth request flow since this is definitely not originating from a clientside form.
Here's a snippet of what I'm trying to do (using the latest version of googleapis, v1.0.2):
var google = require('googleapis')
var auth = new google.auth.OAuth2()
auth.setCredentials({
'access_token': user.token
})
google.plus('v1').moments.insert({
collection: 'vault',
userId: 'me',
debug: true,
resource: {
type: "http://schemas.google.com/AddActivity",
target: {
type: "http://schema.org/CreativeWork",
url: "...omitted...",
image: "...omitted...",
description: "test",
name: "test"
}
},
auth: auth
}, function (err, response) {
if (err) {
console.error(err)
res.send(err.code, err)
} else {
console.log(response)
res.send(200)
}
})
ref 1 (out-of-date w.r.t. an older version of googleapis)
ref 2 (client-side, where the use of data-requestvisibleactions is more obvious)
As you speculated, you need the request_visible_actions parameter as part of the URL calling the oauth endpoint.
It looks like the current version of passport-google-oauth doesn't support this parameter. Judging by several of the open issues and pull requests, it isn't clear that the author will respond to requests to add it either. You have two possible options:
Switch to using the OAuth support that is included in google-api-nodejs-client
Patch the passport-google-oauth code. (And possibly submit a pull request in the hopes it will be useful to someone else.)
I don't use passport.js or the passport module in question, so I can't test this, but based on the github repository, I think you can insert the following in lib/passport-google-oauth/oauth2.js after line 136 and before the return statement:
if (options.requestVisibleActions) {
// Space separated list of allowed app actions
// as documented at:
// https://developers.google.com/+/web/app-activities/#writing_an_app_activity_using_the_google_apis_client_libraries
// https://developers.google.com/+/api/moment-types/
params['request_visible_actions'] = options.requestVisibleActions;
}

Resources