forked from services/password-reset-web
init
This commit is contained in:
253
src/SetPassword.vue
Normal file
253
src/SetPassword.vue
Normal file
@@ -0,0 +1,253 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>⚿ Submit new password</h1>
|
||||
|
||||
<div v-if="validationRequestActive" class="alert alert-info">
|
||||
Validating reset token …
|
||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||
</div>
|
||||
<template v-else>
|
||||
|
||||
|
||||
<div v-if="!tokenIsValid" class="alert alert-danger" role="alert">
|
||||
<template v-if="validationRequestError">
|
||||
<h3>Failed to validate password reset link</h3>
|
||||
<p>{{ validationRequestError }}</p>
|
||||
</template>
|
||||
<template v-else>
|
||||
<h3>Invalid link provided</h3>
|
||||
<p>This password reset link isn't valid (anymore). Please
|
||||
<router-link :to="{ name: 'request' }" tag="a">renew your password reset request</router-link></p>
|
||||
</template>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div v-if="setRequestFinished" class="alert alert-success" role="alert">
|
||||
✊ Your password has been set successfully! The change takes effect immediately.
|
||||
</div>
|
||||
<form v-else method="POST" class="col-md-10 mb-lg-5" @submit.prevent="onSubmit">
|
||||
<p>Please provide a new password.</p>
|
||||
|
||||
<fieldset :disabled="setRequestIsWorking">
|
||||
<div class="row">
|
||||
<label for="password1" class="col-sm-3 col-form-label"><strong>New password:</strong></label>
|
||||
<div class="col-sm-9">
|
||||
<input id="password1" type="password" class="form-control" required="true" v-model="password1"
|
||||
aria-label="New password" ref="password1" minlength="8"
|
||||
autocomplete="new-password">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 row">
|
||||
<label for="password2" class="col-sm-3 col-form-label"><strong>Repeat password:</strong></label>
|
||||
<div class="col-sm-9">
|
||||
<input id="password2" type="password" class="form-control" required="true"
|
||||
aria-label="New password again" v-model="password2"
|
||||
autocomplete="new-password">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 row">
|
||||
<label for="password-quality" class="col-sm-3 col-form-label"><strong>Password quality:</strong></label>
|
||||
<div class="col-sm-9">
|
||||
<password-strength :password="password1" class="mt-2"
|
||||
v-on:score="qualityScore = $event"
|
||||
v-on:feedback="qualityFeedback = $event"
|
||||
></password-strength>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="setRequestError" class="alert alert-danger" role="alert">{{ setRequestError }}</div>
|
||||
|
||||
<div class="text-muted float-start">{{ qualityFeedback }}</div>
|
||||
<div class="text-end" :title="setButtonTitle">
|
||||
<button class="btn btn-outline-danger" type="submit" id="button-submit"
|
||||
:disabled="setButtonTitle !== null">
|
||||
<span v-if="!setRequestIsWorking">⮷ Set</span>
|
||||
<span v-else>
|
||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<h3 class="text-muted">How to choose a good password?</h3>
|
||||
<p>As of today (2021) there are three approved strategies:</p>
|
||||
|
||||
<ul>
|
||||
<li>Choose a reasonably complex password using multiple character classes (upper-case, lower-case, special
|
||||
characters). Downside: They are hard to remember for users and potentionally lead to passwords being
|
||||
reused across different services.</li>
|
||||
<li>Choose a long password that consists of a personal sentence which is hard to break for machines and easy
|
||||
to remember for users</li>
|
||||
<li>Just for the sake of completeness: Choose a second factor to get the best possible security. This is not
|
||||
yet supported by the ECG services. But it's on our radar.</li>
|
||||
</ul>
|
||||
<p>Although currently not technically prevented it's not allowed to reuse a previous password</p>
|
||||
<p>Furthermore we recommend using a password manager like KeePass.</p>
|
||||
<h6>In the case of problems</h6>
|
||||
<p>
|
||||
You can find <a href="https://wiki.ecogood.org/x/DYQjB" target="_blank">
|
||||
a detailed explanation of the password reset process in the wiki</a>.
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
import PasswordStrength from './PasswordStrength';
|
||||
import axios from 'axios';
|
||||
import zxcvbn from 'zxcvbn';
|
||||
|
||||
let cssClasses = {
|
||||
0: 'bg-danger',
|
||||
1: 'bg-danger',
|
||||
2: 'bg-warning',
|
||||
3: 'bg-warning',
|
||||
4: 'bg-success'
|
||||
};
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PasswordStrength
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
// step 1: validate the given token
|
||||
token: this.$route.params.token,
|
||||
validationRequestActive: true,
|
||||
validationRequestError: null,
|
||||
tokenIsValid: false,
|
||||
|
||||
// step 2: set the new password
|
||||
password1: "",
|
||||
password2: "",
|
||||
setRequestIsWorking: false,
|
||||
setRequestFinished: false,
|
||||
setRequestError: null,
|
||||
qualityScore: 0,
|
||||
qualityFeedback: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
setButtonTitle: function () {
|
||||
if (this.qualityScore < 4) {
|
||||
return 'The password strength is too low to be accepted';
|
||||
} else if (this.password1 !== this.password2) {
|
||||
return "The two provided passwords don't match";
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
validateToken: function () {
|
||||
this.validationRequestActive = true;
|
||||
|
||||
let tokenEncoded = encodeURIComponent(this.token);
|
||||
axios
|
||||
.get(`password/token/${tokenEncoded}`)
|
||||
.then(
|
||||
() => this.tokenIsValid = true,
|
||||
(error) => {
|
||||
let containsResponse = typeof error.response !== 'undefined';
|
||||
this.validationRequestError = containsResponse && error.response.status === 404
|
||||
? null
|
||||
: error.message;
|
||||
})
|
||||
.catch(() => this.validationRequestError = "validation request error")
|
||||
.finally(() => this.validationRequestActive = false);
|
||||
},
|
||||
onSubmit: function () {
|
||||
this.setRequestIsWorking = true;
|
||||
|
||||
let tokenEncoded = encodeURIComponent(this.token);
|
||||
|
||||
let config = {
|
||||
headers: {
|
||||
'Content-Type': 'text/plain'
|
||||
},
|
||||
responseType: 'text'
|
||||
};
|
||||
|
||||
axios
|
||||
.post(`password/${tokenEncoded}`, this.password1, config)
|
||||
.then(() => {
|
||||
this.setRequestFinished = true;
|
||||
this.setRequestError = null;
|
||||
|
||||
localStorage.removeItem('timestamp');
|
||||
}, (error) => {
|
||||
let containsResponse = typeof error.response !== 'undefined';
|
||||
this.setRequestError = containsResponse && error.response.status === 400
|
||||
? "Insufficient password strength!"
|
||||
: error.message;
|
||||
})
|
||||
.finally(() => {
|
||||
this.setRequestIsWorking = false;
|
||||
});
|
||||
},
|
||||
onUserTest: function () {
|
||||
let uidEncoded = encodeURIComponent(this.uid);
|
||||
|
||||
let config = {
|
||||
headers: {
|
||||
'Content-Type': 'text/plain'
|
||||
},
|
||||
responseType: 'text'
|
||||
};
|
||||
|
||||
this.testResult = null;
|
||||
this.testIsWorking = true;
|
||||
|
||||
axios
|
||||
.post(`password/test/${uidEncoded}`, this.password, config)
|
||||
.then((response) => {
|
||||
this.testRequestFinished = true;
|
||||
this.testRequestError = null;
|
||||
this.testResult = true;
|
||||
}, (error) => {
|
||||
this.testResult = false;
|
||||
this.testRequestError = error.message;
|
||||
})
|
||||
.finally(() => {
|
||||
this.testIsWorking = false;
|
||||
});
|
||||
},
|
||||
onScoreUpdate: function (score) {
|
||||
this.qualityScore = score;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.validateToken();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#button-submit {
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
#button-submit:enabled {
|
||||
animation: pulse 0.3s 1 alternate;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
transform: scale(0.95);
|
||||
box-shadow: 0 0 0 0 rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
|
||||
70% {
|
||||
transform: scale(1);
|
||||
box-shadow: 0 0 0 10px rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(0.95);
|
||||
box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user