Skip to content

Commit 5d9bf24

Browse files
authored
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
1 parent bc08b54 commit 5d9bf24

File tree

2 files changed

+127
-0
lines changed

2 files changed

+127
-0
lines changed

spec/ParseGraphQLServer.spec.js

+71
Original file line numberDiff line numberDiff line change
@@ -7083,6 +7083,77 @@ describe('ParseGraphQLServer', () => {
70837083
expect(result.data.resetPassword.clientMutationId).toEqual(clientMutationId);
70847084
expect(result.data.resetPassword.ok).toBeTruthy();
70857085
});
7086+
7087+
it('should reset password', async () => {
7088+
const clientMutationId = uuidv4();
7089+
let resetPasswordToken;
7090+
const emailAdapter = {
7091+
sendVerificationEmail: () => {},
7092+
sendPasswordResetEmail: ({ link }) => {
7093+
resetPasswordToken = link.split('token=')[1].split('&')[0];
7094+
},
7095+
sendMail: () => {},
7096+
};
7097+
parseServer = await global.reconfigureServer({
7098+
appName: 'test',
7099+
emailAdapter: emailAdapter,
7100+
publicServerURL: 'http://localhost:13377/parse',
7101+
auth: {
7102+
myAuth: {
7103+
module: global.mockCustomAuthenticator('parse', 'graphql'),
7104+
},
7105+
},
7106+
});
7107+
const user = new Parse.User();
7108+
user.setUsername('user1');
7109+
user.setPassword('user1');
7110+
user.setEmail('user1@user1.user1');
7111+
await user.signUp();
7112+
await Parse.User.logOut();
7113+
await Parse.User.requestPasswordReset('user1@user1.user1');
7114+
await apolloClient.mutate({
7115+
mutation: gql`
7116+
mutation ConfirmResetPassword($input: ConfirmResetPasswordInput!) {
7117+
confirmResetPassword(input: $input) {
7118+
clientMutationId
7119+
ok
7120+
}
7121+
}
7122+
`,
7123+
variables: {
7124+
input: {
7125+
clientMutationId,
7126+
username: 'user1',
7127+
password: 'newPassword',
7128+
token: resetPasswordToken,
7129+
},
7130+
},
7131+
});
7132+
const result = await apolloClient.mutate({
7133+
mutation: gql`
7134+
mutation LogInUser($input: LogInInput!) {
7135+
logIn(input: $input) {
7136+
clientMutationId
7137+
viewer {
7138+
sessionToken
7139+
}
7140+
}
7141+
}
7142+
`,
7143+
variables: {
7144+
input: {
7145+
clientMutationId,
7146+
username: 'user1',
7147+
password: 'newPassword',
7148+
},
7149+
},
7150+
});
7151+
7152+
expect(result.data.logIn.clientMutationId).toEqual(clientMutationId);
7153+
expect(result.data.logIn.viewer.sessionToken).toBeDefined();
7154+
expect(typeof result.data.logIn.viewer.sessionToken).toBe('string');
7155+
});
7156+
70867157
it('should send verification email again', async () => {
70877158
const clientMutationId = uuidv4();
70887159
const emailAdapter = {

src/GraphQL/loaders/usersMutations.js

+56
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import * as objectsMutations from '../helpers/objectsMutations';
55
import { OBJECT } from './defaultGraphQLTypes';
66
import { getUserFromSessionToken } from './usersQueries';
77
import { transformTypes } from '../transformers/mutation';
8+
import Parse from 'parse/node';
89

910
const usersRouter = new UsersRouter();
1011

@@ -250,6 +251,61 @@ const load = parseGraphQLSchema => {
250251
parseGraphQLSchema.addGraphQLType(resetPasswordMutation.type, true, true);
251252
parseGraphQLSchema.addGraphQLMutation('resetPassword', resetPasswordMutation, true, true);
252253

254+
const confirmResetPasswordMutation = mutationWithClientMutationId({
255+
name: 'ConfirmResetPassword',
256+
description:
257+
'The confirmResetPassword mutation can be used to reset the password of an existing user.',
258+
inputFields: {
259+
username: {
260+
descriptions: 'Username of the user that have received the reset email',
261+
type: new GraphQLNonNull(GraphQLString),
262+
},
263+
password: {
264+
descriptions: 'New password of the user',
265+
type: new GraphQLNonNull(GraphQLString),
266+
},
267+
token: {
268+
descriptions: 'Reset token that was emailed to the user',
269+
type: new GraphQLNonNull(GraphQLString),
270+
},
271+
},
272+
outputFields: {
273+
ok: {
274+
description: "It's always true.",
275+
type: new GraphQLNonNull(GraphQLBoolean),
276+
},
277+
},
278+
mutateAndGetPayload: async ({ username, password, token }, context) => {
279+
const { config } = context;
280+
if (!username) {
281+
throw new Parse.Error(Parse.Error.USERNAME_MISSING, 'you must provide a username');
282+
}
283+
if (!password) {
284+
throw new Parse.Error(Parse.Error.PASSWORD_MISSING, 'you must provide a password');
285+
}
286+
if (!token) {
287+
throw new Parse.Error(Parse.Error.OTHER_CAUSE, 'you must provide a token');
288+
}
289+
290+
const userController = config.userController;
291+
await userController.updatePassword(username, token, password);
292+
return { ok: true };
293+
},
294+
});
295+
296+
parseGraphQLSchema.addGraphQLType(
297+
confirmResetPasswordMutation.args.input.type.ofType,
298+
true,
299+
true
300+
);
301+
parseGraphQLSchema.addGraphQLType(confirmResetPasswordMutation.type, true, true);
302+
parseGraphQLSchema.addGraphQLMutation(
303+
'confirmResetPassword',
304+
confirmResetPasswordMutation,
305+
true,
306+
true
307+
);
308+
253309
const sendVerificationEmailMutation = mutationWithClientMutationId({
254310
name: 'SendVerificationEmail',
255311
description:

0 commit comments

Comments
 (0)