Skip to content

Commit 6b51782

Browse files
authored
Merge pull request #1016 from hpcc-systems/yadhap/expired-password-flow
Yadhap/expired password flow
2 parents 1fe6e79 + a656e7a commit 6b51782

File tree

7 files changed

+255
-85
lines changed

7 files changed

+255
-85
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// Imports from Library
2+
import React, { useState } from 'react';
3+
import { Button } from 'antd';
4+
import { CheckCircleFilled, CloseCircleFilled } from '@ant-design/icons';
5+
6+
// Local Imports
7+
import { requestPasswordReset } from './utils';
8+
9+
function ExpiredPassword({ email }) {
10+
const [resetting, setResetting] = useState(false);
11+
const [resetSuccess, setResetSuccess] = useState(false);
12+
const [successMessage, setSuccessMessage] = useState('');
13+
const [resetError, setResetError] = useState(false);
14+
15+
const passwordExpiredMessage = (
16+
<div>
17+
<span style={{ fontSize: '1.5rem', fontWeight: '700', color: 'var(--danger)' }}>Password Expired !</span>
18+
<div>
19+
Your password has expired because it was not changed within the required reset period or you have not logged in
20+
for more than 90 days. To regain access to the application, please click the button below to request a password
21+
reset.
22+
</div>
23+
</div>
24+
);
25+
26+
// Success message
27+
const SuccessMessage = (
28+
<div>
29+
<div
30+
style={{
31+
display: 'flex',
32+
justifyContent: 'center',
33+
alignItems: 'center',
34+
gap: '1rem',
35+
color: 'var(--success)',
36+
fontSize: '1.5rem',
37+
fontWeight: '700',
38+
}}>
39+
<CheckCircleFilled /> {successMessage}
40+
</div>
41+
<div style={{ fontSize: '1rem' }}>
42+
Your password reset request has been submitted. An administrator will review your request and take the necessary
43+
action.
44+
</div>
45+
<div>
46+
{/* Go back to login Button */}
47+
<Button color="primary" type="primary" href="/login">
48+
Back to Login
49+
</Button>
50+
</div>
51+
</div>
52+
);
53+
54+
// Failure message
55+
const ErrorMessage = (
56+
<div>
57+
<div
58+
style={{
59+
display: 'flex',
60+
justifyContent: 'center',
61+
alignItems: 'center',
62+
gap: '1rem',
63+
color: 'var(--danger)',
64+
fontSize: '1.5rem',
65+
fontWeight: '700',
66+
}}>
67+
<CloseCircleFilled /> Error Submitting Request
68+
</div>
69+
<div style={{ fontSize: '1rem' }}>
70+
There was an error submitting your password reset request. Please try again or contact your system
71+
administrator.
72+
</div>
73+
</div>
74+
);
75+
76+
// Render message based on reset status
77+
const renderMessage = {
78+
resetSuccess: SuccessMessage,
79+
resetError: ErrorMessage,
80+
passwordExpiredMessage,
81+
};
82+
83+
// Handle reset password button click
84+
const handleResetPassword = async () => {
85+
try {
86+
setResetting(true);
87+
const response = await requestPasswordReset({ email });
88+
setSuccessMessage(response.message);
89+
setResetting(false);
90+
setResetSuccess(true);
91+
} catch (err) {
92+
setResetError(true);
93+
setResetting(false);
94+
}
95+
};
96+
97+
//JSX
98+
return (
99+
<div style={{ display: 'flex', flexDirection: 'column', textAlign: 'center', alignItems: 'center', gap: '1rem' }}>
100+
{resetSuccess && renderMessage.resetSuccess}
101+
{resetError && renderMessage.resetError}
102+
{!resetSuccess && !resetError && renderMessage.passwordExpiredMessage}
103+
104+
{resetSuccess || (
105+
<Button color="primary" onClick={handleResetPassword} type="primary">
106+
{resetting ? 'Requesting Password Reset' : 'Request Password Reset'}
107+
</Button>
108+
)}
109+
</div>
110+
);
111+
}
112+
113+
export default ExpiredPassword;

Tombolo/client-reactjs/src/components/login/login.js

+12-2
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ import { getDeviceInfo } from './utils';
55
import { authActions } from '../../redux/actions/Auth';
66
import { Constants } from '../common/Constants';
77
import UnverifiedUser from './UnverifiedUser';
8+
import ExpiredPassword from './ExpiredPassword';
89

910
const Login = () => {
1011
const [unverifiedUserLoginAttempt, setUnverifiedUserLoginAttempt] = useState(false);
12+
const [expiredPassword, setExpiredPassword] = useState(false);
1113
const [loading, setLoading] = useState(false);
1214
const [azureLoginAttempted, setAzureLoginAttempted] = useState(false);
1315
const [email, setEmail] = useState(null);
@@ -29,6 +31,12 @@ const Login = () => {
2931
return;
3032
}
3133

34+
if (test?.type === 'password-expired') {
35+
// window.location.href = '/expired-password';
36+
setExpiredPassword(true);
37+
return;
38+
}
39+
3240
if (test?.type === Constants.LOGIN_SUCCESS) {
3341
//reload page if login is succesful
3442
window.location.href = '/';
@@ -105,9 +113,11 @@ const Login = () => {
105113

106114
return (
107115
<>
108-
{unverifiedUserLoginAttempt ? (
116+
{unverifiedUserLoginAttempt && (
109117
<UnverifiedUser setUnverifiedUserLoginAttempt={setUnverifiedUserLoginAttempt} email={email} />
110-
) : (
118+
)}
119+
{expiredPassword && <ExpiredPassword email={email} />}
120+
{!unverifiedUserLoginAttempt && !expiredPassword && (
111121
<>
112122
<Form onFinish={onFinish} layout="vertical">
113123
{loading && (

Tombolo/client-reactjs/src/components/login/utils.js

+21
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,24 @@ export const resendVerificationCode = async (email) => {
8585

8686
return responseJson;
8787
};
88+
89+
// Make post request to request a password reset
90+
export const requestPasswordReset = async ({ email }) => {
91+
const payload = {
92+
method: 'POST',
93+
headers: authHeader(),
94+
body: JSON.stringify({ email }),
95+
};
96+
97+
const response = await fetch('/api/auth/requestPasswordReset', payload);
98+
99+
// Get the data from the response
100+
const responseJson = await response.json();
101+
102+
// Check if the response is ok
103+
if (!response.ok) {
104+
throw new Error(responseJson.message);
105+
}
106+
107+
return responseJson;
108+
};

Tombolo/client-reactjs/src/redux/actions/Auth.js

+9-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@ async function login({ email, password, deviceInfo }) {
1414
clearStorage();
1515
const user = await loginBasicUserFunc(email, password, deviceInfo);
1616

17-
if (user && user?.message === 'unverified') {
17+
if (user && user?.message === 'password-expired') {
18+
return {
19+
type: 'password-expired',
20+
payload: null,
21+
};
22+
} else if (user && user?.message === 'unverified') {
1823
return {
1924
type: 'unverified',
2025
payload: null,
@@ -107,7 +112,9 @@ const loginBasicUserFunc = async (email, password, deviceInfo) => {
107112
const data = await response.json();
108113

109114
if (response.status === 401) {
110-
if (data.message === 'unverified') {
115+
if (data.message === 'password-expired') {
116+
return data;
117+
} else if (data.message === 'unverified') {
111118
return data;
112119
} else {
113120
message.error(data.message);

0 commit comments

Comments
 (0)