Browse Source

Fix Chrome add-on does randomly logout

getPassword action logout on error. This is bad because the error can be
because of access_token expired.

This patch will:

 * display a spinner is refresh token is found on localStorage
 * try to refresh the access token and the refresh token with the
   refresh token
 * authenticate the user if ok and get password profiles
 * finally set loading to false to display password generator view

Fixes #236
b5
Guillaume Vincent 3 years ago
parent
commit
68fd82eeaa
16 changed files with 95 additions and 47 deletions
  1. +1
    -1
      packages/lesspass-pure/package.json
  2. +37
    -1
      packages/lesspass-pure/src/LessPass.vue
  3. +5
    -0
      packages/lesspass-pure/src/api/baseURL.js
  4. +0
    -1
      packages/lesspass-pure/src/api/default.js
  5. +3
    -3
      packages/lesspass-pure/src/api/http.js
  6. +18
    -0
      packages/lesspass-pure/src/images/loading.svg
  7. +5
    -8
      packages/lesspass-pure/src/store/actions.js
  8. +3
    -7
      packages/lesspass-pure/src/store/defaultPassword.js
  9. +2
    -2
      packages/lesspass-pure/src/store/getters.js
  10. +2
    -2
      packages/lesspass-pure/src/store/getters.test.js
  11. +2
    -3
      packages/lesspass-pure/src/store/index.js
  12. +2
    -2
      packages/lesspass-pure/src/store/mutations.js
  13. +4
    -4
      packages/lesspass-pure/src/store/mutations.test.js
  14. +3
    -3
      packages/lesspass-pure/src/views/Login.vue
  15. +5
    -7
      packages/lesspass-pure/src/views/PasswordGenerator.vue
  16. +3
    -3
      packages/lesspass-pure/src/views/Register.vue

+ 1
- 1
packages/lesspass-pure/package.json View File

@@ -1,6 +1,6 @@
{
"name": "lesspass-pure",
"version": "9.3.1",
"version": "9.3.2",
"description": "LessPass web component",
"license": "GPL-3.0",
"author": "Guillaume Vincent <guillaume@oslab.fr>",


+ 37
- 1
packages/lesspass-pure/src/LessPass.vue View File

@@ -60,17 +60,37 @@ button,
.right-addon input {
padding-right: 30px;
}

#loading__view {
position: relative;
height: 358px;
}

.loading__icon {
width: 64px;
margin: auto;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
</style>
<template>
<div id="lesspass" class="card">
<lesspass-menu></lesspass-menu>
<lesspass-message></lesspass-message>
<div class="lesspass__inner-box card-body">
<router-view></router-view>
<div id="loading__view" v-if="isLoading">
<img src="./images/loading.svg" alt="loading" class="loading__icon" />
</div>
<router-view v-else></router-view>
</div>
</div>
</template>
<script>
import axios from "axios";
import { getBaseURL } from "./api/baseURL";
import Menu from "./components/Menu.vue";
import Message from "./components/Message.vue";

@@ -80,9 +100,25 @@ export default {
"lesspass-menu": Menu,
"lesspass-message": Message
},
data: () => ({
isLoading: false
}),
created() {
this.$store.dispatch("cleanMessage");
this.$store.dispatch("resetPassword");
const refresh = localStorage.getItem("refresh_token");
if (refresh) {
this.isLoading = true;
axios
.post("/api/auth/jwt/refresh/", { refresh }, { baseURL: getBaseURL() })
.then(response => {
this.$store.dispatch("login", response.data);
return this.$store.dispatch("getPasswords");
})
.finally(() => {
this.isLoading = false;
});
}
}
};
</script>

+ 5
- 0
packages/lesspass-pure/src/api/baseURL.js View File

@@ -0,0 +1,5 @@
export const defaultBaseURL = "https://lesspass.com";

export function getBaseURL() {
return localStorage.getItem("baseURL") || defaultBaseURL;
}

+ 0
- 1
packages/lesspass-pure/src/api/default.js View File

@@ -1 +0,0 @@
export const defaultbaseURL = "https://lesspass.com";

+ 3
- 3
packages/lesspass-pure/src/api/http.js View File

@@ -1,8 +1,8 @@
import axios from "axios";
import { defaultbaseURL } from "./default";
import { getBaseURL } from "./baseURL";

