implement password resets
This commit is contained in:
parent
29fcfbcc7c
commit
837a34d8cc
|
|
@ -299,6 +299,6 @@ div[data-cvm-node=vm0b0t] {
|
|||
}
|
||||
}
|
||||
|
||||
#accountDropdownMenuLink, #accountModalError {
|
||||
#accountDropdownMenuLink, #accountModalError, #accountModalSuccess {
|
||||
display: none;
|
||||
}
|
||||
|
|
@ -109,6 +109,10 @@
|
|||
<span id="accountModalErrorText"></span>
|
||||
<button type="button" class="btn-close" aria-label="Close" id="accountModalErrorDismiss"></button>
|
||||
</div>
|
||||
<div class="alert alert-success alert-dismissible" id="accountModalSuccess" role="alert">
|
||||
<span id="accountModalSuccessText"></span>
|
||||
<button type="button" class="btn-close" aria-label="Close" id="accountModalSuccessDismiss"></button>
|
||||
</div>
|
||||
<div id="accountLoginSection">
|
||||
<form id="accountLoginForm">
|
||||
<label for="accountLoginUsername">Username</label><br/>
|
||||
|
|
@ -116,7 +120,7 @@
|
|||
<label for="accountLoginPassword">Password</label><br/>
|
||||
<input id="accountLoginPassword" type="password" class="form-control" placeholder="Password" name="password" required><br>
|
||||
<div id="accountLoginCaptcha"></div>
|
||||
<button type="submit" class="btn btn-primary">Login</button>
|
||||
<button type="submit" class="btn btn-primary">Login</button> <button type="button" class="btn btn-secondary" id="accountForgotPasswordButton">Forgot Password</button>
|
||||
</form>
|
||||
</div>
|
||||
<div id="accountRegisterSection">
|
||||
|
|
@ -141,7 +145,7 @@
|
|||
<p id="accountVerifyEmailText"></p>
|
||||
<form id="accountVerifyEmailForm">
|
||||
<label for="accountVerifyEmailCode">Code</label><br>
|
||||
<input id="accountVerifyEmailCode" type="text" class="form-control" name="code" required><br>
|
||||
<input id="accountVerifyEmailCode" type="text" class="form-control" name="code" placeholder="Code" required><br>
|
||||
<label for="accountVerifyEmailPassword">Your password</label><br>
|
||||
<input id="accountVerifyEmailPassword" type="password" class="form-control" placeholder="Password" name="password" required/><br/>
|
||||
<button type="submit" class="btn btn-primary">Verify</button>
|
||||
|
|
@ -162,6 +166,30 @@
|
|||
<input id="accountSettingsCurrentPassword" type="password" class="form-control" placeholder="Current Password" name="currentpassword" required/><br/>
|
||||
<button type="submit" class="btn btn-primary">Update</button>
|
||||
</form>
|
||||
</div>
|
||||
<div id="accountResetPasswordSection">
|
||||
<form id="accountResetPasswordForm">
|
||||
<label for="accountResetPasswordEmail">E-Mail</label><br>
|
||||
<input id="accountResetPasswordEmail" type="email" class="form-control" placeholder="E-Mail" name="email" required/><br/>
|
||||
<label for="accountResetPasswordUsername">Username</label>
|
||||
<input id="accountResetPasswordUsername" type="text" class="form-control" placeholder="Username" name="username" required/><br/>
|
||||
<div id="accountResetPasswordCaptcha"></div>
|
||||
<button type="submit" class="btn btn-primary">Reset</button>
|
||||
</div>
|
||||
<div id="accountResetPasswordVerifySection">
|
||||
<center>
|
||||
<i class="fa-solid fa-envelope" style="font-size: 12rem"></i>
|
||||
<p id="accountVerifyPasswordResetText"></p>
|
||||
<form id="accountResetPasswordVerifyForm">
|
||||
<label for="accountResetPasswordCode">Code</label><br>
|
||||
<input id="accountResetPasswordCode" type="text" class="form-control" name="code" placeholder="Code" required><br>
|
||||
<label for="accountResetPasswordNewPassword">New Password</label><br>
|
||||
<input id="accountResetPasswordNewPassword" type="password" class="form-control" placeholder="New Password" name="password" required/><br/>
|
||||
<label for="accountResetPasswordConfirmNewPassword">Confirm New Password</label><br>
|
||||
<input id="accountResetPasswordConfirmNewPassword" type="password" class="form-control" placeholder="Confirm New Password" name="confirmpassword" required/><br/>
|
||||
<button type="submit" class="btn btn-primary">Reset</button>
|
||||
</form>
|
||||
</center>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -153,6 +153,43 @@ export default class AuthManager {
|
|||
res(json);
|
||||
});
|
||||
}
|
||||
|
||||
sendPasswordResetEmail(username : string, email : string, captchaToken : string | undefined) {
|
||||
return new Promise<PasswordResetResult>(async res => {
|
||||
if (!this.info) throw new Error("Cannot send password reset email without fetching API information.");
|
||||
if (!captchaToken && this.info.hcaptcha.required) throw new Error("This API requires a valid hCaptcha token.");
|
||||
var data = await fetch(this.apiEndpoint + "/api/v1/sendreset", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username: username,
|
||||
email: email,
|
||||
captchaToken: captchaToken
|
||||
})
|
||||
});
|
||||
res(await data.json() as PasswordResetResult);
|
||||
});
|
||||
}
|
||||
|
||||
resetPassword(username : string, email : string, code : string, newPassword : string) {
|
||||
return new Promise<PasswordResetResult>(async res => {
|
||||
var data = await fetch(this.apiEndpoint + "/api/v1/reset", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username: username,
|
||||
email: email,
|
||||
code: code,
|
||||
newPassword: newPassword
|
||||
})
|
||||
});
|
||||
res(await data.json() as PasswordResetResult);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export interface AuthServerInformation {
|
||||
|
|
@ -211,4 +248,9 @@ export interface UpdateAccountResult {
|
|||
error : string | undefined;
|
||||
verificationRequired : boolean | undefined;
|
||||
sessionExpired : boolean | undefined;
|
||||
}
|
||||
|
||||
export interface PasswordResetResult {
|
||||
success : boolean;
|
||||
error : string | undefined;
|
||||
}
|
||||
|
|
@ -49,8 +49,11 @@ export enum I18nStringKey {
|
|||
kAccountModal_Register = 'kAccountModal_Register',
|
||||
kAccountModal_Verify = 'kAccountModal_Verify',
|
||||
kAccountModal_AccountSettings = 'kAccountModal_AccountSettings',
|
||||
kAccountModal_ResetPassword = 'kAccountModal_ResetPassword',
|
||||
|
||||
kAccountModal_VerifyText = 'kAccountModal_VerifyText',
|
||||
kAccountModal_VerifyPasswordResetText = 'kAccountModal_VerifyPasswordResetText',
|
||||
kAccountModal_PasswordResetSuccess = 'kAccountModal_PasswordResetSuccess',
|
||||
kMissingCaptcha = 'kMissingCaptcha',
|
||||
kPasswordsMustMatch = 'kPasswordsMustMatch',
|
||||
|
||||
|
|
|
|||
|
|
@ -79,6 +79,9 @@ const elements = {
|
|||
accountModalError: document.getElementById("accountModalError") as HTMLDivElement,
|
||||
accountModalErrorText: document.getElementById("accountModalErrorText") as HTMLSpanElement,
|
||||
accountModalErrorDismiss: document.getElementById("accountModalErrorDismiss") as HTMLButtonElement,
|
||||
accountModalSuccess: document.getElementById("accountModalSuccess") as HTMLDivElement,
|
||||
accountModalSuccessText: document.getElementById("accountModalSuccessText") as HTMLSpanElement,
|
||||
accountModalSuccessDismiss: document.getElementById("accountModalSuccessDismiss") as HTMLButtonElement,
|
||||
accountLoginSection: document.getElementById("accountLoginSection") as HTMLDivElement,
|
||||
accountRegisterSection: document.getElementById("accountRegisterSection") as HTMLDivElement,
|
||||
accountVerifyEmailSection: document.getElementById("accountVerifyEmailSection") as HTMLDivElement,
|
||||
|
|
@ -107,6 +110,20 @@ const elements = {
|
|||
accountSettingsNewPassword: document.getElementById("accountSettingsNewPassword") as HTMLInputElement,
|
||||
accountSettingsConfirmNewPassword: document.getElementById("accountSettingsConfirmNewPassword") as HTMLInputElement,
|
||||
accountSettingsCurrentPassword: document.getElementById("accountSettingsCurrentPassword") as HTMLInputElement,
|
||||
|
||||
accountResetPasswordSection: document.getElementById("accountResetPasswordSection") as HTMLDivElement,
|
||||
accountResetPasswordForm: document.getElementById("accountResetPasswordForm") as HTMLFormElement,
|
||||
accountResetPasswordEmail: document.getElementById("accountResetPasswordEmail") as HTMLInputElement,
|
||||
accountResetPasswordUsername: document.getElementById("accountResetPasswordUsername") as HTMLInputElement,
|
||||
accountResetPasswordCaptcha: document.getElementById("accountResetPasswordCaptcha") as HTMLDivElement,
|
||||
|
||||
accountResetPasswordVerifySection: document.getElementById("accountResetPasswordVerifySection") as HTMLDivElement,
|
||||
accountVerifyPasswordResetText: document.getElementById("accountVerifyPasswordResetText") as HTMLParagraphElement,
|
||||
accountResetPasswordVerifyForm: document.getElementById("accountResetPasswordVerifyForm") as HTMLFormElement,
|
||||
accountResetPasswordCode: document.getElementById("accountResetPasswordCode") as HTMLInputElement,
|
||||
accountResetPasswordNewPassword: document.getElementById("accountResetPasswordNewPassword") as HTMLInputElement,
|
||||
accountResetPasswordConfirmNewPassword: document.getElementById("accountResetPasswordConfirmNewPassword") as HTMLInputElement,
|
||||
accountForgotPasswordButton: document.getElementById("accountForgotPasswordButton") as HTMLButtonElement,
|
||||
};
|
||||
|
||||
let auth : AuthManager|null = null;
|
||||
|
|
@ -879,10 +896,12 @@ async function renderAuth() {
|
|||
elements.accountLogoutButton.style.display = "none";
|
||||
elements.accountRegisterCaptcha.innerHTML = "";
|
||||
elements.accountLoginCaptcha.innerHTML = "";
|
||||
elements.accountResetPasswordCaptcha.innerHTML = "";
|
||||
if (auth!.info!.hcaptcha.required) {
|
||||
var hconfig = {sitekey: auth!.info!.hcaptcha.siteKey!};
|
||||
hcaptcha.render(elements.accountRegisterCaptcha, hconfig);
|
||||
hcaptcha.render(elements.accountLoginCaptcha, hconfig);
|
||||
hcaptcha.render(elements.accountResetPasswordCaptcha, hconfig);
|
||||
}
|
||||
var token = localStorage.getItem("collabvm_session_" + new URL(auth!.apiEndpoint).host);
|
||||
if (token) {
|
||||
|
|
@ -905,12 +924,15 @@ function loadAccount() {
|
|||
}
|
||||
const accountModal = new bootstrap.Modal(elements.accountModal);
|
||||
elements.accountModalErrorDismiss.addEventListener('click', () => elements.accountModalError.style.display = "none");
|
||||
elements.accountModalSuccessDismiss.addEventListener('click', () => elements.accountModalSuccess.style.display = "none");
|
||||
elements.accountLoginButton.addEventListener("click", () => {
|
||||
elements.accountModalTitle.innerText = TheI18n.GetString(I18nStringKey.kAccountModal_Login);
|
||||
elements.accountRegisterSection.style.display = "none";
|
||||
elements.accountVerifyEmailSection.style.display = "none";
|
||||
elements.accountLoginSection.style.display = "block";
|
||||
elements.accountSettingsSection.style.display = "none";
|
||||
elements.accountResetPasswordSection.style.display = "none";
|
||||
elements.accountResetPasswordVerifySection.style.display = "none";
|
||||
accountModal.show();
|
||||
});
|
||||
elements.accountRegisterButton.addEventListener("click", () => {
|
||||
|
|
@ -919,6 +941,8 @@ elements.accountRegisterButton.addEventListener("click", () => {
|
|||
elements.accountVerifyEmailSection.style.display = "none";
|
||||
elements.accountLoginSection.style.display = "none";
|
||||
elements.accountSettingsSection.style.display = "none";
|
||||
elements.accountResetPasswordSection.style.display = "none";
|
||||
elements.accountResetPasswordVerifySection.style.display = "none";
|
||||
accountModal.show();
|
||||
});
|
||||
elements.accountSettingsButton.addEventListener("click", () => {
|
||||
|
|
@ -927,6 +951,8 @@ elements.accountSettingsButton.addEventListener("click", () => {
|
|||
elements.accountVerifyEmailSection.style.display = "none";
|
||||
elements.accountLoginSection.style.display = "none";
|
||||
elements.accountSettingsSection.style.display = "block";
|
||||
elements.accountResetPasswordSection.style.display = "none";
|
||||
elements.accountResetPasswordVerifySection.style.display = "none";
|
||||
// Fill fields
|
||||
elements.accountSettingsUsername.value = auth!.account!.username;
|
||||
elements.accountSettingsEmail.value = auth!.account!.email;
|
||||
|
|
@ -939,6 +965,11 @@ elements.accountLogoutButton.addEventListener('click', async () => {
|
|||
if (VM) closeVM();
|
||||
renderAuth();
|
||||
});
|
||||
elements.accountForgotPasswordButton.addEventListener('click', () => {
|
||||
elements.accountModalTitle.innerText = TheI18n.GetString(I18nStringKey.kAccountModal_ResetPassword);
|
||||
elements.accountLoginSection.style.display = "none";
|
||||
elements.accountResetPasswordSection.style.display = "block";
|
||||
});
|
||||
// i dont know if theres a better place to put this
|
||||
let accountBeingVerified;
|
||||
elements.accountLoginForm.addEventListener('submit', async (e) => {
|
||||
|
|
@ -1084,6 +1115,65 @@ elements.accountSettingsForm.addEventListener('submit', async e => {
|
|||
}
|
||||
return false;
|
||||
});
|
||||
let resetPasswordUsername;
|
||||
let resetPasswordEmail;
|
||||
elements.accountResetPasswordForm.addEventListener('submit', async e => {
|
||||
e.preventDefault();
|
||||
var hcaptchaToken = undefined;
|
||||
var hcaptchaID = undefined;
|
||||
if (auth!.info!.hcaptcha.required) {
|
||||
hcaptchaID = elements.accountResetPasswordCaptcha.children[0].getAttribute("data-hcaptcha-widget-id")!
|
||||
var response = hcaptcha.getResponse(hcaptchaID);
|
||||
if (response === "") {
|
||||
elements.accountModalErrorText.innerHTML = TheI18n.GetString(I18nStringKey.kMissingCaptcha);
|
||||
elements.accountModalError.style.display = "block";
|
||||
return false;
|
||||
}
|
||||
hcaptchaToken = response;
|
||||
}
|
||||
var username = elements.accountResetPasswordUsername.value;
|
||||
var email = elements.accountResetPasswordEmail.value;
|
||||
var result = await auth!.sendPasswordResetEmail(username, email, hcaptchaToken);
|
||||
if (auth!.info!.hcaptcha.required) hcaptcha.reset(hcaptchaID);
|
||||
if (result.success) {
|
||||
resetPasswordUsername = username;
|
||||
resetPasswordEmail = email;
|
||||
elements.accountResetPasswordUsername.value = "";
|
||||
elements.accountResetPasswordEmail.value = "";
|
||||
elements.accountVerifyPasswordResetText.innerText = TheI18n.GetString(I18nStringKey.kAccountModal_VerifyPasswordResetText, email);
|
||||
elements.accountResetPasswordSection.style.display = "none";
|
||||
elements.accountResetPasswordVerifySection.style.display = "block";
|
||||
} else {
|
||||
elements.accountModalErrorText.innerHTML = result.error!;
|
||||
elements.accountModalError.style.display = "block";
|
||||
}
|
||||
return false;
|
||||
});
|
||||
elements.accountResetPasswordVerifyForm.addEventListener('submit', async e => {
|
||||
e.preventDefault();
|
||||
var code = elements.accountResetPasswordCode.value;
|
||||
var password = elements.accountResetPasswordNewPassword.value;
|
||||
if (password !== elements.accountResetPasswordConfirmNewPassword.value) {
|
||||
elements.accountModalErrorText.innerHTML = TheI18n.GetString(I18nStringKey.kPasswordsMustMatch);
|
||||
elements.accountModalError.style.display = "block";
|
||||
return false;
|
||||
}
|
||||
var result = await auth!.resetPassword(resetPasswordUsername!, resetPasswordEmail!, code, password);
|
||||
if (result.success) {
|
||||
elements.accountResetPasswordCode.value = "";
|
||||
elements.accountResetPasswordNewPassword.value = "";
|
||||
elements.accountResetPasswordConfirmNewPassword.value = "";
|
||||
elements.accountModalSuccessText.innerHTML = TheI18n.GetString(I18nStringKey.kAccountModal_PasswordResetSuccess);
|
||||
elements.accountModalSuccess.style.display = "block";
|
||||
elements.accountResetPasswordVerifySection.style.display = "none";
|
||||
elements.accountLoginSection.style.display = "block";
|
||||
|
||||
} else {
|
||||
elements.accountModalErrorText.innerHTML = result.error!;
|
||||
elements.accountModalError.style.display = "block";
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
// Public API
|
||||
w.collabvm = {
|
||||
|
|
|
|||
|
|
@ -45,10 +45,13 @@
|
|||
"kAccountModal_Register": "Register",
|
||||
"kAccountModal_Verify": "Verify E-Mail",
|
||||
"kAccountModal_AccountSettings": "Account Settings",
|
||||
"kAccountModal_ResetPassword": "Reset Password",
|
||||
|
||||
"kMissingCaptcha": "Please fill out the captcha.",
|
||||
"kPasswordsMustMatch": "Passwords must match.",
|
||||
"kAccountModal_VerifyText": "We sent an E-Mail to {0}. To verify your account, please enter the 8-digit code from the E-Mail below.",
|
||||
"kAccountModal_VerifyPasswordResetText": "We sent an E-Mail to {0}. To reset your password, please enter the 8-digit code from the E-Mail below.",
|
||||
"kAccountModal_PasswordResetSuccess": "Your password has been changed successfully. You can now log in with your new password.",
|
||||
|
||||
"kNotLoggedIn": "Not Logged in"
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user