How to create draft in gmail api - node.js

I am trying to create a new draft for a user with the gmail api, however i am getting an error I dont understand.
I searched but can't find a way to fix this error in gmail api, i know how to fix it please help
error
code: 403,
errors: [
{
message: 'Insufficient Permission',
domain: 'global',
reason: 'insufficientPermissions'
}
]
my code
async function sendMessage() {
const gmail = google.gmail({ version: 'v1', auth: oauth2Client });
var drafts = await gmail.users.drafts.create({
userId: "me",
requestBody: {
id: "",
message: {
raw: "LS0tLQpGcm9tOiBKb2huIERvZSA8c0BnbWFpbC5jb20+ClRvOiBNYXJ5IFNtaXRoIDxzQGdtYWlsLmNvbT4KU3ViamVjdDogU2F5aW5nIEhlbGxvCkRhdGU6IEZyaSwgMjEgTm92IDE5OTcgMDk6NTU6MDYgLTA2MDAKTWVzc2FnZS1JRDogPDEyMzRAbG9jYWwubWFjaGluZS5leGFtcGxlPgoKaGVsbG8KLS0tLQ=="
}
}
}).catch((v) => {
console.log(v)
})
}

message: 'Insufficient Permission', domain: 'global', reason: 'insufficientPermissions'
Means that when you authorized this user you did not request enough scopes of authorization in order to use this method.
The Quick start for node.js
Has you use
const SCOPES = ['https://www.googleapis.com/auth/gmail.readonly'];
While users.drafts.create requires one of the following scopes
Which means you need to change the scope in your code reauthorize your application which will request the proper scope from your user.
tip
To remove authorization make sure to delete the file in token.json it contains the users access token and refresh token, once deleted it will request authorization of your application again
const TOKEN_PATH = path.join(process.cwd(), 'token.json');

Related

Google Tokens after a while have invalid permission

I am using the Google Calendar API in my Node.js app to fetch events from a user's calendar. I am using the googleapis package and have followed the steps to authenticate and authorize the API.
Everything was working fine for the first few days, but now I am receiving the following error message when trying to fetch events:
{
"error": "invalid_grant",
"error_description": "Bad Request"
}
I am not sure why this is happening, as I have not made any changes to my code or the way I am using the API. I have tried refreshing the access token, but this did not resolve the issue.
Here's the relevant code I am using:
const { google } = require('googleapis');
const calendar = google.calendar('v3');
const OAuth2 = google.auth.OAuth2;
const auth = new OAuth2(CLIENT_ID, CLIENT_SECRET, redirect);
auth.forceRefreshOnFailure = true;
// tokens is from DB where are stroed as they come as a response from Google
auth.setCredentials(tokens);
// Check if calendar is available
const from = new Date();
const to = new Date(24 * 60 * 60 * 1000 + +from);
try {
const freeBusyQuery = {
requestBody: {
items: [{ id: 'primary' }],
timeMin: from.toISOString(),
timeMax: to.toISOString(),
},
fields: 'calendars,groups,kind,timeMax,timeMin',
alt: 'json',
auth: auth.client,
};
const response = await calendar.freebusy.query(freeBusyQuery);
return !!response.data;
} catch (error) {
// Couple days works ok and after always get error on this request with stored Token
console.error(error);
return false;
}
And an example of body for auth:
const options = {
access_type: 'offline',
prompt: 'consent',
include_granted_scopes: true,
scopes: 'openid email profile https://www.googleapis.com/auth/calendar'
}
I am using version 95.0.0 of the googleapis package.
Publishing status of consent screen is In production.
Can someone help me understand why I am receiving the permission_denied error and how I can resolve it?

AuthenticationFailed when authenticating via nodejs app and package #azure/msal-node

