Skip to content

Commit

Permalink
Merge b2b525d into c4aadc9
Browse files Browse the repository at this point in the history
  • Loading branch information
Jason Posthuma authored Feb 25, 2021
2 parents c4aadc9 + b2b525d commit 2eecdb5
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 3 deletions.
40 changes: 40 additions & 0 deletions spec/ParseUser.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const request = require('../lib/request');
const passwordCrypto = require('../lib/password');
const Config = require('../lib/Config');
const cryptoUtils = require('../lib/cryptoUtils');
const UsersRouter = require('../lib/Routers/UsersRouter');

function verifyACL(user) {
const ACL = user.getACL();
Expand Down Expand Up @@ -3925,6 +3926,45 @@ describe('Parse.User testing', () => {
}
});

describe('UsersRouter.handleLogIn', () => {
it('should work with valid userFromJWT', async done => {
const user = await Parse.User.signUp('some_user', 'some_password');

const fakeReq = {
userFromJWT: user,
config: Config.get('test'),
info: {},
};

const { response = {} } = await new UsersRouter.UsersRouter().handleLogIn(fakeReq);

expect(user.id).toEqual(response.objectId);
expect(response.sessionToken).toBeTruthy();
done();
});

it('should fail with non-existing userFromJWT', async done => {
const user = new Parse.User({
username: 'not_a_real_user',
id: '1234567',
});

const fakeReq = {
userFromJWT: user,
config: Config.get('test'),
info: {},
};

try {
await new UsersRouter.UsersRouter().handleLogIn(fakeReq);
fail('User login should not have succeeded');
} catch (error) {
expect(error.code).toEqual(101);
}
done();
});
});

describe('issue #4897', () => {
it_only_db('mongo')('should be able to login with a legacy user (no ACL)', async () => {
// This issue is a side effect of the locked users and legacy users which don't have ACL's
Expand Down
41 changes: 38 additions & 3 deletions src/Routers/UsersRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,34 @@ export class UsersRouter extends ClassesRouter {
});
}

/**
* Validates JWT bearer token and looks up user `req.userFromJWT`. CRITICALLY IMPORTANT that the JWT has already been validated by this point (eg: express middleware, AWS API Gateway Authorizer)
* @param {Object} req The request
* @returns {Object} User object
*/
_authenticateUserFromRequestWithJwt(req) {
return new Promise((resolve, reject) => {
if (!req.userFromJWT) {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid credentials.');
}

const query = {
objectId: req.userFromJWT.id,
};
return req.config.database
.find('_User', query)
.then(results => {
if (!results.length)
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid credentials.');
const user = results[0];
resolve(user);
})
.catch(error => {
return reject(error);
});
});
}

handleMe(req) {
if (!req.info || !req.info.sessionToken) {
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Invalid session token');
Expand Down Expand Up @@ -171,10 +199,17 @@ export class UsersRouter extends ClassesRouter {
}

async handleLogIn(req) {
const user = await this._authenticateUserFromRequest(req);
let userFromJWT;
if (req.userFromJWT) {
// Could be just used `req.userFromJWT`, but the forced lookup
// Ensures the user hasn't been deleted since the the JWT was granted
userFromJWT = await this._authenticateUserFromRequestWithJwt(req);
}

const user = userFromJWT || (await this._authenticateUserFromRequest(req));

// handle password expiry policy
if (req.config.passwordPolicy && req.config.passwordPolicy.maxPasswordAge) {
// handle password expiry policy - ignore if user is managed in SSO (provided by JWT)
if (!userFromJWT && req.config.passwordPolicy && req.config.passwordPolicy.maxPasswordAge) {
let changedAt = user._password_changed_at;

if (!changedAt) {
Expand Down

0 comments on commit 2eecdb5

Please sign in to comment.