Salesforce Login Page not coming for OAuth using NodeJS - node.js

I'm trying to login to Salesforce from Google Assistant (using dialogflow) using OAuth. Whatever I say to Google Assistant is supposed to be fulfilled (matched to intent which is then gets matched to the code that fulfills the intent of the user. So, basically the fulfillment code resides on a server (node js express) hosted on Heroku.
The problem is whenever I start by saying 'Talk to test app' I expect to see the Salesforce login page coming up (where I would enter the creds and then the consent part comes) but this page never comes. I'm sure that there might be something missing on my configuration of account linking / code but i'm not able to understand it.
const express = require('express');
const bodyParser = require('body-parser');
const jsforce = require('jsforce');
const { dialogflow } = require('actions-on-google');
const {
SimpleResponse,
BasicCard,
Image,
Suggestions,
Button
} = require('actions-on-google');
var options;
var port = process.env.PORT || 3000;
const expApp = express().use(bodyParser.json());
//app instance
const app = dialogflow({
debug: true
});
app.intent('Default Welcome Intent', (conv) => {
expApp.get('/oauth2/auth', function(req, res) {
const oauth2 = new jsforce.OAuth2({
clientId: process.env.SALESFORCE_CONSUMER_KEY,
clientSecret: process.env.SALESFORCE_CONSUMER_SECRET,
redirectUri: process.env.REDIRECT_URI
});
res.redirect(oauth2.getAuthorizationUrl({}));
});
//
// Pass received authorization code and get access token
//
expApp.get('/getAccessToken', function(req,res) {
const oauth2 = new jsforce.OAuth2({
clientId: process.env.SALESFORCE_CONSUMER_KEY,
clientSecret: process.env.SALESFORCE_CONSUMER_SECRET,
redirectUri: process.env.REDIRECT_URI
});
const conn = new jsforce.Connection({ oauth2 : oauth2 });
conn.authorize(req.query.code, function(err, userInfo) {
if (err) {
return console.error(err);
}
const conn2 = new jsforce.Connection({
instanceUrl : conn.instanceUrl,
accessToken : conn.accessToken
});
conn2.identity(function(err, res) {
if (err) { return console.error(err); }
console.log("user ID: " + res.user_id);
console.log("organization ID: " + res.organization_id);
console.log("username: " + res.username);
console.log("display name: " + res.display_name);
options = { Authorization: 'Bearer '+conn.accessToken};
});
});
});
conv.ask(new SimpleResponse({
speech:'Hi, how is it going? You are being guided to the login page',
text:'Hi, how is it going? You are being guided to the login page',
}));
});
expApp.get('/', function (req, res) {
res.send('Hello World!');
});
expApp.listen(port, function () {
expApp.post('/fulfillment', app);
console.log('Example app listening on port !');
});

OAuth with Google Assistant is managed from the Actions on Google project that you create for your assistant. In these settings you manage which Token and OAuth URL have to be used for the sign-in in your app. If you want the users to sign-in through the assistant app, you will have to choose the OAuth sign-in option.
So you don't have to use your own code to get the OAuth page, you can just use the SignIn() response given to you in the Actions on Google SDK. This will trigger the account linking flow for Google Assistant.
app.intent('Start Signin', (conv) => {
conv.ask(new SignIn('To get your account details'));
});
app.intent('ask_for_sign_in_confirmation', (conv, params, signin) => {
if (signin.status !== 'OK') {
return conv.ask('You need to sign in before using the app.');
}
// const access = conv.user.access.token;
// possibly do something with access token
return conv.ask('Great! Thanks for signing in.');
});

Related

Error while trying to login with through Steam from node.js

