309 lines
10 KiB
TypeScript
309 lines
10 KiB
TypeScript
import { localize as $l } from "@padloc/core/lib/locale.js";
|
|
import { ErrorCode } from "@padloc/core/lib/error.js";
|
|
import { passwordStrength } from "../util.js";
|
|
import { app } from "../init.js";
|
|
import { element, html, property, query } from "./base.js";
|
|
import { StartForm, sharedStyles } from "./start-form.js";
|
|
import { Input } from "./input.js";
|
|
import { LoadingButton } from "./loading-button.js";
|
|
import { alert, choose, prompt } from "../dialog.js";
|
|
import "./logo.js";
|
|
|
|
@element("pl-signup")
|
|
export class Signup extends StartForm {
|
|
verificationCode: string;
|
|
|
|
@property()
|
|
private _weakPassword = false;
|
|
|
|
@query("#emailInput")
|
|
private _emailInput: Input;
|
|
@query("#nameInput")
|
|
private _nameInput: Input;
|
|
@query("#passwordInput")
|
|
private _passwordInput: Input;
|
|
@query("#repeatPasswordInput")
|
|
private _repeatPasswordInput: Input;
|
|
@query("#submitButton")
|
|
private _submitButton: LoadingButton;
|
|
|
|
async reset() {
|
|
this._emailInput.value = (this.invite && this.invite.email) || "";
|
|
this._emailInput.checkValidity();
|
|
this._passwordInput.value = "";
|
|
this._repeatPasswordInput.value = "";
|
|
this._submitButton.stop();
|
|
this._weakPassword = false;
|
|
super.reset();
|
|
}
|
|
|
|
render() {
|
|
const { _weakPassword } = this;
|
|
return html`
|
|
${sharedStyles}
|
|
|
|
<style include="shared">
|
|
|
|
.title {
|
|
width: 300px;
|
|
margin: 30px auto;
|
|
font-size: var(--font-size-small);
|
|
font-weight: bold;
|
|
letter-spacing: 0.5px;
|
|
padding: 0 10px;
|
|
}
|
|
|
|
.hint {
|
|
font-size: var(--font-size-tiny);
|
|
box-sizing: border-box;
|
|
max-height: 100px;
|
|
padding: 0 10px;
|
|
margin-bottom: 30px;
|
|
transition: color 0.2s;
|
|
text-shadow: none;
|
|
}
|
|
|
|
.hint.warning {
|
|
color: var(--color-error);
|
|
font-weight: bold;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
#submitButton {
|
|
margin-bottom: 30px;
|
|
}
|
|
|
|
.login {
|
|
text-decoration: underline;
|
|
cursor: pointer;
|
|
}
|
|
|
|
pl-input:not([focused]) + .hint {
|
|
color: rgba(0, 0, 0, 0.2)
|
|
}
|
|
</style>
|
|
|
|
<div flex></div>
|
|
|
|
<form>
|
|
|
|
<pl-logo class="animate"></pl-logo>
|
|
|
|
<div class="title animate">
|
|
${$l("Welcome to Padloc! Let's get you started by creating an account for you. Already have one?")}
|
|
<span class="login" @click=${() => this._login()}>➔ Sign In</span>
|
|
</div>
|
|
|
|
<pl-input
|
|
id="emailInput"
|
|
type="email"
|
|
required
|
|
.label=${$l("Email Address")}
|
|
class="tiles-2 animate"
|
|
@enter=${() => this._submit()}>
|
|
</pl-input>
|
|
|
|
<div class="hint animate">
|
|
${$l(
|
|
"Your email address serves as your username and allows us to get in touch with you. " +
|
|
"Don't worry, we would never send you any spam!"
|
|
)}
|
|
</div>
|
|
|
|
<pl-input
|
|
id="nameInput"
|
|
.label=${$l("Your Name")}
|
|
class="tiles-2 animate"
|
|
@enter=${() => this._submit()}>
|
|
</pl-input>
|
|
|
|
<div class="hint animate">
|
|
${$l("What should we call you?")}
|
|
</div>
|
|
|
|
<pl-input
|
|
id="passwordInput"
|
|
type="password"
|
|
required
|
|
.label=${$l("Master Password")}
|
|
class="tiles-2 animate"
|
|
@change=${() => this._updatePwdStrength()}
|
|
@enter=${() => this._submit()}>
|
|
</pl-input>
|
|
|
|
<div class="hint animate">
|
|
${$l(
|
|
"Your master password is a single passphrase used to protect your data. " +
|
|
"Without it, nobody will be able to access your data - not even us!"
|
|
)}
|
|
</div>
|
|
|
|
<div class="hint warning animate" ?hidden=${!_weakPassword}>${$l("WARNING: Weak Password!")}</div>
|
|
|
|
<pl-input
|
|
id="repeatPasswordInput"
|
|
type="password"
|
|
required
|
|
.label=${$l("Repeat Master Password")}
|
|
class="tiles-2 animate"
|
|
@enter=${() => this._submit()}>
|
|
</pl-input>
|
|
|
|
<div class="hint animate">
|
|
${$l(
|
|
"Don't forget your master password! For privacy and security reasons we don't keep " +
|
|
"a record of you password which means we won't be able to help you recover your " +
|
|
"data in case you forget it."
|
|
)}
|
|
</div>
|
|
|
|
<pl-loading-button id="submitButton" class="tap tiles-3 animate" @click=${() => this._submit()}>
|
|
${$l("Create Account")}
|
|
</pl-loading-button>
|
|
|
|
</form>
|
|
|
|
<div flex></div>
|
|
`;
|
|
}
|
|
|
|
private async _submit() {
|
|
if (this._submitButton.state === "loading") {
|
|
return;
|
|
}
|
|
|
|
this._emailInput.blur();
|
|
this._passwordInput.blur();
|
|
|
|
if (this._emailInput.invalid) {
|
|
await alert(this._emailInput.validationMessage || $l("Please enter a valid email address!"), {
|
|
type: "warning"
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (!this._passwordInput.value) {
|
|
await alert($l("Please enter a master password!"), { type: "warning" });
|
|
return;
|
|
}
|
|
|
|
if (this._passwordInput.value !== this._repeatPasswordInput.value) {
|
|
await alert($l("You didn't repeat your master password correctly. Try again!"), { type: "warning" });
|
|
return;
|
|
}
|
|
|
|
const email = this._emailInput.value;
|
|
const password = this._passwordInput.value;
|
|
const name = this._nameInput.value;
|
|
|
|
const strength = await passwordStrength(password);
|
|
if (strength.score < 2) {
|
|
const choice = await choose(
|
|
$l(
|
|
"The password you entered is weak which makes it easier for attackers to break " +
|
|
"the encryption used to protect your data. Try to use a longer password or include a " +
|
|
"variation of uppercase, lowercase and special characters as well as numbers!"
|
|
),
|
|
[$l("Learn More"), $l("Choose Different Password"), $l("Use Anyway")],
|
|
{
|
|
type: "warning",
|
|
title: $l("WARNING: Weak Password"),
|
|
hideIcon: true,
|
|
preventDismiss: true
|
|
}
|
|
);
|
|
switch (choice) {
|
|
case 0:
|
|
this._openPwdHowTo();
|
|
return;
|
|
case 1:
|
|
this._passwordInput.focus();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!this.verificationCode) {
|
|
await app.verifyEmail(email);
|
|
}
|
|
|
|
return this._signup(email, password, name);
|
|
}
|
|
|
|
private async _signup(email: string, password: string, name: string): Promise<void> {
|
|
this._submitButton.start();
|
|
|
|
if (!this.verificationCode) {
|
|
this.verificationCode = await prompt(
|
|
$l(
|
|
"One last step! To verify your email address, please enter " +
|
|
"the verification code from the email we just sent you!"
|
|
),
|
|
{ placeholder: "Enter Verification Code", confirmLabel: "Submit" }
|
|
);
|
|
|
|
if (this.verificationCode === null) {
|
|
this._submitButton.stop();
|
|
return;
|
|
}
|
|
}
|
|
|
|
try {
|
|
await app.signup({ email, password, name, verify: this.verificationCode, invite: this.invite });
|
|
this._submitButton.success();
|
|
this.done();
|
|
} catch (e) {
|
|
this.verificationCode = "";
|
|
this._submitButton.fail();
|
|
switch (e.code) {
|
|
case ErrorCode.EMAIL_VERIFICATION_FAILED:
|
|
switch (
|
|
await choose(
|
|
$l(
|
|
"We failed to verify your email address. Please make sure to " +
|
|
"enter the verification code we sent you via email!"
|
|
),
|
|
[$l("Try Again"), $l("Resend Email"), $l("Cancel")],
|
|
{ type: "warning", title: $l("Invalid Validation Code!") }
|
|
)
|
|
) {
|
|
case 0:
|
|
return this._signup(email, password, name);
|
|
case 1:
|
|
return this._submit();
|
|
default:
|
|
return;
|
|
}
|
|
case ErrorCode.ACCOUNT_EXISTS:
|
|
const choice = await choose($l("An account with this email address already exists!"), [
|
|
$l("Login"),
|
|
$l("Change Email")
|
|
]);
|
|
if (choice === 0) {
|
|
this.dispatch("cancel");
|
|
} else {
|
|
this._emailInput.focus();
|
|
}
|
|
return;
|
|
default:
|
|
throw e;
|
|
}
|
|
}
|
|
}
|
|
|
|
private async _updatePwdStrength() {
|
|
const pwd = this._passwordInput.value;
|
|
const result = await passwordStrength(pwd);
|
|
const score = result.score;
|
|
this._weakPassword = score < 3;
|
|
}
|
|
|
|
private _openPwdHowTo() {
|
|
window.open("https://padlock.io/howto/choose-master-password/", "_system");
|
|
}
|
|
|
|
private _login() {
|
|
this.dispatch("login");
|
|
}
|
|
}
|