I have an Azure app registered . I am trying to authenticate to that app . I am able to do that and successfully get the accesstoken and idtoken.
However, when I use that token and try to make a request to list subscriptions API (https://management.azure.com/subscriptions?api-version=2020-01-01) , the request fails and give response "AuthenticationFailed". I have also tried changing the scope to https://management.azure.com/.default but the same error is there. Below is the nodejs code and I am also attaching the API permissions of app
const config = {
auth: {
clientId: 'xxx',
authority: 'https://login.microsoftonline.com/organizations',
clientSecret: 'yyy',
},
system: {
loggerOptions: {
loggerCallback(loglevel, message, containsPii) {
console.log(message);
},
piiLoggingEnabled: false,
logLevel: msal.LogLevel.Verbose,
},
},
};
// Create msal application object
const pca = new msal.ConfidentialClientApplication(config);
// Create Express App and Routes
const app = express();
app.get('/', (req, res) => {
const authCodeUrlParameters = {
scopes: ['user.read','https://management.azure.com/user_impersonation'],
redirectUri: REDIRECT_URI,
};
// get url to sign user in and consent to scopes needed for application
pca
.getAuthCodeUrl(authCodeUrlParameters)
.then((response) => {
res.redirect(response);
})
.catch((error) => console.log(JSON.stringify(error)));
});
The response I am getting is
{
"error": {
"code": "AuthenticationFailed",
"message": "Authentication failed."
}
}
The error "AuthenticationFailed" usually occurs if you are using different scope token to call the API.
I tried to generate access token with the same scope as you
via Postman and got the same error while calling the query like below:
Please note that,
user.read audience is Microsoft Graph API
https://management.azure.com/user_impersonation audience is Azure Service Management.
As you have given two different scopes with different audiences, it will consider the first scope (user.read) to generate the token as mentioned in this SO Thread which was solved by me.
When you call the query https://management.azure.com/subscriptions?api-version=2020-01-01 with the above token, you will get the error as it is intended for MS Graph audience.
I tried to generate the token with scope https://management.azure.com/user_impersonation only, removing user.read like below:
With the above generated token, I am able to call the API successfully like below:
If you want token with different scopes, then you have to generate two access tokens separately.

Teams bot SSO without dialogs framework. Invalid x5t claim

I'm following this tutorial:
https://learn.microsoft.com/en-us/learn/modules/msteams-sso/7-exercise-bots-sso
https://github.com/OfficeDev/TrainingContent/tree/master/Teams/80%20Using%20Single%20Sign-On%20with%20Microsoft%20Teams/Demos/02-learn-msteams-sso-bot
https://youtu.be/cmI06T2JLEg
https://github.com/microsoft/BotBuilder-Samples/tree/main/samples/javascript_nodejs/24.bot-authentication-msgraph
The bot worked as expected. But I would like not to use the dialog framework. I'm having trouble adapting the model.
In the personal scope I reply to a message with an oauth card:
const oauthCard = await CardFactory.oauthCard(SsoConnectionName, undefined, undefined, undefined, {
id: 'random65jHf9276hDy47',
uri: `api://botid-${MicrosoftAppId}`
})
await context.sendActivity(MessageFactory.attachment(oauthCard))
so i get the token on the handler
handleTeamsSigninTokenExchange(context, query) {
if (context?.activity?.name === tokenExchangeOperationName) {
console.dir(context?.activity?.value)
token = context?.activity?.value?.token
}
}
What am I supposed to do with this token? I get the Invalid x5t claim error when I try to use microsoft client like this:
msGraphClient = microsoft.Client.init({
debugLogging: true,
authProvider: done => {
done(null, token)
}
})
// on message 'whoiam'
const me = await msGraphClient.api("me").get()
Is this the correct token? How do I initialize the Microsoft Graph client with this token?
My repo sample: https://github.com/itacirgabral/teamsbotSSOdemo/blob/nodialog/nodialogs.js
You can use below code snippet for initializing the Graph Client:
// Get an Authenticated Microsoft Graph client using the token issued to the user.
this.graphClient = Client.init({
authProvider: (done) => {
done(null, this._token); // First parameter takes an error if you can't get an access token.
}
});
Refence sample link:
https://github.com/OfficeDev/Microsoft-Teams-Samples/blob/main/samples/app-sso/nodejs/server/models/simpleGraphClient.js
https://github.com/OfficeDev/Microsoft-Teams-Samples/blob/main/samples/app-sso/nodejs/server/api/appController.js

How to get email and profile information from OAuth2 Google API?

I'm trying to retrieve the name of a logged in user using Google API Node.js Client, using OAuth2 API.
Following the usage example, I managed to do the login, but I can't find a way to get the profile information.
I'm not using People API nor Plus API, cause as far as i know, OAuth2 includes https://www.googleapis.com/auth/userinfo.profile, which should be enough for the task.
I have seen some similar questions and tried the solutions of this one but it didn't work, maybe it's too old (?)
With the npm package googleapis how do I get the user's email address after authenticating them?
Looking at other API's like Google Sheets, it's possible to call their functions like this:
var google = require('googleapis');
var sheets = google.sheets('v4');
...
sheets.spreadsheets.values.get({
auth: auth,
spreadsheetId: file_id,
range: my_ranges,
}, function(err, response){
...
}
);
But it seems that OAuth2 doesn't work like that...
You can use Quickstart for node.js. The detail information is https://developers.google.com/gmail/api/quickstart/nodejs. Using a sample script from Quickstart, you can retrieve access token by OAuth2, and retrieve email and user profile.
Before it runs a sample of Quickstart, please confirm Prerequisites, Step 1 and Step 2.
You can use by changing listLabels(auth) as follows. The scope is https://www.googleapis.com/auth/gmail.readonly.
Script :
var gmail = google.gmail({
auth: auth,
version: 'v1'
});
gmail.users.getProfile({
auth: auth,
userId: 'me'
}, function(err, res) {
if (err) {
console.log(err);
} else {
console.log(res);
}
});
gmail.users.messages.get({
'userId': 'me',
'id': 'mail ID',
'format': 'raw'
}, function (err, res) {
console.log(new Buffer(res.raw, 'base64').toString())
});
gmail.users.getProfile retrieves user profile.
gmail.users.messages.get retrieves email.
If I misunderstand your question, I'm sorry.
Added :
Please change above to following script. Scope is https://www.googleapis.com/auth/userinfo.profile.
Script :
var oauth2 = google.oauth2({
auth: auth,
version: 'v2'
});
oauth2.userinfo.v2.me.get(
function(err, res) {
if (err) {
console.log(err);
} else {
console.log(res);
}
});
Result :
{
id: '#####',
name: '#####',
given_name: '#####',
family_name: '#####',
link: '#####',
picture: '#####',
gender: '#####',
locale: '#####'
}
2021 Solution
This answer may divert from the originally asked question but I think it will be useful for some people who are getting google user information in the backend by generating AuthUrl and sending it to the client side and then receiving the data response in the call back URL after the user gives permission from the client side.
Some global declarations
import { google } from "googleapis";
const Oauth2Client = new google.auth.OAuth2(
googleCredentials.CLIENT_ID,
googleCredentials.CLIENT_SECRET,
googleCredentials.REDIRECT_URI
);
Generate the Auth URL with the scopes
const SCOPE = [
'https://www.googleapis.com/auth/userinfo.profile', // get user info
'https://www.googleapis.com/auth/userinfo.email', // get user email ID and if its verified or not
];
const auth_url = Oauth2Client.generateAuthUrl({
access_type: "offline",
scope: SCOPE,
prompt: "consent",
state: "GOOGLE_LOGIN",
});
return res.json({ url: auth_url }); // send the Auth URL to the front end
Get the user data in the callback
let code = req.query.code; // get the code from req, need to get access_token for the user
let { tokens } = await Oauth2Client.getToken(code); // get tokens
let oauth2Client = new google.auth.OAuth2(); // create new auth client
oauth2Client.setCredentials({access_token: tokens.access_token}); // use the new auth client with the access_token
let oauth2 = google.oauth2({
auth: oauth2Client,
version: 'v2'
});
let { data } = await oauth2.userinfo.get(); // get user info
console.log(data); // you will find name, email, picture etc. here
Feel free to discuss in the comments if there's any confusion or error
You can also look into PassportJS. They have multiple strategies, including OAuth2 and 3 different Google Auth strategies. My answer doesn't really answer your question but maybe even taking a peek at Passport's code, you may get your answer.
http://passportjs.org/

Get an access token to Linkedin API using email and password

Is it possible to get an access token in order to use node-linkedin package by providing credentials - user email and password? What I'm trying to do is an command line app, that can make an api calls to linkedin and write results in a text file. Thus, I'm not sending any api call results to the client.
I've been trying to do the same thing.
the simple-oauth2 module has a method for logging in via password, but I cannot seem to get it to work.
https://www.npmjs.com/package/simple-oauth2
// Set the configuration settings
const credentials = {
client: {
id: '<client-id>',
secret: '<client-secret>'
},
auth: {
tokenHost: 'https://api.linkedin.com',
authorizePath: 'https://api.linkedin.com/uas/oauth/requestToken',
tokenPath: 'https://api.linkedin.com/uas/oauth/accessToken'
}
};
// Initialize the OAuth2 Library
const oauth2 = require('simple-oauth2').create(credentials);
// Get the access token object.
const tokenConfig = {
username: 'username',
password: 'password'
};
// Callbacks
// Save the access token
oauth2.ownerPassword.getToken(tokenConfig, (error, result) => {
if (error) {
return console.log('Access Token Error', error.message);
}
const token = oauth2.accessToken.create(result);
});
I am not sure these endpoints are correct:
auth: {
tokenHost: 'https://api.linkedin.com',
authorizePath: 'https://api.linkedin.com/uas/oauth/requestToken',
tokenPath: 'https://api.linkedin.com/uas/oauth/accessToken'
}
I get a 400 bad request.

Resources