From a3a00d4ab1d1e6ee5b968a984cf514b80a4702e9 Mon Sep 17 00:00:00 2001 From: Prerna Mehra Date: Mon, 29 Mar 2021 10:15:41 +0530 Subject: [PATCH] GraphQL: reset password with emailed token (#7290) * renamed "resetPassword" to "requestResetPassword" & created new "resetPassword" mutation * added new route to handle resetPassword in UsersRouter.js * updated resetPassword test to "requestResetPassword" mutation * updated "resetPassword" mutation args description * changed token arg description to rerun the tests * directly using updatePassword for resetPassword * removed handleResetPassword from UsersRouter.js file * added test case for reset Password * changed mutation names to "resetPassword" & "confirmResetPassword" * changed mutation names in test also --- spec/ParseGraphQLServer.spec.js | 71 +++++++++++++++++++++++++++ src/GraphQL/loaders/usersMutations.js | 56 +++++++++++++++++++++ 2 files changed, 127 insertions(+) diff --git a/spec/ParseGraphQLServer.spec.js b/spec/ParseGraphQLServer.spec.js index 709d5aaa25..da8c89adb5 100644 --- a/spec/ParseGraphQLServer.spec.js +++ b/spec/ParseGraphQLServer.spec.js @@ -7086,6 +7086,77 @@ describe('ParseGraphQLServer', () => { expect(result.data.resetPassword.clientMutationId).toEqual(clientMutationId); expect(result.data.resetPassword.ok).toBeTruthy(); }); + + it('should reset password', async () => { + const clientMutationId = uuidv4(); + let resetPasswordToken; + const emailAdapter = { + sendVerificationEmail: () => {}, + sendPasswordResetEmail: ({ link }) => { + resetPasswordToken = link.split('token=')[1].split('&')[0]; + }, + sendMail: () => {}, + }; + parseServer = await global.reconfigureServer({ + appName: 'test', + emailAdapter: emailAdapter, + publicServerURL: 'http://localhost:13377/parse', + auth: { + myAuth: { + module: global.mockCustomAuthenticator('parse', 'graphql'), + }, + }, + }); + const user = new Parse.User(); + user.setUsername('user1'); + user.setPassword('user1'); + user.setEmail('user1@user1.user1'); + await user.signUp(); + await Parse.User.logOut(); + await Parse.User.requestPasswordReset('user1@user1.user1'); + await apolloClient.mutate({ + mutation: gql` + mutation ConfirmResetPassword($input: ConfirmResetPasswordInput!) { + confirmResetPassword(input: $input) { + clientMutationId + ok + } + } + `, + variables: { + input: { + clientMutationId, + username: 'user1', + password: 'newPassword', + token: resetPasswordToken, + }, + }, + }); + const result = await apolloClient.mutate({ + mutation: gql` + mutation LogInUser($input: LogInInput!) { + logIn(input: $input) { + clientMutationId + viewer { + sessionToken + } + } + } + `, + variables: { + input: { + clientMutationId, + username: 'user1', + password: 'newPassword', + }, + }, + }); + + expect(result.data.logIn.clientMutationId).toEqual(clientMutationId); + expect(result.data.logIn.viewer.sessionToken).toBeDefined(); + expect(typeof result.data.logIn.viewer.sessionToken).toBe('string'); + }); + it('should send verification email again', async () => { const clientMutationId = uuidv4(); const emailAdapter = { diff --git a/src/GraphQL/loaders/usersMutations.js b/src/GraphQL/loaders/usersMutations.js index 9c57c85125..50cb241b68 100644 --- a/src/GraphQL/loaders/usersMutations.js +++ b/src/GraphQL/loaders/usersMutations.js @@ -5,6 +5,7 @@ import * as objectsMutations from '../helpers/objectsMutations'; import { OBJECT } from './defaultGraphQLTypes'; import { getUserFromSessionToken } from './usersQueries'; import { transformTypes } from '../transformers/mutation'; +import Parse from 'parse/node'; const usersRouter = new UsersRouter(); @@ -250,6 +251,61 @@ const load = parseGraphQLSchema => { parseGraphQLSchema.addGraphQLType(resetPasswordMutation.type, true, true); parseGraphQLSchema.addGraphQLMutation('resetPassword', resetPasswordMutation, true, true); + const confirmResetPasswordMutation = mutationWithClientMutationId({ + name: 'ConfirmResetPassword', + description: + 'The confirmResetPassword 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 } = context; + 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 (!token) { + throw new Parse.Error(Parse.Error.OTHER_CAUSE, 'you must provide a token'); + } + + const userController = config.userController; + await userController.updatePassword(username, token, password); + return { ok: true }; + }, + }); + + parseGraphQLSchema.addGraphQLType( + confirmResetPasswordMutation.args.input.type.ofType, + true, + true + ); + parseGraphQLSchema.addGraphQLType(confirmResetPasswordMutation.type, true, true); + parseGraphQLSchema.addGraphQLMutation( + 'confirmResetPassword', + confirmResetPasswordMutation, + true, + true + ); + const sendVerificationEmailMutation = mutationWithClientMutationId({ name: 'SendVerificationEmail', description: