-
-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Parse Authentication in an existent Express App #2985
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
The best way to do this is to use node-parse SDK from within your node app. Call the |
Try adding an express middleware something like this. Just showing the happy path if the token is valid, it'll add the user object to the request and then run next middleware.
|
What I do is:
Here's the middleware I use: authMiddleware.requireLogin = function (req, res, next) {
// debug(req.session);
debug('AUTH: ' + req.method + ' ' + req.url);
var redirectUrl = qs.escape(req.originalUrl);
// debug(req.session);
if (req.session.token == undefined) {
debug('No session token');
return res.redirect('/account/pub/login?redirect=' + redirectUrl);
} else {
debug('Query for session token' + req.session.token);
Parse.Cloud.useMasterKey();
var sq = new Parse.Query('_Session')
.equalTo('sessionToken', req.session.token)
.include('user');
sq.first().then(function(sessionResult) {
if (sessionResult == undefined) {
debug("No matching session");
res.redirect('/account/pub/login');
} else {
debug("Got matching session");
req.user = sessionResult.get('user');
res.locals.session = req.session;
res.locals.user = req.user;
next();
}
}, function(err) {
debug("Error or no matching session: " + err);
res.redirect('/account/pub/login');
});
}
}; And the login code: router.post('/login', function(req, res) {
debug('Got post to /login');
req.checkBody('lg_email', 'Enter a valid email address.').isEmail();
req.checkBody('lg_password', 'Password required.').notEmpty();
var errors = req.validationErrors();
if (errors) {
debug('ERRORS logging in:');
debug(errors);
res.render('pages/account-public/login', { 'errors': errors, 'prev_body': req.body });
return;
}
debug('Got valid login form.');
debug('Try to login');
Parse.User.logIn(req.body.lg_email, req.body.lg_password).then(function(user) {
debug('Successfully logged in!');
debug(user);
debug('---');
debug(Parse.User.current());
// req.session.user = user;
req.session.token = user.getSessionToken();
debug("Session token: " + user.getSessionToken());
debug("Session token: " + req.session.token);
debug(req.session);
req.user = user;
if (req.body.redirectUrl) {
res.redirect(req.body.redirectUrl);
} else {
res.redirect('/');
}
}, function(err) {
debug('Error logging in.');
debug('Error: ');
debug(err);
if (err.code == 101) {
errors = [{ param: 'unknown_param',
msg: "Invalid email or password.",
value: ''
}];
} else {
errors = [{ param: 'unknown_param',
msg: error.message,
value: ''
}];
}
req.session = null;
res.render('pages/account-public/login', { 'errors': errors, 'prev_body': req.body });
});
}); This all depends on the app.use(cookieSession({
name: 'session',
secret: "xxxxx",
maxAge: 15724800000
})); |
@milesrichardson I'm getting: As a result of:
Querying for the session token in the API explorer on the dashboard succeeds.. |
Why are you guys home baking the logic to validate a session token using queries against the _Session collection when parse-server already has the build-in token validation via the REST api by doing a GET on parse/users/me? For example are you checking the session expiry? If you query this with the token in the header it'll do all this validation and return the user in the body of the request.. Is it the overhead of doing a http call that's leading you to rewrite this, seems a bit risky imho. http://blog.parse.com/announcements/validating-session-tokens-with-the-rest-api/ |
We'll provide in the next release an authentication middleware backed by the parse-server auth middleware. |
@flovilmart - Will this be before the parse server shutdown?(oh please oh please) :) |
I came up with this solution, caching the user in Redis and implementing this middleware. Set the auth.js import request from 'request-promise'
import ParseClient from './parse-client'
import Cache from './cache'
export default (config) => {
let client = ParseClient(config);
let cache = Cache('auth');
function auth(req, res, next) {
// if no session token, bad request
if(!req.headers['x-session-token']){
res.status(400).send("Missing sessionToken")
return
}
let authToken = req.headers['x-session-token']
//if user in session, forward the request
cache.get(authToken)
.then( user => user == null ? client.getMe(authToken) : user )
.then( user => cache.set(authToken, user) )
.then( user => {
req.user = user;
next();
})
.catch( error => {
console.error('Fail to authenticate user', error);
res.sendStatus(401);
})
}
return auth
} cache.js import redis from 'redis'
import Promise from 'bluebird'
import { config } from './config'
// Define redis promisses
Promise.promisifyAll(redis.RedisClient.prototype);
Promise.promisifyAll(redis.Multi.prototype);
let redisClient = redis.createClient(config.cache.port, config.cache.host, {db: 1});
export default (prefix) => {
return {
set: function(_key, value) {
let key = `${prefix}:${_key}`
return redisClient.setAsync(key, JSON.stringify(value))
.then(() => redisClient.persistAsync(key))
.then(() => redisClient.expireAsync(key, config.cache.ttl))
.then(() => value)
},
get: function(_key) {
let key = `${prefix}:${_key}`
return redisClient.getAsync(key)
.then(value => value == null ? null : JSON.parse(value))
},
del: function(_key) {
let key = `${prefix}:${_key}`
return redisClient.delAsync(key)
}
}
} and the parse-client.js a simple function to get the user based on the session token. import request from 'request-promise'
...
getMe: function(authToken) {
return request({
uri: config.uri + '/users/me',
method: 'GET',
json: true,
headers: {
'X-Parse-Application-Id': config.applicationId,
'X-Parse-REST-API-Key': config.restKey,
'X-Parse-Session-Token': authToken
}
})
} and set up the middleware app.use('/api/*', auth(parseConfig)); |
Running redis adds to the complication. I'd rather something which didn't
make my parse-server migration simple..
…On Sun, Dec 11, 2016 at 2:41 AM, ronaldo-getreveel ***@***.*** > wrote:
I came up with this solution, caching the user in Redis and implementing
this middleware.
Set the x-session-token as a session header and pass it from
ParseUser.getCurrentUser().getSessionToken() value.
And access the req.user object in your routers endpoints.
auth.js
import request from 'request-promise'import ParseClient from './parse-client'import Cache from './cache'
export default (config) => {
let client = ParseClient(config);
let cache = Cache('auth');
function auth(req, res, next) {
// if no session token, bad request
if(!req.headers['x-session-token']){
res.status(400).send("Missing sessionToken")
return
}
let authToken = req.headers['x-session-token']
//if user in session, forward the request
cache.get(authToken)
.then( user => user == null ? client.getMe(authToken) : user )
.then( user => cache.set(authToken, user) )
.then( user => {
req.user = user;
next();
})
.catch( error => {
console.error('Fail to authenticate user', error);
res.sendStatus(401);
})
}
return auth
}
cache.js
import redis from 'redis'import Promise from 'bluebird'import { config } from './config'
// Define redis promissesPromise.promisifyAll(redis.RedisClient.prototype);Promise.promisifyAll(redis.Multi.prototype);
let redisClient = redis.createClient(config.cache.port, config.cache.host, {db: 1});
export default (prefix) => {
return {
set: function(_key, value) {
let key = `${prefix}:${_key}`
return redisClient.setAsync(key, JSON.stringify(value))
.then(() => redisClient.persistAsync(key))
.then(() => redisClient.expireAsync(key, config.cache.ttl))
.then(() => value)
},
get: function(_key) {
let key = `${prefix}:${_key}`
return redisClient.getAsync(key)
.then(value => value == null ? null : JSON.parse(value))
},
del: function(_key) {
let key = `${prefix}:${_key}`
return redisClient.delAsync(key)
}
}
}
and the parse-client.js a simple function to get the user based on the
session token.
getMe: function(authToken) {
return request({
uri: config.uri + '/users/me',
method: 'GET',
json: true,
headers: {
'X-Parse-Application-Id': config.applicationId,
'X-Parse-REST-API-Key': config.restKey,
'X-Parse-Session-Token': authToken
}
})
}
and set up the middleware
app.use('/api/*', auth(parseConfig));
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#2985 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAFBNKwdfg2zeaK9j5dr_dQF2UnvXC_kks5rG-99gaJpZM4Kmuid>
.
|
@flovilmart Hi sir, do you have any news regarding the authentication middleware ? |
For now, I decided to go the single page application route (using Vue.js). I store the session token on the client and upon refresh, I use |
Closing due to lack of activity, please update to latest parse-server version and open a new issue if the issue persist. Don't forget to include your current:
|
Is there any update on Parse express auth middleware ? |
I don't know if this is the right place to ask that.
I have an existent Express API and now we started to using Parse in the same server.
Is there a way to authenticate an user in the Parse server from the Express endpoints?
I'm trying to avoid the migration of all the existent API to the cloud functions.
Thanks
The text was updated successfully, but these errors were encountered: