When i use this function in Cloud Code Parse.User.current() return null.
I'm using parseExpressCookieSession for login.
Any advice?
var express = require('express');
var expressLayouts = require('cloud/express-layouts');
var parseExpressHttpsRedirect = require('parse-express-https-redirect');
var parseExpressCookieSession = require('parse-express-cookie-session');
// Required for initializing enter code hereExpress app in Cloud Code.
var app = express();
// Global app configuration section
app.set('views', 'cloud/views');
app.set('view engine', 'ejs'); // Switch to Jade by replacing ejs with jade here.
app.use(expressLayouts); // Use the layout engine for express
app.set('layout', 'layout');
app.use(parseExpressHttpsRedirect()); // Require user to be on HTTPS.
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.cookieParser('helloworld'));
app.use(parseExpressCookieSession({
fetchUser: true,
cookie: { maxAge: 3600000 * 24 }
}));
Parse.Cloud.beforeSave('Menu', function(request, response) {
var Business = Parse.Object.extend('Business');
var query = new Parse.Query(Business);
query.equalTo('profile', Parse.User.current().get('profile'));
query.find({
success: function(business) {
console.log(business);
response.success();
},
error: function(error) {
response.error(error.message);
}
});
});
app.listen();
This the code that i use to login/logout
app.post('/login', function(req, res) {
Parse.User.logIn(req.body.username, req.body.password).then(function(user) {
// Login succeeded, redirect to homepage.
// parseExpressCookieSession will automatically set cookie.
res.redirect('/');
},
function(error) {
// Login failed, redirect back to login form.
res.redirect('/');
});
});
// Logs out the user
app.post('/logout', function(req, res) {
Parse.User.logOut();
res.redirect('/');
});
It is an old question but answering for future reference.
Parse.User.current() works in Javascript SDK when used in clients ex. WebApp where users log in and the you can fetch the current user with that function.
To get the user calling a Cloud Code function or doing an operation on an object (beforeSave,afterSave,beforeDelete and so on) you use the request.user property it contains the user issuing the request to Parse.com.
More details about Parse.Cloud.FunctionRequest here: https://parse.com/docs/js/api/classes/Parse.Cloud.FunctionRequest.html
Example code:
Parse.Cloud.beforeSave('Menu', function(request, response) {
var requestUser = request.user;
// instance of Parse.User object of the user calling .save() on an object of class "Menu"
// code cut for brevity
});
Related
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 !
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.
I see this error message "Cannot GET /logout" for the /logout URL, but from the docs it seems like that link should automatically be registered as a route. My code is very basic at the moment, looks like:
var express = require("express");
var stormpath = require('express-stormpath');
var app = express();
var port = 1337;
app.use(stormpath.init(app, {
apiKey: {
id: '<>',
secret: '<>'
},
application: {
href: "<>"
},
website: true
}));
app.get("/", stormpath.loginRequired, function(req, res) {
res.send("Hello Node.js and Express.");
});
app.on('stormpath.ready', function() {
console.log('Stormpath Ready!');
});
console.log("Web application opened.");
app.listen(port);
Any help is much appreciated.
The logout route for express-stormpath requires a POST request. We do this to prevent the omnibar from accidentally logging you out from the application.
I am using KeystoneJS and wanna insert the oauth2 codes.
All my Token and Users codes are run very fine; the methods are triggered via console.log. The token is generated successfully.
However, there is no return and finally I receive a timeout in my client:
// Setup Route Bindings
exports = module.exports = function(app) {
// Oauth2
var oauth = oauthserver({
model: require('../models/Client'),
grants: ['password', 'refresh_token'],
debug: true });
// Views
app.get('/', routes.views.index);
// both routes below can be triggered correctly, but timeout return
app.all('/oauth/token', oauth.grant());
app.get('/secret', oauth.authorise(), function (req, res) {
res.send('Secret area');
});
};
I have a working development version and even my testing version worked until recently. The authentication works sometimes, but mostly the authentication just fails with req.isAuthenticated().
server.js:
var express = require('express');
var app = express();
var port = PORTS[ENVIRONMENT];
var passport = require('passport');
var morgan = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var session = require('express-session');
var passportConfigs = require('./config/passport');
var routes = require('./routes.js');
// App setup
app.use("/", express.static(__dirname + '/public/'));
// configuration ===============================================================
/* open mongo connection */
require('./database/' + ENVIRONMENT + '.js');
/* === passport configs === */
passportConfigs(passport, ENVIRONMENT);
// set up our express application
app.use(morgan(morganEnv)); // log every request to the console
app.use(cookieParser()); // read cookies (needed for auth)
//app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true
})); // get information from html forms
app.set('view engine', 'ejs'); // set up ejs for templating
// required for passport
app.use(session({
secret: '********' ,
resave: false,
saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session()); // persistent login sessions
passport serialize:
passport.deserializeUser(function(id, done) {
userQueries.findID(id)
.then(function(user) {
var firebaseRef = firebaseRefMod(user, environment).ref;
if(!firebaseRef) {
throw new Error('problem with generating firebase reference, dunno why!');
}
console.log("UserFirst:", { userData: filterUserData( user ), FBRef: firebaseRef })
done(null, { userData: filterUserData( user ), FBRef: firebaseRef } );
}).then(null, function(err) {
console.log(err);
done(err, null);
});
});
routes:
app.post('/auth', isLoggedIn, function(req, res) {
console.log("req", req.user)
res.status(200).send({ "authKey": authGenerator.createToken(authGenerator.types.NORMAL, req.user.userData) , "user": req.user.userData } );
});
function isLoggedIn(req, res, next) {
console.log("userIn", req.isAuthenticated())
// if user is authenticated in the session, carry on
if (req.isAuthenticated())
return next();
// if they aren't redirect them to the home page
errorInRestRequest(res, errorCodes.userNotAuthenticated, req.user);
return false;
}
So basically if the authentication succeeds as it should, the isLoggedIn-function has access to user data from req.user. Even if the authentication does not success, the user is deserialized without problem every time verified by the "console.log("UserFirst:"... entry), but the req.user does not hold the data when it reaches in isLoggedIn-function.
Unfortunately my knowledge of passport and express middlewares are limited, so I'm puzzled as to where the data vanishes in between.
Seems like the problem was caused by wrong node module versions. Do not have the specifics yet, but reverting several modules to older versions fixed the issue.