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
71 changes: 71 additions & 0 deletions spec/ParseGraphQLServer.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7083,6 +7083,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 = {
Expand Down
56 changes: 56 additions & 0 deletions src/GraphQL/loaders/usersMutations.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -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:
Expand Down