Skip to content

Facilitate 'Ask Password' Email Resend and Alter 'Reset Password' Button Logic #7992

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

Merged
7 changes: 7 additions & 0 deletions .changeset/grumpy-frogs-travel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@wso2is/admin.users.v1": patch
"@wso2is/admin.core.v1": patch
"@wso2is/i18n": patch
---

Facilitate 'Ask Password' Email Resend and Alter 'Reset Password' Button Logic
1 change: 1 addition & 0 deletions features/admin.core.v1/store/reducers/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ export const commonConfigReducerInitialState: CommonConfigReducerStateInterface<
publicCertificates: "",
remoteLogging: "",
requestPathAuthenticators: "",
resendCode: "",
resourceTypes: "",
roles: "",
rolesV2: "",
Expand Down
46 changes: 46 additions & 0 deletions features/admin.users.v1/api/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { AxiosError, AxiosResponse } from "axios";
import { UserManagementConstants } from "../constants";
import { SCIMBulkEndpointInterface } from "../models/endpoints";
import {
ResendCodeRequest,
UserDetailsInterface,
UserListInterface,
UserSessionsInterface
Expand Down Expand Up @@ -470,3 +471,48 @@ export const terminateAllUserSessions = (userId: string): Promise<AxiosResponse>
error.config);
});
};

/**
* Resends the verification code/link for a user. This supports scenarios such as resending the
* verification link or code needed to complete an admin-forced password reset, resending an account
* confirmation link or code, and similar use cases.
*
* @param data - The request payload containing user information and properties.
* @returns A promise that resolves when the code is sent successfully.
* @throws `IdentityAppsApiException` if the request fails or if the response status is not as expected.
*/
export const resendCode = (data: ResendCodeRequest): Promise<void> => {

const requestConfig: RequestConfigInterface = {
data,
headers: {
"Content-Type": "application/json"
},
method: HttpMethods.POST,
url: store.getState().config.endpoints.resendCode
};

return httpClient(requestConfig)
.then((response: AxiosResponse) => {
if (response.status !== 201) {
throw new IdentityAppsApiException(
UserManagementConstants.RESEND_CODE_REQUEST_ERROR,
null,
response.status,
response.request,
response,
response.config);
}

return Promise.resolve();
})
.catch((error: AxiosError) => {
throw new IdentityAppsApiException(
UserManagementConstants.RESEND_CODE_REQUEST_ERROR,
error.stack,
error.code,
error.request,
error.response,
error.config);
});
};
57 changes: 43 additions & 14 deletions features/admin.users.v1/components/user-change-password.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,11 @@ interface ChangePasswordPropsInterface extends TestableComponentInterface {
* Handles force password reset trigger.
*/
handleForcePasswordResetTrigger?: () => void;
/**
* Flag to identify if this is a password reset operation.
* When false, it indicates that a new password is being set (Usage: in the ask password flow).
*/
isResetPassword?: boolean;
}

/**
Expand All @@ -103,6 +108,7 @@ export const ChangePasswordComponent: FunctionComponent<ChangePasswordPropsInter
handleCloseChangePasswordModal,
connectorProperties,
handleForcePasswordResetTrigger,
isResetPassword = true,
[ "data-testid" ]: testId
} = props;

Expand Down Expand Up @@ -585,11 +591,15 @@ export const ChangePasswordComponent: FunctionComponent<ChangePasswordPropsInter
updateUserInfo(user.id, data).then(() => {
onAlertFired({
description: t(
"user:profile.notifications.changeUserPassword.success.description"
isResetPassword
? "user:profile.notifications.changeUserPassword.success.description"
: "user:profile.notifications.setUserPassword.success.description"
),
level: AlertLevels.SUCCESS,
message: t(
"user:profile.notifications.changeUserPassword.success.message"
isResetPassword
? "user:profile.notifications.changeUserPassword.success.message"
: "user:profile.notifications.setUserPassword.success.message"
)
});
handleCloseChangePasswordModal();
Expand All @@ -599,22 +609,35 @@ export const ChangePasswordComponent: FunctionComponent<ChangePasswordPropsInter
.catch((error: any) => {
if (error.response && error.response.data && error.response.data.detail) {
onAlertFired({
description: t("user:profile.notifications.changeUserPassword.error.description",
{ description: error.response.data.detail }),
description: t(
isResetPassword
? "user:profile.notifications.changeUserPassword.error.description"
: "user:profile.notifications.setUserPassword.error.description",
{ description: error.response.data.detail }
),
level: AlertLevels.ERROR,
message: t("user:profile.notifications.changeUserPassword.error." +
"message")
message: t(
isResetPassword
? "user:profile.notifications.changeUserPassword.error.message"
: "user:profile.notifications.setUserPassword.error.message"
)
});

return;
}

onAlertFired({
description: t("user:profile.notifications.changeUserPassword" +
".genericError.description"),
description: t(
isResetPassword
? "user:profile.notifications.changeUserPassword.genericError.description"
: "user:profile.notifications.setUserPassword.genericError.description"
),
level: AlertLevels.ERROR,
message: t("user:profile.notifications.changeUserPassword.genericError." +
"message")
message: t(
isResetPassword
? "user:profile.notifications.changeUserPassword.genericError.message"
: "user:profile.notifications.setUserPassword.genericError.message"
)
});
handleCloseChangePasswordModal();
handleModalClose();
Expand All @@ -629,7 +652,7 @@ export const ChangePasswordComponent: FunctionComponent<ChangePasswordPropsInter
* configured in the server.
*/
const resolveModalContent = () => {
if (governanceConnectorProperties?.length > 1) {
if (isResetPassword && governanceConnectorProperties?.length > 1) {
return (
<>
<Grid.Row>
Expand Down Expand Up @@ -671,7 +694,9 @@ export const ChangePasswordComponent: FunctionComponent<ChangePasswordPropsInter
<Grid.Column mobile={ 16 } tablet={ 16 } computer={ 14 }>
<Message
type="warning"
content={ t("user:modals.changePasswordModal.message") }
content={ isResetPassword
? t("user:modals.changePasswordModal.message")
: t("user:modals.setPasswordModal.message") }
/>
</Grid.Column>
</Grid.Row>
Expand All @@ -687,7 +712,9 @@ export const ChangePasswordComponent: FunctionComponent<ChangePasswordPropsInter
size="tiny"
>
<Modal.Header>
{ t("user:modals.changePasswordModal.header") }
{ isResetPassword
? t("user:modals.changePasswordModal.header")
: t("user:modals.setPasswordModal.header") }
</Modal.Header>
<Modal.Content>
<Forms
Expand Down Expand Up @@ -717,7 +744,9 @@ export const ChangePasswordComponent: FunctionComponent<ChangePasswordPropsInter
disabled={ isSubmitting }
onClick={ () => setTriggerSubmit() }
>
{ t("user:modals.changePasswordModal.button") }
{ isResetPassword
? t("user:modals.changePasswordModal.button")
: t("user:modals.setPasswordModal.button") }
</PrimaryButton>
<LinkButton
data-testid={ `${ testId }-cancel-button` }
Expand Down
3 changes: 3 additions & 0 deletions features/admin.users.v1/components/user-profile.scss
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,6 @@
flex-direction: row;
}
}
.link.pointing {
margin-left: 0.2em;
}
Loading
Loading