dev(oidc): re-auth remaining protected endpoints

The OIDC re-authentication flow, which replaces password confirmation
for accounts that were created with OIDC and do not have a password, was
previously added to "change username" for manual testing of the
backend-side implementation. Add the re-authentication flow to the
remaining user-protected endpoints, which are:
- change password
- change email
- disable two-factor authentication

When using "change password" on a new account created via OIDC, the
account changes state to a passworded account which causes these flows
to use password confirmation as before instead of re-authentication.
This commit is contained in:
KernelDeimos
2026-02-19 16:13:41 -05:00
parent 142d745f0a
commit 0b8eafa128
4 changed files with 417 additions and 115 deletions
+71 -23
View File
@@ -150,32 +150,79 @@ const TabSecurity = {
$el_window.find('.dashboard-section-security .disable-2fa').on('click', async function (e) {
let win;
const password_confirm_promise = new TeePromise();
const try_password = async () => {
const value = $win.find('.password-entry').val();
// Do not send Authorization: user-protected endpoints use session cookie (hasHttpPowers)
const resp = await fetch(`${window.api_origin}/user-protected/disable-2fa`, {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
password: value,
}),
});
if ( resp.status !== 200 ) {
/* eslint no-empty: ["error", { "allowEmptyCatch": true }] */
let message; try {
message = (await resp.json()).message;
} catch (e) {
function openRevalidatePopup (revalidateUrl, onDone) {
const url = revalidateUrl || (window.user && window.user.oidc_revalidate_url);
if ( ! url ) {
onDone && onDone(new Error('No revalidate URL'));
return null;
}
let doneCalled = false;
const hint = $win.find('.disable-2fa-oidc-hint');
hint.text(i18n('revalidate_sign_in_popup') || 'Sign in with your linked account in the popup.').show();
const popup = window.open(url, 'puter-revalidate', 'width=500,height=600');
const onMessage = (ev) => {
if ( (ev.origin !== window.gui_origin) && (ev.origin !== window.location.origin) ) return;
if ( !ev.data || ev.data.type !== 'puter-revalidate-done' ) return;
if ( doneCalled ) return;
doneCalled = true;
window.removeEventListener('message', onMessage);
hint.hide();
onDone && onDone();
};
window.addEventListener('message', onMessage);
const checkClosed = setInterval(() => {
if ( popup && popup.closed ) {
clearInterval(checkClosed);
window.removeEventListener('message', onMessage);
hint.hide();
if ( ! doneCalled ) {
doneCalled = true;
onDone && onDone(new Error('Popup closed'));
}
}
message = message || i18n('error_unknown_cause');
$win.find('.password-entry').addClass('error');
$win.find('.error-message').text(message).show();
}, 300);
return popup;
}
const doRequest = () => fetch(`${window.api_origin}/user-protected/disable-2fa`, {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ password: $win.find('.password-entry').val() }),
});
const try_password = async () => {
const resp = await doRequest();
if ( resp.status === 200 ) {
password_confirm_promise.resolve(true);
$(win).close();
return;
}
password_confirm_promise.resolve(true);
$(win).close();
const data = await resp.json().catch(() => ({}));
if ( data.code === 'oidc_revalidation_required' && data.revalidate_url ) {
openRevalidatePopup(data.revalidate_url, async (err) => {
if ( err ) {
$win.find('.error-message').text(err.message || 'Re-validation required.').show();
return;
}
const r2 = await doRequest();
if ( r2.status === 200 ) {
password_confirm_promise.resolve(true);
$(win).close();
} else {
let message; try {
message = (await r2.json()).message;
} catch (e) {
}
$win.find('.error-message').text(message || i18n('error_unknown_cause')).show();
}
});
return;
}
const message = data.message || i18n('error_unknown_cause');
$win.find('.password-entry').addClass('error');
$win.find('.error-message').text(message).show();
};
let h = '';
@@ -186,6 +233,7 @@ const TabSecurity = {
h += '</div>';
h += '<div style="display: flex; flex-direction: column; gap: 10pt;">';
h += '<input type="password" class="password-entry" />';
h += '<p class="disable-2fa-oidc-hint" style="margin:0;font-size:12px;color:#666;display:none;"></p>';
h += '<span class="error-message" style="color: var(--dashboard-error-text); display: none;"></span>';
h += '</div>';
h += '<div style="display: flex; gap: 5pt;">';
+76 -24
View File
@@ -82,33 +82,83 @@ export default {
});
$el_window.find('.disable-2fa').on('click', async function (e) {
let win, password_entry;
let win;
const password_confirm_promise = new TeePromise();
const try_password = async () => {
const value = password_entry.get('value');
// No Authorization header: user-protected endpoints use session cookie (hasHttpPowers)
const resp = await fetch(`${window.api_origin}/user-protected/disable-2fa`, {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
password: value,
}),
});
if ( resp.status !== 200 ) {
/* eslint no-empty: ["error", { "allowEmptyCatch": true }] */
let message; try {
message = (await resp.json()).message;
} catch (e) {
function openRevalidatePopup ($win, revalidateUrl, onDone) {
const url = revalidateUrl || (window.user && window.user.oidc_revalidate_url);
if ( ! url ) {
onDone && onDone(new Error('No revalidate URL'));
return null;
}
let doneCalled = false;
const hint = $win.find('.disable-2fa-oidc-hint');
hint.text(i18n('revalidate_sign_in_popup') || 'Sign in with your linked account in the popup.').show();
const popup = window.open(url, 'puter-revalidate', 'width=500,height=600');
const onMessage = (ev) => {
if ( (ev.origin !== window.gui_origin) && (ev.origin !== window.location.origin) ) return;
if ( !ev.data || ev.data.type !== 'puter-revalidate-done' ) return;
if ( doneCalled ) return;
doneCalled = true;
window.removeEventListener('message', onMessage);
hint.hide();
onDone && onDone();
};
window.addEventListener('message', onMessage);
const checkClosed = setInterval(() => {
if ( popup && popup.closed ) {
clearInterval(checkClosed);
window.removeEventListener('message', onMessage);
hint.hide();
if ( ! doneCalled ) {
doneCalled = true;
onDone && onDone(new Error('Popup closed'));
}
}
message = message || i18n('error_unknown_cause');
password_entry.set('error', message);
}, 300);
return popup;
}
const doRequest = () => fetch(`${window.api_origin}/user-protected/disable-2fa`, {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
password: win ? $(win).find('.password-entry').val() : '',
}),
});
const try_password = async () => {
const resp = await doRequest();
if ( resp.status === 200 ) {
password_confirm_promise.resolve(true);
$(win).close();
return;
}
password_confirm_promise.resolve(true);
$(win).close();
const data = await resp.json().catch(() => ({}));
const $win = $(win);
if ( data.code === 'oidc_revalidation_required' && data.revalidate_url ) {
openRevalidatePopup($win, data.revalidate_url, async (err) => {
if ( err ) {
$win.find('.error-message').text(err.message || 'Re-validation required.').show();
return;
}
const r2 = await doRequest();
if ( r2.status === 200 ) {
password_confirm_promise.resolve(true);
$(win).close();
} else {
let message; try {
message = (await r2.json()).message;
} catch (e) {
}
$win.find('.error-message').text(message || i18n('error_unknown_cause')).show();
}
});
return;
}
$win.find('.password-entry').addClass('error');
$win.find('.error-message').text(data.message || i18n('error_unknown_cause')).show();
};
let h = '';
@@ -117,8 +167,10 @@ export default {
h += `<h3 style="text-align:center; font-weight: 500; font-size: 20px;">${i18n('disable_2fa_confirm')}</h3>`;
h += `<p style="text-align:center; padding: 0 20px;">${i18n('disable_2fa_instructions')}</p>`;
h += '</div>';
h += '<div style="display: flex; gap: 5pt;">';
h += '<div style="display: flex; flex-direction: column; gap: 10pt;">';
h += '<input type="password" class="password-entry" />';
h += '<p class="disable-2fa-oidc-hint" style="margin:0;font-size:12px;color:#666;display:none;"></p>';
h += '<span class="error-message" style="color: red; display: none;"></span>';
h += `<button class="button confirm-disable-2fa">${i18n('disable_2fa')}</button>`;
h += `<button class="button secondary cancel-disable-2fa">${i18n('cancel')}</button>`;
h += '</div>';
+131 -38
View File
@@ -41,11 +41,18 @@ async function UIWindowChangeEmail (options) {
h += `<label for="confirm-new-email-${internal_id}">${i18n('new_email')}</label>`;
h += `<input id="confirm-new-email-${internal_id}" type="text" name="new-email" class="new-email" autocomplete="off" />`;
h += '</div>';
// password confirmation
h += '<div style="overflow: hidden; margin-top: 10px; margin-bottom: 30px;">';
// password / OIDC revalidate
h += '<div class="change-email-auth-row" style="overflow: hidden; margin-top: 10px; margin-bottom: 30px;">';
h += '<div class="change-email-password-wrap">';
h += `<label>${i18n('account_password')}</label>`;
h += `${place_password_entry.html}`;
h += '</div>';
h += '<div class="change-email-oidc-wrap" style="display:none;">';
h += '<button type="button" class="button change-email-revalidate-btn"></button>';
h += '<span class="change-email-revalidated-msg" style="display:none;"></span>';
h += '</div>';
h += '<p class="change-email-oidc-hint" style="margin-top:6px;font-size:12px;color:#666;display:none;"></p>';
h += '</div>';
// Change Email
h += `<button class="change-email-btn button button-primary button-block button-normal">${i18n('change_email')}</button>`;
@@ -74,6 +81,15 @@ async function UIWindowChangeEmail (options) {
show_in_taskbar: false,
onAppend: function (this_window) {
$(this_window).find('.new-email').get(0)?.focus({ preventScroll: true });
const oidc_only = !!(window.user && window.user.oidc_only);
const authRow = $(this_window).find('.change-email-auth-row');
if ( oidc_only ) {
authRow.find('.change-email-password-wrap').hide();
const oidcWrap = authRow.find('.change-email-oidc-wrap').show();
oidcWrap.find('.change-email-revalidate-btn').text(i18n('revalidate_with_google') || 'Re-validate with Google');
} else {
authRow.find('.change-email-oidc-wrap').hide();
}
},
window_class: 'window-publishWebsite',
body_css: {
@@ -87,12 +103,16 @@ async function UIWindowChangeEmail (options) {
password_entry.attach(place_password_entry);
$(el_window).find('.change-email-btn').on('click', function (e) {
// hide previous error/success msg
$(el_window).find('.form-success-msg, .form-success-msg').hide();
const origin = window.gui_origin || window.api_origin || '';
const apiUrl = `${origin}/user-protected/change-email`;
let revalidated = false;
$(el_window).find('.change-email-btn').on('click', async function (e) {
$(el_window).find('.form-success-msg, .form-error-msg').hide();
const new_email = $(el_window).find('.new-email').val();
const password = $(el_window).find('.password').val();
const password = password_entry.get('value');
const oidc_only = !!(window.user && window.user.oidc_only);
if ( ! new_email ) {
$(el_window).find('.form-error-msg').html(i18n('all_fields_required'));
@@ -101,45 +121,118 @@ async function UIWindowChangeEmail (options) {
}
$(el_window).find('.form-error-msg').hide();
// disable button
$(el_window).find('.change-email-btn').addClass('disabled');
// disable input
$(el_window).find('.new-email').attr('disabled', true);
$.ajax({
url: `${window.gui_origin}/user-protected/change-email`,
type: 'POST',
async: true,
// headers: {
// 'Authorization': `Bearer ${window.auth_token}`,
// },
contentType: 'application/json',
data: JSON.stringify({
new_email: new_email,
password: password_entry.get('value'),
const doSubmit = () => fetch(apiUrl, {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
new_email,
password: password !== undefined && password !== '' ? password : undefined,
}),
success: function (data) {
$(el_window).find('.form-success-msg').html(i18n('email_change_confirmation_sent'));
$(el_window).find('.form-success-msg').fadeIn();
$(el_window).find('input').val('');
// update email
window.user.email = new_email;
// enable button
$(el_window).find('.change-email-btn').removeClass('disabled');
// enable input
$(el_window).find('.new-email').attr('disabled', false);
},
error: function (err) {
$(el_window).find('.form-error-msg').html(html_encode(err.responseJSON?.message));
});
if ( oidc_only && !revalidated && !password ) {
openRevalidatePopup(null, async (err) => {
if ( err ) {
onError(err.message || 'Re-validation required.');
return;
}
const res = await doSubmit();
const data = res.ok ? await res.json().catch(() => ({})) : await res.json().catch(() => ({}));
if ( res.ok ) onSuccess();
else onError(data.message || 'Request failed');
});
return;
}
let res = await doSubmit();
const data = res.ok ? await res.json().catch(() => ({})) : await res.json().catch(() => ({}));
if ( res.ok ) {
onSuccess();
return;
}
if ( data.code === 'oidc_revalidation_required' && data.revalidate_url ) {
openRevalidatePopup(data.revalidate_url, async (err) => {
if ( err ) {
onError(err.message || 'Re-validation required.');
return;
}
const r2 = await doSubmit();
const d2 = r2.ok ? await r2.json().catch(() => ({})) : await r2.json().catch(() => ({}));
if ( r2.ok ) onSuccess();
else onError(d2.message || 'Request failed');
});
return;
}
onError(data.message || 'Request failed');
});
function openRevalidatePopup (revalidateUrl, onDone) {
const url = revalidateUrl || (window.user && window.user.oidc_revalidate_url);
if ( ! url ) {
onDone && onDone(new Error('No revalidate URL'));
return null;
}
let doneCalled = false;
const hint = $(el_window).find('.change-email-oidc-hint');
hint.text(i18n('revalidate_sign_in_popup') || 'Sign in with your linked account in the popup.').show();
const popup = window.open(url, 'puter-revalidate', 'width=500,height=600');
const onMessage = (ev) => {
if ( (ev.origin !== window.gui_origin) && (ev.origin !== window.location.origin) ) return;
if ( !ev.data || ev.data.type !== 'puter-revalidate-done' ) return;
if ( doneCalled ) return;
doneCalled = true;
window.removeEventListener('message', onMessage);
revalidated = true;
hint.hide();
$(el_window).find('.change-email-revalidated-msg').text(i18n('revalidated') || 'Re-validated.').show();
$(el_window).find('.change-email-revalidate-btn').hide();
onDone && onDone();
};
window.addEventListener('message', onMessage);
const checkClosed = setInterval(() => {
if ( popup && popup.closed ) {
clearInterval(checkClosed);
window.removeEventListener('message', onMessage);
hint.hide();
if ( ! doneCalled ) {
doneCalled = true;
onDone && onDone(new Error('Popup closed'));
}
}
}, 300);
return popup;
}
$(el_window).find('.change-email-revalidate-btn').on('click', function () {
openRevalidatePopup(null, (err) => {
if ( err ) {
$(el_window).find('.form-error-msg').html(html_encode(err.message || 'Re-validation required.'));
$(el_window).find('.form-error-msg').fadeIn();
// enable button
$(el_window).find('.change-email-btn').removeClass('disabled');
// enable input
$(el_window).find('.new-email').attr('disabled', false);
},
}
});
});
function onError (message) {
$(el_window).find('.form-error-msg').html(html_encode(message));
$(el_window).find('.form-error-msg').fadeIn();
$(el_window).find('.change-email-btn').removeClass('disabled');
$(el_window).find('.new-email').attr('disabled', false);
}
function onSuccess () {
const new_email = $(el_window).find('.new-email').val();
$(el_window).find('.form-success-msg').html(i18n('email_change_confirmation_sent'));
$(el_window).find('.form-success-msg').fadeIn();
$(el_window).find('input').val('');
window.user.email = new_email;
$(el_window).find('.change-email-btn').removeClass('disabled');
$(el_window).find('.new-email').attr('disabled', false);
}
}
export default UIWindowChangeEmail;
+139 -30
View File
@@ -17,8 +17,8 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import UIWindow from './UIWindow.js';
import check_password_strength from '../helpers/check_password_strength.js';
import UIWindow from './UIWindow.js';
async function UIWindowChangePassword (options) {
options = options ?? {};
@@ -30,11 +30,17 @@ async function UIWindowChangePassword (options) {
h += '<div class="form-error-msg"></div>';
// success msg
h += '<div class="form-success-msg"></div>';
// current password
h += '<div style="overflow: hidden; margin-bottom: 20px;">';
// current password / OIDC revalidate
h += '<div class="change-password-auth-row" style="overflow: hidden; margin-bottom: 20px;">';
h += '<div class="change-password-current-wrap">';
h += `<label for="current-password-${internal_id}">${i18n('current_password')}</label>`;
h += `<input id="current-password-${internal_id}" class="current-password" type="password" name="current-password" autocomplete="current-password" />`;
h += '</div>';
h += '<div class="change-password-oidc-wrap" style="display:none;">';
h += '<button type="button" class="button change-password-revalidate-btn"></button>';
h += '<span class="change-password-revalidated-msg" style="display:none;"></span>';
h += '</div>';
h += '</div>';
// new password
h += '<div style="overflow: hidden; margin-top: 20px; margin-bottom: 20px;">';
h += `<label for="new-password-${internal_id}">${i18n('new_password')}</label>`;
@@ -45,6 +51,7 @@ async function UIWindowChangePassword (options) {
h += `<label for="confirm-new-password-${internal_id}">${i18n('confirm_new_password')}</label>`;
h += `<input id="confirm-new-password-${internal_id}" type="password" name="confirm-new-password" class="confirm-new-password" autocomplete="off" />`;
h += '</div>';
h += '<p class="change-password-oidc-hint" style="margin-top:6px;font-size:12px;color:#666;display:none;"></p>';
// Change Password
h += `<button class="change-password-btn button button-primary button-block button-normal">${i18n('change_password')}</button>`;
@@ -72,7 +79,16 @@ async function UIWindowChangePassword (options) {
dominant: true,
show_in_taskbar: false,
onAppend: function (this_window) {
$(this_window).find('.current-password').get(0).focus({ preventScroll: true });
$(this_window).find('.current-password').get(0)?.focus({ preventScroll: true });
const oidc_only = !!(window.user && window.user.oidc_only);
const authRow = $(this_window).find('.change-password-auth-row');
if ( oidc_only ) {
authRow.find('.change-password-current-wrap').hide();
const oidcWrap = authRow.find('.change-password-oidc-wrap').show();
oidcWrap.find('.change-password-revalidate-btn').text(i18n('revalidate_with_google') || 'Re-validate with Google');
} else {
authRow.find('.change-password-oidc-wrap').hide();
}
},
window_class: 'window-publishWebsite',
body_css: {
@@ -84,27 +100,47 @@ async function UIWindowChangePassword (options) {
...options.window_options,
});
$(el_window).find('.change-password-btn').on('click', function (e) {
const origin = window.gui_origin || window.api_origin || '';
const apiUrl = `${origin}/user-protected/change-password`;
$(el_window).find('.change-password-btn').on('click', async function (e) {
const current_password = $(el_window).find('.current-password').val();
const new_password = $(el_window).find('.new-password').val();
const confirm_new_password = $(el_window).find('.confirm-new-password').val();
const oidc_only = !!(window.user && window.user.oidc_only);
// hide success message
$(el_window).find('.form-success-msg').hide();
$(el_window).find('.form-success-msg, .form-error-msg').hide();
// check if all fields are filled
if ( !current_password || !new_password || !confirm_new_password ) {
if ( !new_password || !confirm_new_password ) {
$(el_window).find('.form-error-msg').html('All fields are required.');
$(el_window).find('.form-error-msg').fadeIn();
return;
}
// check if new password and confirm new password are the same
else if ( new_password !== confirm_new_password ) {
// For password users, current password is required; for OIDC, we need revalidated or will open popup
if ( !oidc_only && !current_password ) {
$(el_window).find('.form-error-msg').html('All fields are required.');
$(el_window).find('.form-error-msg').fadeIn();
return;
}
if ( oidc_only && !revalidated && !current_password ) {
$(el_window).find('.change-password-btn').addClass('disabled');
openRevalidatePopup(null, async (err) => {
if ( err ) {
onError(err.message || 'Re-validation required.');
return;
}
const res = await doSubmit('');
const data = res.ok ? await res.json().catch(() => ({})) : await res.json().catch(() => ({}));
if ( res.ok ) onSuccess();
else onError(data.message || 'Request failed');
});
return;
}
if ( new_password !== confirm_new_password ) {
$(el_window).find('.form-error-msg').html(i18n('passwords_do_not_match'));
$(el_window).find('.form-error-msg').fadeIn();
return;
}
// check password strength
const pass_strength = check_password_strength(new_password);
if ( ! pass_strength.overallPass ) {
$(el_window).find('.form-error-msg').html(i18n('password_strength_error'));
@@ -113,29 +149,102 @@ async function UIWindowChangePassword (options) {
}
$(el_window).find('.form-error-msg').hide();
$(el_window).find('.change-password-btn').addClass('disabled');
$(el_window).find('.current-password, .new-password, .confirm-new-password').attr('disabled', true);
// Do not send Authorization: user-protected endpoints use session cookie (hasHttpPowers)
$.ajax({
url: `${window.api_origin }/user-protected/change-password`,
type: 'POST',
async: true,
xhrFields: { withCredentials: true },
contentType: 'application/json',
data: JSON.stringify({
password: current_password,
const doSubmit = (currentPass) => fetch(apiUrl, {
method: 'POST',
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
password: currentPass !== undefined ? currentPass : current_password,
new_pass: new_password,
}),
success: function (data) {
$(el_window).find('.form-success-msg').html(i18n('password_changed'));
$(el_window).find('.form-success-msg').fadeIn();
$(el_window).find('input').val('');
},
error: function (err) {
$(el_window).find('.form-error-msg').html(html_encode(err.responseText));
$(el_window).find('.form-error-msg').fadeIn();
},
});
let res = await doSubmit(current_password);
const data = res.ok ? await res.json().catch(() => ({})) : await res.json().catch(() => ({}));
if ( res.ok ) {
onSuccess();
return;
}
if ( data.code === 'oidc_revalidation_required' && data.revalidate_url ) {
openRevalidatePopup(data.revalidate_url, async (err) => {
if ( err ) {
onError(err.message || 'Re-validation required.');
return;
}
const r2 = await doSubmit('');
const d2 = r2.ok ? await r2.json().catch(() => ({})) : await r2.json().catch(() => ({}));
if ( r2.ok ) onSuccess();
else onError(d2.message || 'Request failed');
});
return;
}
onError(data.message || res.statusText || 'Request failed');
});
let revalidated = false;
function openRevalidatePopup (revalidateUrl, onDone) {
const url = revalidateUrl || (window.user && window.user.oidc_revalidate_url);
if ( ! url ) {
onDone && onDone(new Error('No revalidate URL'));
return null;
}
let doneCalled = false;
const hint = $(el_window).find('.change-password-oidc-hint');
hint.text(i18n('revalidate_sign_in_popup') || 'Sign in with your linked account in the popup.').show();
const popup = window.open(url, 'puter-revalidate', 'width=500,height=600');
const onMessage = (ev) => {
if ( (ev.origin !== window.gui_origin) && (ev.origin !== window.location.origin) ) return;
if ( !ev.data || ev.data.type !== 'puter-revalidate-done' ) return;
if ( doneCalled ) return;
doneCalled = true;
window.removeEventListener('message', onMessage);
revalidated = true;
hint.hide();
$(el_window).find('.change-password-revalidated-msg').text(i18n('revalidated') || 'Re-validated.').show();
$(el_window).find('.change-password-revalidate-btn').hide();
onDone && onDone();
};
window.addEventListener('message', onMessage);
const checkClosed = setInterval(() => {
if ( popup && popup.closed ) {
clearInterval(checkClosed);
window.removeEventListener('message', onMessage);
hint.hide();
if ( ! doneCalled ) {
doneCalled = true;
onDone && onDone(new Error('Popup closed'));
}
}
}, 300);
return popup;
}
$(el_window).find('.change-password-revalidate-btn').on('click', function () {
openRevalidatePopup(null, (err) => {
if ( err ) {
onError(err.message || 'Re-validation required.');
}
});
});
function onError (message) {
$(el_window).find('.form-error-msg').html(html_encode(message));
$(el_window).find('.form-error-msg').fadeIn();
$(el_window).find('.change-password-btn').removeClass('disabled');
$(el_window).find('.current-password, .new-password, .confirm-new-password').attr('disabled', false);
}
function onSuccess () {
$(el_window).find('.form-success-msg').html(i18n('password_changed'));
$(el_window).find('.form-success-msg').fadeIn();
$(el_window).find('input').val('');
$(el_window).find('.change-password-btn').removeClass('disabled');
$(el_window).find('.current-password, .new-password, .confirm-new-password').attr('disabled', false);
}
}
export default UIWindowChangePassword;