diff --git a/src/actions/auth/reducer.actions.js b/src/actions/auth/reducer.actions.js
index a9ac78b..9425c6f 100644
--- a/src/actions/auth/reducer.actions.js
+++ b/src/actions/auth/reducer.actions.js
@@ -30,12 +30,16 @@ export function setAuthRequestError(exception) {
error["Email"] = error.email;
delete error["email"];
}
+ if (error.password) {
+ error["Password"] = error.password;
+ delete error["password"];
+ }
if (error.password1) {
error["Password"] = error.password1;
delete error["password1"];
}
if (error.password2) {
- error["Password Confirmation"] = error.password2;
+ error["Confirm Password"] = error.password2;
delete error["password2"];
}
if (error.non_field_errors) {
@@ -58,6 +62,14 @@ export function setAuthRequestError(exception) {
error["Confirm New Password"] = error.new_password2;
delete error["new_password2"];
}
+ if (error.token) {
+ error["Token"] = error.token;
+ delete error["token"];
+ }
+ if (error.uid) {
+ error["UID"] = error.uid;
+ delete error["uid"];
+ }
return {
type: SET_AUTH_REQUEST_ERROR,
diff --git a/src/actions/auth/saga.actions.js b/src/actions/auth/saga.actions.js
index 16a5032..528e0a8 100644
--- a/src/actions/auth/saga.actions.js
+++ b/src/actions/auth/saga.actions.js
@@ -3,7 +3,9 @@ import {
SEND_EMAIL_VERIFICATION_REQUEST,
SEND_LOGIN_REQUEST,
SEND_LOGOUT_REQUEST,
- SEND_CHANGE_PASSWORD_REQUEST
+ SEND_CHANGE_PASSWORD_REQUEST,
+ SEND_FORGOT_PASSWORD_REQUEST,
+ SEND_RESET_PASSWORD_REQUEST
} from "../../constants/auth.constants";
export function sendRegisterRequest(postBody) {
@@ -30,12 +32,26 @@ export function sendLoginRequest(postBody) {
export function sendLogoutRequest() {
return {
type: SEND_LOGOUT_REQUEST
- }
+ };
}
export function sendChangePasswordRequest(postBody) {
return {
type: SEND_CHANGE_PASSWORD_REQUEST,
data: postBody
- }
+ };
+}
+
+export function sendForgotPasswordRequest(postBody) {
+ return {
+ type: SEND_FORGOT_PASSWORD_REQUEST,
+ data: postBody
+ };
+}
+
+export function sendResetPasswordRequest(postBody) {
+ return {
+ type: SEND_RESET_PASSWORD_REQUEST,
+ data: postBody
+ };
}
diff --git a/src/api/auth.api.js b/src/api/auth.api.js
index 9ff8c3d..ee083a3 100644
--- a/src/api/auth.api.js
+++ b/src/api/auth.api.js
@@ -55,3 +55,29 @@ export function changePassword(new_password1, new_password2, old_password) {
old_password
}).then(resp => Promise.resolve(resp));
}
+
+/**
+ * Function wrapping POST request for a forget password email call
+ * @param {string} email - email of user for password reset
+ */
+export function forgotPassword(email) {
+ return post("/rest-auth/password/reset/", { email }).then(resp =>
+ Promise.resolve(resp)
+ );
+}
+
+/**
+ * Function wrapping POST request for resetting a user's password
+ * @param {string} uid - uid supplied by forgot password email
+ * @param {string} token - token supplied by forgot password email
+ * @param {string} new_password1 - user new password
+ * @param {string} new_password2 - user new password confirmation
+ */
+export function resetPassword(uid, token, new_password1, new_password2) {
+ return post("/rest-auth/password/reset/confirm/", {
+ uid,
+ token,
+ new_password1,
+ new_password2
+ }).then(resp => Promise.resolve(resp));
+}
diff --git a/src/components/App.jsx b/src/components/App.jsx
index e444629..29e178b 100644
--- a/src/components/App.jsx
+++ b/src/components/App.jsx
@@ -1,8 +1,10 @@
import React, { Component } from "react";
import { Route, Switch } from "react-router-dom";
+import ForgotPassword from "./Auth/ForgotPassword";
import Login from "./Auth/Login";
import Register from "./Auth/Register";
+import ResetPassword from "./Auth/ResetPassword";
import Settings from "./Auth/Settings";
import VerifyEmail from "./Auth/VerifyEmail";
import PrivateRoute from "./Shared/PrivateRoute";
@@ -34,6 +36,8 @@ class App extends Component {
component={VerifyEmail}
/>
+
+
diff --git a/src/components/Auth/ForgotPassword.jsx b/src/components/Auth/ForgotPassword.jsx
new file mode 100644
index 0000000..d85c135
--- /dev/null
+++ b/src/components/Auth/ForgotPassword.jsx
@@ -0,0 +1,98 @@
+import React, { Component } from "react";
+import { connect } from "react-redux";
+import { Link, Redirect } from "react-router-dom";
+import { Button, Container, Form, Header, Message } from "semantic-ui-react";
+
+import {
+ clearAuthRequestError,
+ clearAuthRequestSuccess,
+ setFormEmail
+} from "../../actions/auth/reducer.actions";
+import { sendForgotPasswordRequest } from "../../actions/auth/saga.actions";
+import Error from "../Shared/Error";
+
+class ForgotPassword extends Component {
+ componentWillMount() {
+ this.props.dispatch(clearAuthRequestError());
+ this.props.dispatch(clearAuthRequestSuccess());
+ }
+
+ changeEmail = event => {
+ this.props.dispatch(setFormEmail(event.target.value));
+ };
+
+ onSubmitForgotPassword = event => {
+ event.preventDefault();
+ const { dispatch, email } = this.props;
+ dispatch(sendForgotPasswordRequest({ email }));
+ };
+
+ render() {
+ const {
+ isSendingAuthRequest,
+ authRequestError,
+ authRequestSuccess,
+ email,
+ userToken
+ } = this.props;
+ if (userToken) return ;
+ return (
+
+ );
+ }
+}
+
+function mapStateToProps(state) {
+ return { ...state.auth };
+}
+
+const ForgotPasswordView = ({
+ isSendingAuthRequest,
+ authRequestError,
+ authRequestSuccess,
+ email,
+ changeEmail,
+ onSubmitForgotPassword
+}) => (
+
+
+ Enter your email and we will send you a link to reset your password.
+
+
+
+
+
+
+ Password Reset Sent!
+ If the email exists, a password reset link will be sent to it.
+
+
+
+
+
+
+
+);
+
+export default connect(mapStateToProps)(ForgotPassword);
diff --git a/src/components/Auth/Login.jsx b/src/components/Auth/Login.jsx
index 5048364..3d819c8 100644
--- a/src/components/Auth/Login.jsx
+++ b/src/components/Auth/Login.jsx
@@ -1,7 +1,7 @@
import React, { Component } from "react";
import { connect } from "react-redux";
-import { Redirect } from "react-router-dom";
-import { Container, Form, Header, Message } from "semantic-ui-react";
+import { Link, Redirect } from "react-router-dom";
+import { Button, Container, Form, Header, Message } from "semantic-ui-react";
import {
clearAuthRequestError,
@@ -102,7 +102,12 @@ const LoginView = ({
Login successful!
Redirecting you now...
- Login
+
+
+
+
);
diff --git a/src/components/Auth/Register.jsx b/src/components/Auth/Register.jsx
index 582a790..49637dc 100644
--- a/src/components/Auth/Register.jsx
+++ b/src/components/Auth/Register.jsx
@@ -117,7 +117,7 @@ const RegisterView = ({
/>
-
+
Confirmation Email Sent!
Please check your email to confirm your registration.
- Register
+ Register
);
diff --git a/src/components/Auth/ResetPassword.jsx b/src/components/Auth/ResetPassword.jsx
new file mode 100644
index 0000000..80e31ca
--- /dev/null
+++ b/src/components/Auth/ResetPassword.jsx
@@ -0,0 +1,118 @@
+import React, { Component } from "react";
+import { connect } from "react-redux";
+import { Link, Redirect } from "react-router-dom";
+import { Container, Form, Header, Message } from "semantic-ui-react";
+
+import {
+ clearAuthRequestError,
+ clearAuthRequestSuccess,
+ setFormPassword,
+ setFormPasswordConfirmation
+} from "../../actions/auth/reducer.actions";
+import { sendResetPasswordRequest } from "../../actions/auth/saga.actions";
+import Error from "../Shared/Error";
+
+class ResetPassword extends Component {
+ componentWillMount() {
+ this.props.dispatch(clearAuthRequestError());
+ this.props.dispatch(clearAuthRequestSuccess());
+ }
+
+ changePassword = event => {
+ this.props.dispatch(setFormPassword(event.target.value));
+ };
+
+ changePasswordConfirmation = event => {
+ this.props.dispatch(setFormPasswordConfirmation(event.target.value));
+ };
+
+ onSubmitResetPassword = event => {
+ event.preventDefault();
+ const { dispatch, password, passwordConfirmation } = this.props;
+ const { uid, token } = this.props.match.params;
+ dispatch(
+ sendResetPasswordRequest({
+ uid,
+ token,
+ new_password1: password,
+ new_password2: passwordConfirmation
+ })
+ );
+ };
+
+ render() {
+ const {
+ isSendingAuthRequest,
+ authRequestError,
+ authRequestSuccess,
+ password,
+ passwordConfirmation,
+ userToken
+ } = this.props;
+ if (userToken) return ;
+ return (
+
+ );
+ }
+}
+
+function mapStateToProps(state) {
+ return { ...state.auth };
+}
+
+const ResetPasswordView = ({
+ isSendingAuthRequest,
+ authRequestError,
+ authRequestSuccess,
+ password,
+ passwordConfirmation,
+ changePassword,
+ changePasswordConfirmation,
+ onSubmitResetPassword
+}) => (
+
+
+
+
+
+
+
+
+
+
+
+
+ Password Reset!
+ You may now log in with the new credentials.
+
+ Reset Password{" "}
+
+
+);
+
+export default connect(mapStateToProps)(ResetPassword);
diff --git a/src/components/Auth/Settings.jsx b/src/components/Auth/Settings.jsx
index 9ca9570..a05db1b 100644
--- a/src/components/Auth/Settings.jsx
+++ b/src/components/Auth/Settings.jsx
@@ -131,7 +131,7 @@ const SettingsView = ({
Password Successfully Changed!
New password has been set.
- Change Password
+ Change Password
diff --git a/src/components/Auth/VerifyEmail.jsx b/src/components/Auth/VerifyEmail.jsx
index 2d07b95..e6e8c00 100644
--- a/src/components/Auth/VerifyEmail.jsx
+++ b/src/components/Auth/VerifyEmail.jsx
@@ -88,7 +88,7 @@ const VerifyEmailView = ({
Email Verified!
Please proceed to log in.
- Verify Email
+ Verify Email
);
diff --git a/src/constants/auth.constants.js b/src/constants/auth.constants.js
index a89c0bd..0a645ca 100644
--- a/src/constants/auth.constants.js
+++ b/src/constants/auth.constants.js
@@ -23,3 +23,5 @@ export const SEND_EMAIL_VERIFICATION_REQUEST =
export const SEND_LOGIN_REQUEST = "SEND_LOGIN_REQUEST";
export const SEND_LOGOUT_REQUEST = "SEND_LOGOUT_REQUEST";
export const SEND_CHANGE_PASSWORD_REQUEST = "SEND_CHANGE_PASSWORD_REQUEST";
+export const SEND_FORGOT_PASSWORD_REQUEST = "SEND_FORGOT_PASSWORD_REQUEST";
+export const SEND_RESET_PASSWORD_REQUEST = "SEND_RESET_PASSWORD_REQUEST";
diff --git a/src/sagas/auth.sagas.js b/src/sagas/auth.sagas.js
index ff0f4d7..11d135a 100644
--- a/src/sagas/auth.sagas.js
+++ b/src/sagas/auth.sagas.js
@@ -14,14 +14,16 @@ import {
setFormPassword,
setFormPasswordConfirmation,
setFormEmailVerification,
- setFormOldPassword,
+ setFormOldPassword
} from "../actions/auth/reducer.actions";
import {
registerUser,
verifyEmail,
loginUser,
logoutUser,
- changePassword
+ changePassword,
+ forgotPassword,
+ resetPassword
} from "../api/auth.api";
function* registerUserCall(postBody) {
@@ -71,7 +73,44 @@ function* changePasswordCall(postBody) {
yield effects.put(isSendingAuthRequest(true));
const { new_password1, new_password2, old_password } = postBody;
try {
- return yield effects.call(changePassword, new_password1, new_password2, old_password);
+ return yield effects.call(
+ changePassword,
+ new_password1,
+ new_password2,
+ old_password
+ );
+ } catch (exception) {
+ yield effects.put(setAuthRequestError(exception));
+ return false;
+ } finally {
+ yield effects.put(isSendingAuthRequest(false));
+ }
+}
+
+function* forgotPasswordCall(postBody) {
+ yield effects.put(isSendingAuthRequest(true));
+ const { email } = postBody;
+ try {
+ return yield effects.call(forgotPassword, email);
+ } catch (exception) {
+ yield effects.put(setAuthRequestError(exception));
+ return false;
+ } finally {
+ yield effects.put(isSendingAuthRequest(false));
+ }
+}
+
+function* resetPasswordCall(postBody) {
+ yield effects.put(isSendingAuthRequest(true));
+ const { uid, token, new_password1, new_password2 } = postBody;
+ try {
+ return yield effects.call(
+ resetPassword,
+ uid,
+ token,
+ new_password1,
+ new_password2
+ );
} catch (exception) {
yield effects.put(setAuthRequestError(exception));
return false;
@@ -137,3 +176,25 @@ export function* changePasswordFlow(request) {
yield effects.put(setFormPasswordConfirmation(""));
}
}
+
+export function* forgotPasswordFlow(request) {
+ yield effects.put(clearAuthRequestSuccess());
+ yield effects.put(clearAuthRequestError());
+ const wasSuccessful = yield effects.call(forgotPasswordCall, request.data);
+ if (wasSuccessful) {
+ yield effects.put(setAuthRequestSuccess(wasSuccessful));
+ yield effects.put(clearAuthRequestError());
+ }
+}
+
+export function* resetPasswordFlow(request) {
+ yield effects.put(clearAuthRequestSuccess());
+ yield effects.put(clearAuthRequestError());
+ const wasSuccessful = yield effects.call(resetPasswordCall, request.data);
+ if (wasSuccessful) {
+ yield effects.put(setAuthRequestSuccess(wasSuccessful));
+ yield effects.put(clearAuthRequestError());
+ yield effects.put(setFormPassword(""));
+ yield effects.put(setFormPasswordConfirmation(""));
+ }
+}
diff --git a/src/sagas/index.js b/src/sagas/index.js
index 7367245..73f6d25 100644
--- a/src/sagas/index.js
+++ b/src/sagas/index.js
@@ -4,14 +4,18 @@ import {
SEND_EMAIL_VERIFICATION_REQUEST,
SEND_LOGIN_REQUEST,
SEND_LOGOUT_REQUEST,
- SEND_CHANGE_PASSWORD_REQUEST
+ SEND_CHANGE_PASSWORD_REQUEST,
+ SEND_FORGOT_PASSWORD_REQUEST,
+ SEND_RESET_PASSWORD_REQUEST
} from "../constants/auth.constants";
import {
registerUserFlow,
verifyEmailFlow,
loginUserFlow,
logoutUserFlow,
- changePasswordFlow
+ changePasswordFlow,
+ forgotPasswordFlow,
+ resetPasswordFlow,
} from "./auth.sagas";
export default function* rootSaga() {
@@ -20,4 +24,6 @@ export default function* rootSaga() {
yield takeLatest(SEND_LOGIN_REQUEST, loginUserFlow);
yield takeLatest(SEND_LOGOUT_REQUEST, logoutUserFlow);
yield takeLatest(SEND_CHANGE_PASSWORD_REQUEST, changePasswordFlow);
+ yield takeLatest(SEND_FORGOT_PASSWORD_REQUEST, forgotPasswordFlow);
+ yield takeLatest(SEND_RESET_PASSWORD_REQUEST, resetPasswordFlow);
}