Skip to content
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

GraphQL: reset password with emailed token #7290

Merged
8 changes: 4 additions & 4 deletions spec/ParseGraphQLServer.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7065,8 +7065,8 @@ describe('ParseGraphQLServer', () => {
await Parse.User.logOut();
const result = await apolloClient.mutate({
mutation: gql`
mutation ResetPassword($input: ResetPasswordInput!) {
resetPassword(input: $input) {
mutation RequestResetPassword($input: RequestResetPasswordInput!) {
requestResetPassword(input: $input) {
clientMutationId
ok
}
Expand All @@ -7080,8 +7080,8 @@ describe('ParseGraphQLServer', () => {
},
});

expect(result.data.resetPassword.clientMutationId).toEqual(clientMutationId);
expect(result.data.resetPassword.ok).toBeTruthy();
expect(result.data.requestResetPassword.clientMutationId).toEqual(clientMutationId);
expect(result.data.requestResetPassword.ok).toBeTruthy();
});
it('should send verification email again', async () => {
const clientMutationId = uuidv4();
Expand Down
60 changes: 57 additions & 3 deletions src/GraphQL/loaders/usersMutations.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,10 +214,10 @@ const load = parseGraphQLSchema => {
parseGraphQLSchema.addGraphQLType(logOutMutation.type, true, true);
parseGraphQLSchema.addGraphQLMutation('logOut', logOutMutation, true, true);

const resetPasswordMutation = mutationWithClientMutationId({
name: 'ResetPassword',
const requestResetPasswordMutation = mutationWithClientMutationId({
name: 'RequestResetPassword',
description:
'The resetPassword mutation can be used to reset the password of an existing user.',
'The requestResetPassword mutation can be used to reset the password of an existing user.',
inputFields: {
email: {
descriptions: 'Email of the user that should receive the reset email',
Expand Down Expand Up @@ -246,6 +246,60 @@ const load = parseGraphQLSchema => {
},
});

parseGraphQLSchema.addGraphQLType(
requestResetPasswordMutation.args.input.type.ofType,
true,
true
);
parseGraphQLSchema.addGraphQLType(requestResetPasswordMutation.type, true, true);
parseGraphQLSchema.addGraphQLMutation(
'requestResetPassword',
requestResetPasswordMutation,
true,
true
);

const resetPasswordMutation = mutationWithClientMutationId({
name: 'ResetPassword',
description:
'The resetPassword mutation can be used to reset the password of an existing user.',
inputFields: {
username: {
descriptions: 'Username of the user that have received the reset email',
type: new GraphQLNonNull(GraphQLString),
},
password: {
descriptions: 'New password of the user',
type: new GraphQLNonNull(GraphQLString),
},
token: {
descriptions: 'Reset token that was emailed to the user',
type: new GraphQLNonNull(GraphQLString),
},
},
outputFields: {
ok: {
description: "It's always true.",
type: new GraphQLNonNull(GraphQLBoolean),
},
},
mutateAndGetPayload: async ({ username, password, token }, context) => {
const { config, auth, info } = context;
await usersRouter.handleResetPassword({
body: {
username,
password,
token,
},
config,
auth,
info,
});

return { ok: true };
},
});

parseGraphQLSchema.addGraphQLType(resetPasswordMutation.args.input.type.ofType, true, true);
parseGraphQLSchema.addGraphQLType(resetPasswordMutation.type, true, true);
parseGraphQLSchema.addGraphQLMutation('resetPassword', resetPasswordMutation, true, true);
Expand Down
27 changes: 27 additions & 0 deletions src/Routers/UsersRouter.js
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,30 @@ export class UsersRouter extends ClassesRouter {
);
}

handleResetPassword(req) {
davimacedo marked this conversation as resolved.
Show resolved Hide resolved
const { username, password, token } = req.body;
if (!username) {
throw new Parse.Error(Parse.Error.USERNAME_MISSING, 'you must provide a username');
}
if (!password) {
throw new Parse.Error(Parse.Error.PASSWORD_MISSING, 'you must provide a password');
}
if (typeof password !== 'string' || typeof username !== 'string') {
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Invalid username/password.');
}
const userController = req.config.userController;
return userController.updatePassword(username, token, password).then(
() => {
return Promise.resolve({
response: {},
});
},
err => {
throw err;
}
);
}

handleVerificationEmailRequest(req) {
this._throwOnBadEmailConfig(req);

Expand Down Expand Up @@ -423,6 +447,9 @@ export class UsersRouter extends ClassesRouter {
this.route('POST', '/requestPasswordReset', req => {
return this.handleResetRequest(req);
});
this.route('POST', '/resetPassword', req => {
return this.handleResetPassword(req);
});
this.route('POST', '/verificationEmailRequest', req => {
return this.handleVerificationEmailRequest(req);
});
Expand Down