I have just started with passport.js. From this article, I got what is the flow of all the passport methods and implemented the same in my application and it is working. Here is my server.js and I am using passport-local strategy. Angular app and rest APIs on the same server
import { registerControllersFromFolder } from 'giuseppe';
import { MessageManager } from './messaging/MessageManager';
import express = require('express');
import bodyParser = require('body-parser');
import session = require("express-session");
import http = require('http');
// class to hold user info
class User {
userId: number;
userName: string;
constructor(userId: number, userName: string) {
this.userId = userId;
this.userName = userName;
}
}
// server class to create http server
export class Server {
// list of apis for which authentication is not required
private static publicApiList: string[] = ["/services/login", "/login", "/favicon.ico"];
// request interceptor that will check user authentication
private static isAuthenticated = (req, res, next) => {
console.log("Authenticating :", req.originalUrl);
if (req.isAuthenticated() || Server.publicApiList.indexOf(req.originalUrl) > -1) {
// express routing
if (req.originalUrl.startsWith("/services")) {
console.log("Express Routing");
return next();
} else { // angular routing -> return index.html
console.log("Angular Routing");
return res.sendFile(__dirname + "/public/index.html");
}
} else {
console.log("User not authenticated.")
res.redirect('/');
}
};
static startServer() {
let userList: User[] = [new User(1, "Sunil"), new User(2, "Sukhi")];
let app = express();
// passport library
let passport = require('passport');
let LocalStrategy = require('passport-local').Strategy;
// middlewares
app.use(express.static(__dirname + "/public"));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(session({ resave: false, saveUninitialized: true, secret: "secretKey123!!" }));
// passport middleware invoked on every request to ensure session contains passport.user object
app.use(passport.initialize());
// load seriliazed session user object to req.user
app.use(passport.session());
// Only during the authentication to specify what user information should be stored in the session.
passport.serializeUser(function (user, done) {
console.log("Serializer : ", user);
done(null, user);
});
// Invoked on every request by passport.session
passport.deserializeUser(function (user, done) {
let validUser = userList.filter(user => user.userId === user.userId)[0];
console.log("D-serializer : ", validUser);
done(null,validUser);
});
// passport strategy : Only invoked on the route which uses the passport.authenticate middleware.
passport.use(new LocalStrategy({
usernameField: 'name',
passwordField: 'password'
},
function (username, password, done) {
console.log("Strategy : Authenticating if user is valid :", username)
let user = userList.filter(user => username === user.userName);
console.log("Valid user : ", user)
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
return done(null, user[0]);
}
));
// intercept request for authentication
app.use(Server.isAuthenticated);
app.post('/services/login', passport.authenticate('local', {
successRedirect: '/profile',
failureRedirect: '/login'
}));
app.get('/services/logout', (req: any, res: any) => {
req.logout();
console.log("User Logout");
res.send("{status:'logout'}")
});
// http server creation
let server = http.createServer(app);
registerControllersFromFolder({ folderPath: './api' })
.then(router => {
app.use(router);
/* start express server */
})
.catch(err => {
/* error happened during loading and registering */
});
server.listen(7000, () => {
console.log('Up and running on port 7000');
});
}
}
exports.startServer = Server.startServer;
// Call a module's exported functions directly from the command line.
require('make-runnable');
When I hit localhost:7000 it serves the index.html page as I have used
app.use(express.static(__dirname + "/public"));
and this is an angular app and because of angular routing login module will get loaded by default. I have used a middleware that checks request authentication and if true then based on request prefix (angular or express) routing is done.
For the login request defined local strategy method is called and if this is true it calls serializer method that takes the responsibility which data should be stored in the request session. and then sucessRedirect or failureRedirect is called.
For subsequent request, As I have used middleware that checks if req.isAuthenticated is true if so then request is served otherwise the user is redirected to login page. I know in every subsequent request deserializeUser method is called that contains the object that was stored by serializeUser method in the login request. As per the document, this makes a call to the database to check valid user.
But I am confused but is the actual use case of deserializeUser method? Where can I take the benefit of this method and if I am intercepting ecah request and check req.isAuthenticted() then why to call database in deserializeUser method?>
As stated in this answer
The first argument of deserializeUser corresponds to the key of the
user object that was given to the done function (see 1.). So your
whole object is retrieved with help of that key. That key here is the
user id (key can be any key of the user object i.e. name,email etc).
In deserializeUser that key is matched with the in memory array /
database or any data resource.
The fetched object is attached to the request object as req.user
Thus, the benefit of deserializeUser is that you have the user object available on every request thereafter.
You ask why you need to use deserializeUser if you call req.isAuthenticated, and the answer lies in the implementation of req.isAuthenticated:
req.isAuthenticated = function() {
var property = 'user';
if (this._passport && this._passport.instance) {
property = this._passport.instance._userProperty || 'user';
}
return (this[property]) ? true : false;
};
To me, it looks like req.isAuthenticated is looking for req[user] to be set, and thus, deserializeUser must be called before it can work.
Related
So I feel like I've tried everything.
Quick workflow breakdown. I have a React App deployed on Netlify that uses a Rest API backend hosted on Heroku with a Jaws MySQL instance and a Redis session store. There is an authorization workflow that uses a google0auth2.0 strategy and passport.js as well as the Redis to store each separate session which I only started using on the production build as my understanding was that express-sessions alone would cause memory leaks. That was my first step through this rabbit hole.
After some research, I added app.set('trust proxy', 1), added app.use(helmet.hsts()) to address headers issues and I attempted to secure the cookie with the code below:
app.use(
expressSession({
...
proxy: isProdEnv,
cookie: {
secure: isProdEnv
}
When checking the network tab in chrome dev tools, I see the cookie is attached to the callback however, it is not attached to the dashboard react page that the API redirects on the successful authorization.
My API index.js is below:
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const expressSession = require('express-session');
const {
createClient
} = require("redis");
let RedisStore = require('connect-redis')(expressSession);
require('dotenv').config();
const PORT = process.env.PORT || 5050;
const isProdEnv = process.env.NODE_ENV === 'production' ? true : false;
// Knex instance for DB managment
const knex = require('knex')(require('./knexfile.js')[process.env.NODE_ENV || 'development']);
const app = express();
app.use(express.json());
app.use(helmet());
app.use(helmet.hsts());
app.use(
cors({
origin: true,
credentials: true,
}),
);
let redisClient = createClient({
url: process.env.REDIS_URL,
lazyConnect: true,
showFriendlyErrorStack: true,
legacyMode: true,
retry_strategy: (options) => {
const {
error,
total_retry_time,
attempt
} = options;
if (error ? .code === 'ECONNREFUSED' || error ? .code === 'NR_CLOSED') {
return 5000;
}
if (total_retry_time > 1000 * 15) {
return undefined;
}
if (attempt > 10) {
return undefined;
}
return Math.min(options.attempt * 1000, 5000); //in ms
},
});
if (!redisClient.isOpen) {
redisClient.connect().catch(console.error);
console.info('connected to redis at', process.env.REDIS_URL);
}
redisClient.on('error', (err) => {
console.log('ⓘ on error:', err);
});
app.set('trust proxy', 1);
// Include express-session middleware (with additional config options required
// for Passport session)
app.use(
expressSession({
store: new RedisStore({
client: redisClient
}),
secret: process.env.SESSION_SECRET,
proxy: isProdEnv,
resave: false,
saveUninitialized: true,
name: 'lostnfound',
cookie: {
secure: isProdEnv
}
}),
);
// =========== Passport Config ============
// Initialize Passport middleware
app.use(passport.initialize());
app.use(passport.session());
passport.use(
new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: process.env.GOOGLE_CALLBACK_URL,
scope: ['profile', 'email'],
},
(_accessToken, _refreshToken, profile, done) => {
const id = String(profile.id);
const profileId = Number(id.slice(-18));
// First let's check if we already have this user in our DB
knex('users')
.select('id')
.where({
google_id: profileId
})
.then((user) => {
if (user.length) {
// If user is found, pass the user object to serialize function
done(null, user[0]);
} else {
// If user isn't found, we create a record
knex('users')
.insert({
google_id: profileId,
avatar_url: profile._json.picture,
first_name: profile.name.givenName,
last_name: profile.name.familyName,
email: profile._json.email,
})
.then((userId) => {
// Pass the user object to serialize function
done(null, {
id: userId[0]
});
})
.catch((err) => {
console.log('Error creating a user', err);
});
}
})
.catch((err) => {
console.log('Error fetching a user', err);
});
},
),
);
// `serializeUser` determines which data of the auth user object should be stored in the session
// The data comes from `done` function of the strategy
// The result of the method is attached to the session as `req.session.passport.user = 12345`
passport.serializeUser((user, done) => {
console.log('serializeUser (user object):', user);
// Store only the user id in session
done(null, user.id);
});
// `deserializeUser` receives a value sent from `serializeUser` `done` function
// We can then retrieve full user information from our database using the userId
passport.deserializeUser((userId, done) => {
console.log('deserializeUser (user id):', userId);
// Query user information from the database for currently authenticated user
knex('users')
.where({
id: userId
})
.then((user) => {
// Remember that knex will return an array of records, so we need to get a single record from it
console.log('req.user:', user[0]);
// The full user object will be attached to request object as `req.user`
done(null, user[0]);
})
.catch((err) => {
console.log('Error finding user', err);
});
});
// Additional information on serializeUser and deserializeUser:
// https://stackoverflow.com/questions/27637609/understanding-passport-serialize-deserialize
// Import all route types for server functionality
const authRoutes = require('./routes/auth');
const postRoutes = require('./routes/post');
app.use('/auth', authRoutes);
app.use('/post', postRoutes);
app.listen(PORT, () => {
console.log(`🚀 Server listening on port ${PORT}.`);
});
And this is my google endpoint and google callback:
const express = require('express');
const router = express.Router();
const passport = require('passport');
require('dotenv').config();
const AuthController = require('../controller/auth');
// Create a login endpoint which kickstarts the auth process and takes user to a consent page
router.get('/google', passport.authenticate('google'));
// This is the endpoint that Google will redirect to after user responds on consent page
router.get(
'/google/callback',
passport.authenticate('google', {
failureRedirect: `${process.env.CLIENT_URL}/auth-fail`,
}),
(_req, res) => {
// Successful authentication, redirect to client-side application
res.redirect(`${process.env.CLIENT_URL}/dashboard`);
},
);
I will add, I'm on the free Heroku account and haven't set up any SSL. Is that what is holding me back? is there any third-party free SSL that I can work into the workflow?
It's hosted here if you want to get a first-hand look. Click on the login to google to go through the workflow and checkout the network tab after you're authorized (if you dare, lol)
I am using Google One Tap to log into a webpage and trying to use AWS' DynamoDB to manage the session info. I previously tried Elasticache and couldnt view the clusters/connect to the DB, so I switched. Now, I have the opposite problem where I'll get 20-100+ sessions saved to DyanmoDB in a 5-30 min span when I go to the login page 1-2x. I thought the problem was having saveUninitialized set to true, but that has not changed anything.
Would really appreciate any suggestions on how to debug this/what's going on/alternatives for session management
const session = require('express-session')
const DynamoDBStore = require('connect-dynamodb')(session)
const options = {
table: 'my-sessions',
AWSConfigJSON: {
accessKeyId: process.env.ACCESSKEYID,
secretAccessKey: process.env.SECRETACCESSKEY,
region: 'us-east'
}
}
app.use(session({
genid: (req) => {
return uuidv4() // use UUIDs for session IDs
},
cookie: {
secure: true
},
store: new DynamoDBStore(options),
secret: 'mysecretkey',
resave: false,
cookie: {
maxAge: 5*60*1000, //set to 5 min
},
saveUninitialized: false,
name: 'id',
rolling: true
}));
Edit: Adding my Google Auth process just in case I modified it/managing sessions incorrectly
This function is on the client side and runs once the user selects the profile to log in with
function handleCredentialResponse(response) {
var id_token = response.credential;
var xhr = new XMLHttpRequest();
xhr.open('POST', '/auth'); // have been getting a 404 error in the console, but I get logs in the terminal that indicate /auth was reached? secondary, but would love any insight into this
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.onload = function() {
console.log('Signed in as: ' + xhr.responseText);
};
xhr.send('idtoken=' + id_token);
}
This is on the server side, and what a POST req to /auth hits:
const client = new OAuth2Client(process.env.GOOGLE_CLIENT_ID);
exports.authLogin = (req, res, next) => {
async function verify() {
// google API function that verifies the JWT signature, the aud claim, the exp claim, and the iss claim.
const ticket = await client.verifyIdToken({
idToken: req.body.idtoken, // passed from handleCredentialResponse function
audience: process.env.GOOGLE_CLIENT_ID,
});
const payload = ticket.getPayload();
const userid = payload['sub'];
req.session.user = userid
req.session.auth = true // logged in
req.session.save()
}
verify().catch(console.error);
next()
}
I'm not quite following everything you're asking here, but I do see an implementation mistake that could create problems. In your exports.authLogin() function, you are calling next() before the asynchronous portion of your middleware has finished. Instead, you should do this:
exports.authLogin = (req, res, next) => {
async function verify() {
// google API function that verifies the JWT signature, the aud claim, the exp claim, and the iss claim.
const ticket = await client.verifyIdToken({
idToken: req.body.idtoken, // passed from handleCredentialResponse function
audience: process.env.GOOGLE_CLIENT_ID,
});
const payload = ticket.getPayload();
const userid = payload['sub'];
req.session.user = userid
req.session.auth = true // logged in
req.session.save()
}
verify().then(next).catch(err => {
console.error(err);
next(err);
});
}
This moves the call to next() into a .then() handler so it does not advance to the next request handler in the chain until after verify() is done. In addition, if verify() fails, it also calls next(err) rather than just doing nothing but logging and leaving the current request stranded.
I also note that you aren't waiting for req.session.save() to complete before proceeding so you need to figure out if that is also something you need to do.
when I created passport-saml strategy, during login, there is a profile object pass to the middleware function, with nameID info there. I need that info to call logout later on.
// passportHandler.js
const passport = require("passport");
const passportSaml = require("passport-saml");
passport.serializeUser((user, done) => {
done(null, user);
});
passport.deserializeUser((user, done) => {
done(null, user);
});
// SAML strategy for passport -- Single IPD
const samlStrategy = new passportSaml.Strategy(
{
entryPoint: process.env.SSO_ENTRYPOINT,
logoutUrl: process.env.SSO_LOGOUT,
issuer: process.env.SSO_ISSUER,
callbackUrl: process.env.SSO_CALLBACK_URL || undefined,
path: process.env.path,
cert: process.env.SSO_CERT.replace(/\\n/gm, "\n"), // change "\n" into real line break
},
(profile, done) => {
console.log('profile', profile); // nameID and nameIDFormat are in profile object
done(null, profile)
}
);
passport.use(samlStrategy);
module.exports = passport;
index.js
// index.js of Express server
import passport from "./src/passportHandler";
import { getLogout } from "./src/routes.js";
const app = express();
app.use(passport.initialize());
app.use(passport.session());
app.get('/sso/logout', getLogout); // this route, I need the above 2 data
getLogout function import from another file, I hardcode nameID and nameIDFormat, how do I get them from the beginning profile object, save them somewhere, and pass them to this route?
// routes.js
export const getLogout = (req, res) => {
!req.user && (req.user = {})
req.user.nameID = 'Eric1234#outlook.onmicrosoft.com'; // hardcode, how to pass this info?
req.user.nameIDFormat = 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'; // hardcode too
const samlStrategy = req._passport?.instance?._strategies?.saml; // is this correct?
samlStrategy.logout(req, (err, request) => {
if (!err) {
res.redirect(request);
}
})
};
my second question is, I get the samlStrategy object from req._passport?.instance?._strategies?.saml, is it a proper way to get it? or, again the similar question, how can I pass saml strategy obj from the beginning create logic to this route?
thanks for any help!
answering my own silly question...
in samlStrategy, at last calling done(null, profile)
const samlStrategy = new passportSaml.Strategy(
{
entryPoint: process.env.SSO_ENTRYPOINT,
logoutUrl: process.env.SSO_LOGOUT,
issuer: process.env.SSO_ISSUER,
callbackUrl: process.env.SSO_CALLBACK_URL || undefined,
path: process.env.path,
cert: process.env.SSO_CERT.replace(/\\n/gm, "\n"), // change "\n" into real line break
},
(profile, done) => {
console.log('profile', profile); // nameID and nameIDFormat are in profile object
done(null, profile)
}
);
then the profile object will become req.user object in the Service Provider's Login Post Callback function
Then I can save the user object somewhere, and use it again when logout being called.
I am using passport-jwt to verify access to a given route in express.js, and then return a Sequelize model to the final controller. The code looks like:
The auth strategy:
const passportStrategy = passport => {
const options = {
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: config.auth.ACCESS_TOKEN_SECRET
};
passport.use(
new Strategy(options, async (payload, done) => {
try {
const user = await User.findOne({ where: { email: payload.email }});
if (user) {
return done(null, {
user
});
}
return done(null, false);
}
catch (error) {
return done(error, false)
}
})
);
};
The route with the auth middleware
router.get('/:user_id/psy', passport.authenticate('jwt', { session: false }), patientsController.getPatientPsy);
The controller function
const getPatientPsy = async (req, res) => {
const authenticatedUser = req.user;
if (authenticatedUser.userType !== "patient") {
res.status(500).send("Big time error");
}
}
If I console.log(authenticatedUser) in the getPatientPsy() controller it successfully prints the Sequelize model with it's dataValues and so on, but when I try to access any property, be it userType or any other it consistently returns undefined.
In the passport-jwt authentication once a User has been found that matches the extracted JWT token, afaik it is returned synchronously and made it available in the req.user object, and I can print it with console.log, but why can't I access the model's properties?
I've tried to make the getPatientPsy() controller a sync function but it doesn't work either.
Thank you.
All right this is embarrassing, by default Passport.js returns the done(null, user) in the req.user property, and since I am returning { user }, I had to access through req.user.user.
I have an Express App ( hosted on Heroku ) which i'm using to handle intents from Dialogflow and make callouts to APEX REST Webservice classes (to get data from Salesforce) and then show the results back on Google Assistant.
For authentication, i'm trying to implement OAuth, and hence I've created Connected App on Salesforce.
On Google Actions under Account Linking i've mentioned the 'Authorization URL' as Express App URL (something like https://testBot.herokuapp.com/authorization) and 'Client Id issued by your Actions to Google' as Consumer Key from Salesforce Connected App and lastly 'Client Secret' as Salesforce Connected App Consumer Secret. Also, my Token URL is like https://testBot.herokuapp.com/token.
On Express i've created routes, first to handle the request coming in for authorization (to get authorization code) and then secondly on the callback route (this is the callback URL on Salesforce Connected App) as mentioned on Implement OAuth account linking i've redirected to redirect_uri (of the form https://oauth-redirect.googleusercontent.com/r/MY_PROJECT_ID) with authorization code and state as parameters. This is how the uri looks https://oauth-redirect.googleusercontent.com/r/MY_PROJECT_ID?code=AUTHORIZATION_CODE&state=STATE_STRING. Now on the 3rd route (https://testBot.herokuapp.com/token), logic is written to exchange authorization code for an access token and a refresh token. Note that the token exchange endpoint responds to POST requests.
Now as per official documentation , Google stores the access token and the refresh token for the user. So, what this means is that Conversation or conv object should hold the access token values however when I try to access the same and then make a callout to the APEX Webservice I could see that conv.user.accessToken gives undefined and hence the callout is also unsuccessful (error : INVALID_SESSION_ID: Session expired or invalid) even after successful authentication.
My question is why i'm not getting the access token from CONV and if this is expected (am I reading the documentation incorrectly) how am I supposed to get the access token ?
Here is the express code:
const express = require('express');
const bodyParser = require('body-parser');
const jsforce = require('jsforce');
const { dialogflow } = require('actions-on-google');
const {
SimpleResponse,
BasicCard,
SignIn,
Image,
Suggestions,
Button
} = require('actions-on-google');
var options;
var timeOut = 3600;
var port = process.env.PORT || 3000;
var conn = {};
const expApp = express().use(bodyParser.json());
expApp.use(bodyParser.urlencoded());
//app instance
const app = dialogflow({
debug: true
});
const oauth2 = new jsforce.OAuth2({
clientId: process.env.SALESFORCE_CONSUMER_KEY,
clientSecret: process.env.SALESFORCE_CONSUMER_SECRET,
redirectUri: 'https://testbot.herokuapp.com/callback'
});
expApp.get('/authorize', function(req, res) {
var queryParams = req.query;
console.log('this is the first request: '+req);
res.redirect(oauth2.getAuthorizationUrl({ state: queryParams.state }));
});
expApp.get('/callback', function(req,res) {
var queryParams = req.query;
console.log('Request came for access callback');
console.log('Query params in callback uri is ', req.query);
let redirectUri = `${process.env.GOOGLE_REDIRECT_URI}?code=${queryParams.code}&state=${queryParams.state}`;
console.log('Google redirecturi is ', redirectUri);
res.redirect(redirectUri);
});
expApp.post('/token', function(req, res) {
console.log('Request came for accesstoken');
console.log('query params are-->', req.body);
console.log('req query-->', req.query);
res.setHeader('Content-Type', 'application/json');
if (req.body.client_id != process.env.SALESFORCE_CONSUMER_KEY) {
console.log('Invalid Client ID');
return res.status(400).send('Invalid Client ID');
}
if (req.body.client_secret != process.env.SALESFORCE_CONSUMER_SECRET) {
console.log('Invalid Client Ksecret');
return res.status(400).send('Invalid Client ID');
}
if (req.body.grant_type) {
if (req.body.grant_type == 'authorization_code') {
console.log('Fetching token from salesforce');
oauth2.requestToken(req.body.code, (err, tokenResponse) => {
if (err) {
console.log(err.message);
return res.status(400).json({ "error": "invalid_grant" });
}
console.log('Token respons: ',tokenResponse);
var googleToken = {
token_type: tokenResponse.token_type,
access_token: tokenResponse.access_token,
refresh_token: tokenResponse.refresh_token,
expires_in: timeOut
};
console.log('Token response for auth code', googleToken);
res.status(200).json(googleToken);
});
}
else if (req.body.grant_type == 'refresh_token') {
console.log('Fetching refresh token from salesforce');
oauth2.refreshToken(req.body.refresh_token, (err, tokenResponse) => {
if (err) {
console.log(err.message);
return res.status(400).json({ "error": "invalid_grant" });
}
console.log('Token response in refresh token: ',tokenResponse);
var googleToken = { token_type: tokenResponse.token_type, access_token: tokenResponse.access_token, expires_in: timeOut };
console.log('Token response for auth code', googleToken);
res.status(200).json(googleToken);
});
}
} else {
res.send('Invalid parameter');
}
});
var createTask = function(oppName,taskSubject,taskPriority,conFName,conn){
return new Promise((resolve,reject)=>{
conn.apex.get("/createTask?oppName="+oppName+"&taskSubject="+taskSubject+"&taskPriority="+taskPriority+"&contactFirstName="+conFName,function(err, res){
if (err) {
console.log('error is --> ',err);
reject(err);
}
else{
console.log('res is --> ',res);
resolve(res);
}
});
});
};
app.intent('Default Welcome Intent', (conv) => {
console.log('Request came for account link flow start');
if(!conv.user.accessToken){
conv.ask(new SignIn());
}
else{
conv.ask('You are already signed in ');
}
});
app.intent('Get SignIn Info', (conv, params, signin) => {
console.log('Sign in info Intent');
console.log('Sign in content-->',signin);
if (signin.status === 'OK') {
conv.ask('Hola, thanks for signing in! What do you want to do next?') ;
}
else {
conv.ask('Something went wrong in the sign in process');
}
});
app.intent('Create Task on Opportunity', (conv, {oppName,taskSubject,taskPriority,contactFirstName} ) => {
console.log('conv: ',conv);
//this logs undefined
console.log('Access token from conv inside intent: ',conv.user.accessToken);
const opName = conv.parameters['oppName'];
const tskSbj = conv.parameters['taskSubject'];
const tskPr = conv.parameters['taskPriority'];
const conFName = conv.parameters['contactFirstName'];
console.log('Instance URL as stored in heroku process variable: ',process.env.INSTANCE_URL);
conn = new jsforce.Connection({
instanceUrl : process.env.INSTANCE_URL,
accessToken : conv.user.accessToken
});
return createTask(opName,tskSbj,tskPr,conFName,conn).then((resp) => {
conv.ask(new SimpleResponse({
speech:resp,
text:resp,
}));
});
});
expApp.get('/', function (req, res) {
res.send('Hello World!');
});
expApp.listen(port, function () {
expApp.post('/fulfillment', app);
console.log('Example app listening on port !');
});
So, on logging conversation.user I understood that conv.user.access.token is correct and not conv.user.accessToken. Hence, now the connection instance would look like:
conn = new jsforce.Connection({
instanceUrl : process.env.INSTANCE_URL,
accessToken : conv.user.acces.token
});
Now, get request on apex web service does send expected response !