I'm trying to login through steam from my webapp but I'm having an hard time.
This is the code in my backend (I'm using Firebase cloud functions) that let me authenticate my user.
const steam = new SteamAuth({
realm: "https://storm.co.gg", // Site name displayed to users on logon
returnUrl: "http://localhost:5001/stormtestfordota/europe-west1/api/auth/steam/authenticate", // Your return route
apiKey: apiKey // Steam API key
});
let loggedUser = "";
const redirectSteamAuth = async (req, res) => {
loggedUser = req.user.username;
const redirectUrl = await steam.getRedirectUrl();
return res.json(redirectUrl);
}
So this is the first endpoint that the user calls when trying to login to Steam. And it works, so the steamcommunity.com opens without problem.
But when I click login in steamcommunity page I'm prompted with this error
So over the name of my account you can see "ERRORE" that stands for "ERROR"
This is the endpoint that should be called after authentication:
const loginWithSteam = async (req, res) => {
try {
const user = await steam.authenticate(req);
db.collection("users").doc(loggedUser).update({
steamId: user.steamid
})
activeDota2(loggedUser, user.steamid);
return res.redirect("https://storm.co.gg/dashboard/home");
} catch (error) {
console.error(error);
return res.status(401).json(error)
}
}
These are the two endpoints:
app.post("/auth/steam", (req, res, next) => validateFirebaseIdToken(req, res, next), redirectSteamAuth);
app.get("/auth/steam/authenticate", loginWithSteam);
I solved this issue. The problem was in the urls of the steam object AND there was a problem with CORS options, I didn't add the DNS of steamcommunity in origins accepted by CORS.

problems trying to use the Instagram api

I am working with Angular and Node.js with Express.js. I have all day trying to get the posts from my Instagram account but I have not even been able to get the API token. I understand that you first have to make a request to this URL: https://api.instagram.com/oauth/authorize?client_id=[instagram-app-id-lex.europa.eu&redirect_uri=[redirect-uriíritu&scope=[scope-lex.europa.eu&response_type=code&state = {state} (replacing the id and redirect-uri obviously) but I get no response. I already created my app in the instagram api following its documentation and add the tester and accept the authorization from the account settings.
At the moment I have this code en node.js:
const instagram = require('../controllers/instagram/instagram.controller');
const route = '/auth'
module.exports= (app,db, protegerRutas)=> {
app.get(`${route}/instagram`, (req, res)=> instagram.auth(req, res));
app.get(`/handle`, (req,res)=> instagram.handleauth(req,res));
// app.get(`${}`)
// app.post(`${route}/actualizar`, protegerRutas, (req, res)=> actualizar.actualizarDatos(req, res, db))
}
controller:
const Instagram = require('node-instagram').default;
const axios = require('axios');
const clavesInstagram = require('../../config/config').instagram;
const {clientId, clientSecret}= clavesInstagram;
const urlInstagram = 'https://api.instagram.com/oauth/authorize?client_id=201577004938695&redirect_uri=https://localhost:3008/auth/handle&scope={scope}&response_type=code';
const redirectUri = 'https://b4ae544459e3.ngrok.io/handle';
const instagram = new Instagram({
clientId: clientId,
clientSecret: clientSecret
})
module.exports= {
auth: (req,res)=> {
axios.get(urlInstagram)
.then( res => {
console.log('\n\nRespuesta: ',res);
})
.catch( err => {
console.log('\n\nError: ',err);
})
},
// auth: (req, res)=> {
// console.log('\n\nAuth Controller...')
// res.redirect(
// instagram.getAuthorizationUrl(redirectUri, {
// scope: ['basic','likes'],
// state: 'your state'
// })
// )
// },
handleauth: async (req, res)=> {
try {
console.log('\n\n\n\nHandleAuth....\n\n\n\n\n')
const code = req.query.code; //código devuelto por Instagram
const data= await instagram.authorizeUser(code, redirectUri);
console.log('\n\nData Instagram:', data)
res.json(data);
} catch(e) {
// statements
console.log(e);
res.json(e)
}
}
}
I understand that in the instagram API configuration the URL of my app is added to which they redirect and send the token, the url of my local app is something like this: http://localhost:3008/ and the path to which I want instagram to redirect would be this: https://localhost:3008/auth/handle (I must put it with 'https' in instagram because it does not allow http, so I don't know if this would include me). The problem is that with this code I am not getting the token that should be sent to my route https://localhost:3008/auth/handle. What am I doing wrong? I've been at this for many hours and honestly I can't find a solution. I read the documentation and try to do what it says but I can't do it. Could someone help me with this? I would be enormously grateful.
Thanks in advance.

How to authenticate Google Calendar API from Dialogflow Fulfillment using OAuth2 Client?

I am trying to build Google Calendar assistant chatbot with Dialogflow fulfillment hosted in GCP using node.js, dialogflow-fulfillment, and googleapis client libraries. I have a problem to create an authentication method using OAuth Client ID. The idea is when the user adds the bot in Google Chat the bot should greet him/her and ask the user for permission for the defined scopes (to create events in one's Google Calendar in this case). What I currently managed to do is to send the user a link where the one will see the scopes, approve those and a code will be generated, but then this code should be passed back to the function to get the token and set the credentials.
link sent to the user
generated code
code passed to the user
Is there a way to get this code automatically and authenticate the user?
My code looks like this (it's a bit messy because of all the tests that I made):
const {google} = require('googleapis');
const {WebhookClient} = require('dialogflow-fulfillment');
const credentials = {"installed":{"client_id":"618408396856-vrd3it4s4nk19tlo7qrnbb51a9f8bq6t.apps.googleusercontent.com","project_id":"pg-xc-n-app-577847","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"d_qDDlFVBtllcotgn2xvc00N","redirect_uris":["urn:ietf:wg:oauth:2.0:oob","http://localhost"]}};
//setting authentication details
const SCOPES = [
'https://www.googleapis.com/auth/calendar.events',
'https://www.googleapis.com/auth/spreadsheets'
];
const {client_secret, client_id, redirect_uris} = credentials.installed;
const authentication = new google.auth.OAuth2(
client_id,
client_secret,
redirect_uris[0]
);
const url = authentication.generateAuthUrl({
access_type: 'offline',
scope: SCOPES
});
const calendarId = 'primary';
const calendar = google.calendar('v3');
process.env.DEBUG = 'dialogflow:*'; // enables lib debugging statements
exports.meetingRoomFulfillment = function meetingRoomFulfillment(req, res) {
const agent = new WebhookClient({ request: req, response: res });
console.log(`Intent ${((req.body.queryResult || {}).intent || {}).displayName}`);
console.log(`Dialogflow Request body`, JSON.stringify(req.body));
if (req.body.queryResult === undefined || req.body.queryResult.intent === undefined || req.body.queryResult.intent.displayName === undefined) {
console.log(`Missing intent so cancelling fulfillment`);
res.send({});
return;
}
function authenticate(agent){
agent.add(`To authenticate this app please visit the following url: ${url}`);
}
function authenticationCode(agent){
const code = agent.parameters.authenticationCode;
console.log('The code: ' + code);
authentication.getToken(code, (err, token) => {
if (err) return console.error('Error retrieving access token', err);
authentication.setCredentials(token);
retrievedToken = token;
console.log(retrievedToken);
});
agent.add('Successfully authenticated!');
}
function makeAppointment (agent) {
const dateTimeStart = new Date(agent.parameters.date.split('T')[0] + 'T' + agent.parameters.time.split('T')[1]);
const dateTimeEnd = new Date(new Date(dateTimeStart).setHours(dateTimeStart.getHours() + 1));
const appointmentTimeString = dateTimeStart.toLocaleString();
const eventDescription = agent.parameters.text;
// Check the availibility of the time, and make an appointment if there is time on the calendar
return createCalendarEvent(dateTimeStart, dateTimeEnd, eventDescription).then(() => {
agent.add(`Ok, let me see if we can fit you in. ${appointmentTimeString} is fine!. I am creating an event called: ${eventDescription}`);
}).catch(() => {
agent.add(`I'm sorry, there are no slots available for this period.`);
});
}
let intentMap = new Map();
intentMap.set('authenticate', authenticate);
intentMap.set('authentication code', authenticationCode);
intentMap.set('Make Appointment', makeAppointment);
agent.handleRequest(intentMap);
}
function createCalendarEvent (dateTimeStart, dateTimeEnd, eventDescription) {
return new Promise((resolve, reject) => {
calendar.events.list({
auth: authentication,
calendarId: calendarId,
timeMin: dateTimeStart.toISOString(),
timeMax: dateTimeEnd.toISOString()
}, (err, calendarResponse) => {
// Check if there is a event already in the calendar
if (err || calendarResponse.data.items.length > 0) {
reject(err || new Error('Requested time conflicts with another appointment'));
console.log(err);
} else {
// Create event for the requested time period
calendar.events.insert({
auth: authentication,
calendarId: calendarId,
resource: {
summary: eventDescription,
start: {dateTime: dateTimeStart},
end: {dateTime: dateTimeEnd}
}
}, (err, event) => {
err ? reject(err) : resolve(event);
console.log(err);
}
);
}
});
});
}
You're on the right track, but having the user go directly to the OAuth link means that they'll get the code, and thus must send it to you.
Instead, you can send them to a page on your site, which redirects them to the OAuth link. The redirection URI you include should redirect back to your site again. This way you get the code and can process it on your server. Once you complete the OAuth dance, your website would tell them they have authorized you and can continue with the chat.

If MFA enabled in AWS cognito, do I need to create js on client side to call cognitoUser.authenticateUser() because of the promt for code?

I am using reactjs and node for server side.
As you can see in the "mfa required" part of the code below, if this is all on node, then I can't really do "prompt" the user for the code, I have to pass this back to the front end.
Tried solution: If I do pass the MFA required to front end and get the users input then send it back to node to call "respondToAuth" I am getting two MFA codes in my SMS message.
Have I tried other solutions?
I am hesitant to use amplify because everything is on the front end, I would ideally like to do my authentication on the back end (thus node).
Another option I am leaning towards is just using initiateAuth api instead of "cognitoUser.AuthenticateUser". This way I can get the challenge response and pass it on in sequence. But as per my initial question, I am wondering if I can implement the below code and be able to route users to input MFA code (without duplicating MFA sms message)
AWS.config.update({
region: process.env.Region
});
var AmazonCognitoIdentity = require('amazon-cognito-identity-js');
const poolData = { //--Moved to env variables
UserPoolId: process.env.UserPoolId, // your user pool id here
ClientId: process.env.ClientId // your app client id here
};
const userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
router.post('/api/authenticateuser', (req, res) => {
const val = req.body;
var userData = {
Username: val.value.user, // your username here
Pool: userPool
};
var authenticationData = {
Username: val.value.user, // your username here
Password: val.value.pass, // your password here
};
const authenticationDetails = new AmazonCognitoIdentity.AuthenticationDetails(authenticationData);
const cognitoUser = new AmazonCognitoIdentity.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function(result) {
console.log('You are now logged in.');
console.log(result);
const accessToken = result.getAccessToken().getJwtToken();
const idToken = result.getIdToken().getJwtToken();
res.json({
accessToken,
idToken
});
},
onFailure: function(err) {
res.json(err);
},
mfaRequired: function(codeDeliveryDetails) {
// console.log("mfa enabled");
// var verificationCode = prompt('Please input verification code' ,'');
// cognitoUser.sendMFACode(verificationCode, this);
// res.json({ MFA:codeDeliveryDetails})
}
});
})

Google Suite - Google API access - Client is unauthorized to retrieve access tokens using this method

I am struggling for days with the set up in trying to access GMail Google API from a node.js script using googleapis lib. I succeeded once but I cannot remember how I did it , I tried to reset a project, service-account and G-Suite Domain wide delegation following the Google doc ..
Here is what I did :
In my GCP Console console,
1. Existing organisation : lechorodescharentes.org
2. In this organisation , I created a project : choro-dev
3. In this project I created a service account : choro-dev-postoffice
with choro-dev-postoffice with role TokenGenerator
and enabled the Google Apps Domain-wid Delegation
downloaded the new private key ( JSON file )
4. I enabled the GMail API ( from Libray menu)
In my G-Suite domain's admin console,
5. I added the following copes for this service account's ClientID
"https://www.googleapis.com/auth/admin.directory.user",
"https://www.googleapis.com/auth/admin.directory.group"
Node.js client
I am trying to access the GMail API with the following Firebase function code using the node.js googleapis library
with server -server authentication using service account
see node.js client code
In this code, I have 2 authentication functions
connect() : to a JSON Web Token
authorize() : to request an access token from the Google OAuth 2.0 Authorization Server
Deployed the Firebase function
Run the function
Got the JWT client displayed
Function ended with error :
{"infos":"unauthorized_client: Client is unauthorized to retrieve access tokens using this method."}
node.js client code
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const {google} = require('googleapis');
const nodemailer = require('nodemailer')
const _ = require('lodash');
const KEY = require('./service-key.json');
function connect () {
return new Promise((resolve, reject) => {
const jwtClient = new google.auth.JWT(
KEY.client_email,
null,
KEY.private_key,
_.values(KEY.scopes), // scopes as authorized in G-Suite admin
KEY.admin_email . // impersonated user
);
jwtClient.authorize((err) => {
if(err) {
reject(err);
} else {
resolve(jwtClient); // returns client
}
});
});
}
// Send a message to the contact user
function sendMessage (client, sender, msg) {
return new Promise((resolve, reject) => {
var transporter = nodemailer.createTransport({
host: 'smtp.gmail.com',
port: 465,
secure: true,
auth: {
type: 'OAuth2',
user: KEY.admin_email,
serviceClient: KEY.client_id,
privateKey: KEY.private_key,
accessToken: client.access_token,
refreshToken: client.refresh_token,
expires: client.expiry_date
}
});
const mailOptions = {
from: 'SITE CONTACT<' + sender + '>',
to: KEY.contact_email,
subject: 'Message',
text: 'From: ' + sender + '\n\n' + msg,
html: '<h1>Message</h1><p>From: ' + sender + '</p><p>' + msg + '</p>'
};
transporter.sendMail(mailOptions, (err, response) => {
if (err) {
reject(err);
return;
}
resolve(response);
});
});
}
function newContactMessage (from, msg) {
return connect()
.then(client => {
return sendMessage(client, from, msg);
});
}
exports.sendContactMessage = functions.https.onRequest((req, res) => {
const sender_email = 'dufourisabelle#orange.fr';
const sender_msg = 'just a test message to contact the site owner.'
newContactMessage(sender_email, sender_msg).then(() => {
return {status: 200};
}, error => {
return {status: error.status, infos: error.message};
}).then(response => {
return res.send(response);
}).catch(console.error);
});
What could I add to it ? I'll try to re-initiate the all process and pray ... ??

Resources