axios.interceptors.request.use(config => {
const baseURL = localStorage.getItem("baseURL") || defaultbaseURL;
const baseURL = getBaseURL();
config.baseURL = baseURL;
const access_token = localStorage.getItem("access_token");
if (access_token) {
@@ -26,7 +26,7 @@ axios.interceptors.response.use(
reject(error);
});
}
const baseURL = localStorage.getItem("baseURL");
const baseURL = getBaseURL()
return axios
.post("/api/auth/jwt/refresh/", { refresh }, { baseURL })
.then(response => {


+ 18
- 0
packages/lesspass-pure/src/images/loading.svg View File

@@ -0,0 +1,18 @@
<svg width="57" height="57" viewBox="0 0 57 57" xmlns="http://www.w3.org/2000/svg" stroke="#0275d8">
<g fill="none" fill-rule="evenodd">
<g transform="translate(1 1)" stroke-width="2">
<circle cx="5" cy="50" r="5">
<animate attributeName="cy" begin="0s" dur="2.2s" values="50;5;50;50" calcMode="linear" repeatCount="indefinite" />
<animate attributeName="cx" begin="0s" dur="2.2s" values="5;27;49;5" calcMode="linear" repeatCount="indefinite" />
</circle>
<circle cx="27" cy="5" r="5">
<animate attributeName="cy" begin="0s" dur="2.2s" from="5" to="5" values="5;50;50;5" calcMode="linear" repeatCount="indefinite" />
<animate attributeName="cx" begin="0s" dur="2.2s" from="27" to="27" values="27;49;5;27" calcMode="linear" repeatCount="indefinite" />
</circle>
<circle cx="49" cy="50" r="5">
<animate attributeName="cy" begin="0s" dur="2.2s" values="50;50;5;50" calcMode="linear" repeatCount="indefinite" />
<animate attributeName="cx" from="49" to="49" begin="0s" dur="2.2s" values="49;5;27;49" calcMode="linear" repeatCount="indefinite" />
</circle>
</g>
</g>
</svg>

+ 5
- 8
packages/lesspass-pure/src/store/actions.js View File

@@ -42,14 +42,11 @@ export const logout = ({ commit }) => {
};

export const getPasswords = ({ commit }) => {
return Password.all()
.then(response => {
commit(types.LOGIN);
const passwords = response.data.results;
commit(types.SET_PASSWORDS, { passwords });
return passwords;
})
.catch(() => logout({ commit }));
return Password.all().then(response => {
const passwords = response.data.results;
commit(types.SET_PASSWORDS, { passwords });
return passwords;
});
};

export const saveOrUpdatePassword = ({ commit, state }) => {


+ 3
- 7
packages/lesspass-pure/src/store/defaultPassword.js View File

@@ -1,4 +1,6 @@
export const defaultOptions = {
export default {
login: "",
site: "",
uppercase: true,
lowercase: true,
numbers: true,
@@ -7,9 +9,3 @@ export const defaultOptions = {
counter: 1,
version: 2
};

export default {
login: "",
site: "",
...defaultOptions
};

+ 2
- 2
packages/lesspass-pure/src/store/getters.js View File

@@ -1,8 +1,8 @@
import btoa from "@oslab/btoa";

export const isAuthenticated = state => state.authenticated;
export const isAuthenticated = state => state.isAuthenticated;

export const isGuest = state => !state.authenticated;
export const isGuest = state => !state.isAuthenticated;

export const passwordURL = state => {
const base64PasswordProfile = btoa(JSON.stringify(state.password));


+ 2
- 2
packages/lesspass-pure/src/store/getters.test.js View File

@@ -36,7 +36,7 @@ test("passwordURL encode uri component", () => {

test("isAuthenticated", () => {
const state = {
authenticated: true
isAuthenticated: true
};
expect(getters.isAuthenticated(state)).toBe(true);
expect(getters.isGuest(state)).toBe(false);
@@ -44,7 +44,7 @@ test("isAuthenticated", () => {

test("isGuest", () => {
const state = {
authenticated: false
isAuthenticated: false
};
expect(getters.isAuthenticated(state)).toBe(false);
expect(getters.isGuest(state)).toBe(true);


+ 2
- 3
packages/lesspass-pure/src/store/index.js View File

@@ -9,12 +9,11 @@ import defaultPassword from "./defaultPassword";
Vue.use(Vuex);

const state = {
authenticated: localStorage.getItem("access_token") !== null,
isAuthenticated: false,
password: Object.assign({}, defaultPassword),
passwords: [],
message: "",
defaultPassword: defaultPassword,
showOptions: false
defaultPassword
};

export default new Vuex.Store({


+ 2
- 2
packages/lesspass-pure/src/store/mutations.js View File

@@ -2,14 +2,14 @@ import * as types from "./mutation-types";

export default {
[types.LOGIN](state) {
state.authenticated = true;
state.isAuthenticated = true;
},
[types.SET_TOKENS](state, { refresh_token, access_token }) {
localStorage.setItem("access_token", access_token);
localStorage.setItem("refresh_token", refresh_token);
},
[types.LOGOUT](state) {
state.authenticated = false;
state.isAuthenticated = false;
state.passwords = [];
localStorage.removeItem("access_token");
localStorage.removeItem("refresh_token");


+ 4
- 4
packages/lesspass-pure/src/store/mutations.test.js View File

@@ -5,10 +5,10 @@ import defaultPassword from "./defaultPassword";
test("LOGOUT", () => {
const LOGOUT = mutations[types.LOGOUT];
const state = {
authenticated: true
isAuthenticated: true
};
LOGOUT(state);
expect(state.authenticated).toBe(false);
expect(state.isAuthenticated).toBe(false);
});

test("RESET_PASSWORD set default password", () => {
@@ -35,9 +35,9 @@ test("LOGOUT clean user personal info", () => {

test("LOGIN", () => {
const LOGIN = mutations[types.LOGIN];
const state = { authenticated: false };
const state = { isAuthenticated: false };
LOGIN(state);
expect(state.authenticated).toBe(true);
expect(state.isAuthenticated).toBe(true);
});

test("SET_PASSWORD", () => {


+ 3
- 3
packages/lesspass-pure/src/views/Login.vue View File

@@ -81,7 +81,7 @@
</template>
<script>
import User from "../api/user";
import { defaultbaseURL } from "../api/default";
import { getBaseURL, defaultBaseURL } from "../api/baseURL";
import MasterPassword from "../components/MasterPassword.vue";
import message from "../services/message";
import { encryptPassword } from "../services/encryption";
@@ -92,7 +92,7 @@ export default {
email: "",
password: "",
encryptMasterPassword: true,
baseURL: localStorage.getItem("baseURL") || defaultbaseURL
baseURL: getBaseURL()
};
},
components: {
@@ -126,7 +126,7 @@ export default {
this.$router.push({ name: "home" });
})
.catch(err => {
if (err.response === undefined && baseURL !== defaultbaseURL) {
if (err.response === undefined && baseURL !== defaultBaseURL) {
message.error(
this.$t(
"DBNotRunning",


+ 5
- 7
packages/lesspass-pure/src/views/PasswordGenerator.vue View File

@@ -136,13 +136,11 @@ export default {
...mapGetters(["passwordURL"])
},
beforeMount() {
this.$store.dispatch("getPasswords").then(() => {
urlParser.getSite().then(site => {
this.$store.dispatch("loadPasswordProfile", { site });
});
this.$store.dispatch("getPasswordFromUrlQuery", {
query: this.$route.query
});
urlParser.getSite().then(site => {
this.$store.dispatch("loadPasswordProfile", { site });
});
this.$store.dispatch("getPasswordFromUrlQuery", {
query: this.$route.query
});
},
mounted() {


+ 3
- 3
packages/lesspass-pure/src/views/Register.vue View File

@@ -62,7 +62,7 @@
</template>
<script>
import User from "../api/user";
import { defaultbaseURL } from "../api/default";
import { getBaseURL, defaultBaseURL } from "../api/baseURL";
import MasterPassword from "../components/MasterPassword.vue";
import message from "../services/message";
import { encryptPassword } from "../services/encryption";
@@ -72,7 +72,7 @@ export default {
return {
email: "",
password: "",
baseURL: localStorage.getItem("baseURL") || defaultbaseURL
baseURL: getBaseURL()
};
},
components: {
@@ -117,7 +117,7 @@ export default {
.catch(() => message.displayGenericError());
})
.catch(err => {
if (err.response === undefined && baseURL !== defaultbaseURL) {
if (err.response === undefined && baseURL !== defaultBaseURL) {
message.error(
this.$t(
"DBNotRunning",


Loading…
Cancel
Save