implements proper reset password workflow within admin panel and sets cookie properly
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
const parseCookies = require('../utilities/parseCookies');
|
||||
|
||||
const getExtractJWT = config => (req) => {
|
||||
const getExtractJWT = (config) => (req) => {
|
||||
const jwtFromHeader = req.get('Authorization');
|
||||
|
||||
if (jwtFromHeader && jwtFromHeader.indexOf('JWT ') === 0) {
|
||||
|
||||
@@ -8,6 +8,7 @@ function resetPassword(collection) {
|
||||
collection,
|
||||
data: args,
|
||||
req: context.req,
|
||||
res: context.res,
|
||||
api: 'GraphQL',
|
||||
};
|
||||
|
||||
|
||||
@@ -65,10 +65,9 @@ async function login(args) {
|
||||
}, {
|
||||
email,
|
||||
id: user.id,
|
||||
collection: collectionConfig.slug,
|
||||
});
|
||||
|
||||
fieldsToSign.collection = collectionConfig.slug;
|
||||
|
||||
const token = jwt.sign(
|
||||
fieldsToSign,
|
||||
config.secret,
|
||||
|
||||
@@ -2,31 +2,21 @@ async function logout(args) {
|
||||
const { config } = this;
|
||||
|
||||
const {
|
||||
collection: {
|
||||
config: collectionConfig,
|
||||
},
|
||||
res,
|
||||
req,
|
||||
} = args;
|
||||
|
||||
const cookieOptions = {
|
||||
expires: new Date(0),
|
||||
httpOnly: true,
|
||||
path: '/',
|
||||
overwrite: true,
|
||||
};
|
||||
|
||||
if (collectionConfig.auth && collectionConfig.auth.secureCookie) {
|
||||
cookieOptions.secure = true;
|
||||
}
|
||||
|
||||
if (req.headers.origin && req.headers.origin.indexOf('localhost') === -1) {
|
||||
let domain = req.headers.origin.replace('https://', '');
|
||||
domain = domain.replace('http://', '');
|
||||
cookieOptions.domain = domain;
|
||||
}
|
||||
|
||||
res.cookie(`${config.cookiePrefix}-token`, '', cookieOptions);
|
||||
res.clearCookie(`${config.cookiePrefix}-token`, cookieOptions);
|
||||
|
||||
return 'Logged out successfully.';
|
||||
}
|
||||
|
||||
@@ -33,8 +33,6 @@ async function resetPassword(args) {
|
||||
data,
|
||||
} = options;
|
||||
|
||||
const { email } = data;
|
||||
|
||||
const user = await Model.findOne({
|
||||
resetPasswordToken: data.token,
|
||||
resetPasswordExpiration: { $gt: Date.now() },
|
||||
@@ -42,7 +40,6 @@ async function resetPassword(args) {
|
||||
|
||||
if (!user) throw new APIError('Token is either invalid or has expired.');
|
||||
|
||||
|
||||
await user.setPassword(data.password);
|
||||
|
||||
user.resetPasswordExpiration = Date.now();
|
||||
@@ -60,7 +57,9 @@ async function resetPassword(args) {
|
||||
}
|
||||
return signedFields;
|
||||
}, {
|
||||
email,
|
||||
email: user.email,
|
||||
id: user.id,
|
||||
collection: collectionConfig.slug,
|
||||
});
|
||||
|
||||
const token = jwt.sign(
|
||||
@@ -71,6 +70,25 @@ async function resetPassword(args) {
|
||||
},
|
||||
);
|
||||
|
||||
if (args.res) {
|
||||
const cookieOptions = {
|
||||
path: '/',
|
||||
httpOnly: true,
|
||||
};
|
||||
|
||||
if (collectionConfig.auth.secureCookie) {
|
||||
cookieOptions.secure = true;
|
||||
}
|
||||
|
||||
if (args.req.headers.origin && args.req.headers.origin.indexOf('localhost') === -1) {
|
||||
let domain = args.req.headers.origin.replace('https://', '');
|
||||
domain = domain.replace('http://', '');
|
||||
cookieOptions.domain = domain;
|
||||
}
|
||||
|
||||
args.res.cookie(`${config.cookiePrefix}-token`, token, cookieOptions);
|
||||
}
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 3. Execute after reset password hook
|
||||
// /////////////////////////////////////
|
||||
|
||||
@@ -4,13 +4,14 @@ async function resetPassword(req, res, next) {
|
||||
try {
|
||||
const token = await this.operations.collections.auth.resetPassword({
|
||||
req,
|
||||
res,
|
||||
collection: req.collection,
|
||||
data: req.body,
|
||||
});
|
||||
|
||||
return res.status(httpStatus.OK)
|
||||
.json({
|
||||
message: 'Password reset',
|
||||
message: 'Password reset successfully.',
|
||||
token,
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
@@ -14,6 +14,7 @@ const ConfirmPassword = () => {
|
||||
if (value === password?.value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return 'Passwords do not match.';
|
||||
}, [password]);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import useFieldType from '../../useFieldType';
|
||||
import withCondition from '../../withCondition';
|
||||
@@ -7,16 +7,21 @@ const HiddenInput = (props) => {
|
||||
const {
|
||||
name,
|
||||
path: pathFromProps,
|
||||
required,
|
||||
value: valueFromProps,
|
||||
} = props;
|
||||
|
||||
const path = pathFromProps || name;
|
||||
|
||||
const { value, setValue } = useFieldType({
|
||||
path,
|
||||
required,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (valueFromProps !== undefined) {
|
||||
setValue(valueFromProps);
|
||||
}
|
||||
}, [valueFromProps, setValue]);
|
||||
|
||||
return (
|
||||
<input
|
||||
type="hidden"
|
||||
@@ -28,14 +33,14 @@ const HiddenInput = (props) => {
|
||||
};
|
||||
|
||||
HiddenInput.defaultProps = {
|
||||
required: false,
|
||||
path: '',
|
||||
value: undefined,
|
||||
};
|
||||
|
||||
HiddenInput.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
path: PropTypes.string,
|
||||
required: PropTypes.bool,
|
||||
value: PropTypes.string,
|
||||
};
|
||||
|
||||
export default withCondition(HiddenInput);
|
||||
|
||||
@@ -69,14 +69,6 @@ const ForgotPassword = () => {
|
||||
<p>
|
||||
Check your email for a link that will allow you to securely reset your password.
|
||||
</p>
|
||||
<br />
|
||||
<Button
|
||||
el="link"
|
||||
buttonStyle="secondary"
|
||||
to={`${admin}/login`}
|
||||
>
|
||||
Go to login
|
||||
</Button>
|
||||
</MinimalTemplate>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import React from 'react';
|
||||
import { Link, useHistory, useParams } from 'react-router-dom';
|
||||
import config from 'payload/config';
|
||||
import StatusList from '../../elements/Status';
|
||||
import MinimalTemplate from '../../templates/Minimal';
|
||||
import Form from '../../forms/Form';
|
||||
import Password from '../../forms/field-types/Password';
|
||||
import ConfirmPassword from '../../forms/field-types/ConfirmPassword';
|
||||
import FormSubmit from '../../forms/Submit';
|
||||
import Button from '../../elements/Button';
|
||||
import { useUser } from '../../data/User';
|
||||
@@ -20,19 +21,16 @@ const ResetPassword = () => {
|
||||
const history = useHistory();
|
||||
const { user, setToken } = useUser();
|
||||
|
||||
const handleResponse = (res) => {
|
||||
res.json()
|
||||
.then((data) => {
|
||||
if (data.token) {
|
||||
setToken(data.token);
|
||||
history.push(`${admin}`);
|
||||
}
|
||||
});
|
||||
const onSuccess = (data) => {
|
||||
if (data.token) {
|
||||
setToken(data.token);
|
||||
history.push(`${admin}`);
|
||||
}
|
||||
};
|
||||
|
||||
if (user) {
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
<MinimalTemplate className={baseClass}>
|
||||
<div className={`${baseClass}__wrap`}>
|
||||
<h1>Already logged in</h1>
|
||||
<p>
|
||||
@@ -51,34 +49,35 @@ const ResetPassword = () => {
|
||||
Back to Dashboard
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</MinimalTemplate>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={baseClass}>
|
||||
<MinimalTemplate className={baseClass}>
|
||||
<div className={`${baseClass}__wrap`}>
|
||||
<StatusList />
|
||||
<h1>Reset Password</h1>
|
||||
<Form
|
||||
handleResponse={handleResponse}
|
||||
onSuccess={onSuccess}
|
||||
method="POST"
|
||||
action={`${serverURL}${api}/${userSlug}/reset-password`}
|
||||
redirect={admin}
|
||||
>
|
||||
<Password
|
||||
error="password"
|
||||
label="Password"
|
||||
label="New Password"
|
||||
name="password"
|
||||
required
|
||||
/>
|
||||
<ConfirmPassword />
|
||||
<HiddenInput
|
||||
name="token"
|
||||
defaultValue={token}
|
||||
value={token}
|
||||
/>
|
||||
<FormSubmit>Reset Password</FormSubmit>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</MinimalTemplate>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ function registerCollections() {
|
||||
.post(forgotPassword);
|
||||
|
||||
router
|
||||
.route(`${slug}/reset-password`)
|
||||
.route(`/${slug}/reset-password`)
|
||||
.post(resetPassword);
|
||||
|
||||
router
|
||||
|
||||
Reference in New Issue
Block a user