Skip to content

Commit af8868b

Browse files
authored
Merge pull request #278 from Seluj78/273-hook-login-frontend-to-backend
273 hook login frontend to backend
2 parents e006372 + cd3ee32 commit af8868b

File tree

14 files changed

+277
-17
lines changed

14 files changed

+277
-17
lines changed

.env.enc

128 Bytes
Binary file not shown.

backend/PyMatcha/utils/password.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
along with this program. If not, see <https://www.gnu.org/licenses/>.
1818
"""
1919
from argon2 import PasswordHasher
20+
from argon2.exceptions import VerificationError
2021

2122
ph = PasswordHasher()
2223

@@ -26,4 +27,9 @@ def hash_password(password: str) -> str:
2627

2728

2829
def check_password(hash: str, password: str) -> bool:
29-
return ph.verify(hash, password)
30+
try:
31+
ph.verify(hash, password)
32+
except VerificationError:
33+
return False
34+
else:
35+
return True

docker-compose.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@ services:
8585
dockerfile: frontend.Dockerfile
8686
volumes:
8787
- './frontend:/frontend'
88-
- '/frontend/node_modules'
8988
ports:
9089
- '4242:8080'
9190
networks:

frontend/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,14 @@
1313
"@sentry/tracing": "^5.24.2",
1414
"axios": "^0.20.0",
1515
"core-js": "^3.6.5",
16+
"jwt-decode": "^3.0.0-beta.2",
17+
"secure-ls": "^1.2.6",
1618
"tailwindcss": "^1.8.10",
1719
"vee-validate": "^3.3.11",
1820
"vue": "^2.6.11",
1921
"vue-router": "^3.2.0",
20-
"vuex": "^3.4.0"
22+
"vuex": "^3.4.0",
23+
"vuex-persistedstate": "^3.1.0"
2124
},
2225
"devDependencies": {
2326
"@fullhuman/postcss-purgecss": "^2.3.0",

frontend/src/auth/logOut.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/* eslint-disable import/prefer-default-export */
2+
import Axios from 'axios';
3+
4+
export const logOut = async () => {
5+
await Axios.delete('/auth/access_revoke');
6+
await Axios.delete('/auth/refresh_revoke');
7+
localStorage.removeItem(process.env.VUE_APP_ACCESS_TOKEN);
8+
localStorage.removeItem(process.env.VUE_APP_REFRESH_TOKEN);
9+
localStorage.removeItem(process.env.VUE_APP_VUEX_PERSISTED_STATE);
10+
localStorage.removeItem(process.env.VUE_APP_SECURE_LS_METADATA);
11+
await this.$store.dispatch('logout');
12+
await this.$router.push('/accounts/signin');
13+
};

frontend/src/auth/tokens.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/* eslint-disable max-len */
2+
import jwtDecode from 'jwt-decode';
3+
import Axios from 'axios';
4+
import { logOut } from './logOut';
5+
6+
export const setAccessToken = (token) => (localStorage.setItem(process.env.VUE_APP_ACCESS_TOKEN, token));
7+
export const setRefreshToken = (token) => (localStorage.setItem(process.env.VUE_APP_REFRESH_TOKEN, token));
8+
9+
export const getAccessToken = () => (localStorage.getItem(process.env.VUE_APP_ACCESS_TOKEN));
10+
export const getRefreshToken = () => (localStorage.getItem(process.env.VUE_APP_REFRESH_TOKEN));
11+
12+
export const tokenIsValid = (token) => {
13+
try {
14+
const { exp } = jwtDecode(token);
15+
return (Date.now() < exp * 1000);
16+
} catch (error) {
17+
return false;
18+
}
19+
};
20+
21+
export const renewAccessToken = async () => {
22+
if (getRefreshToken()) {
23+
try {
24+
const response = await Axios.post('/auth/refresh', {});
25+
localStorage.setItem('matchaAccessToken', response.data.access_token);
26+
} catch (error) {
27+
await logOut();
28+
}
29+
}
30+
};
31+
32+
export const handleAccessTokenExpiration = async () => {
33+
const accessToken = getAccessToken();
34+
if (accessToken && !tokenIsValid(accessToken)) {
35+
await renewAccessToken();
36+
}
37+
};

frontend/src/plugins/http.js

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,33 @@
1+
/* eslint-disable prefer-arrow-callback */
2+
/* eslint-disable func-names */
3+
/* eslint-disable no-param-reassign */
4+
15
import Axios from 'axios';
26
import Vue from 'vue';
7+
import { getAccessToken, getRefreshToken, handleAccessTokenExpiration } from '../auth/tokens';
38

4-
function createAxiosInstance(baseURL) {
5-
return Axios.create({
6-
baseURL,
7-
headers: {
8-
'Content-Type': 'application/json',
9-
Authorization: `Bearer ${localStorage.token}`,
10-
},
11-
});
12-
}
9+
Axios.defaults.baseURL = process.env.VUE_APP_BACKEND_BASE_URL;
1310

14-
const axiosInstance = createAxiosInstance(process.env.VUE_APP_BACKEND_BASE_URL);
11+
Axios.interceptors.request.use(async function (config) {
12+
if (config.url === '/auth/refresh' || config.url === '/auth/refresh_revoke') {
13+
config.headers.Authorization = `Bearer ${getRefreshToken()}`;
14+
} else if (config.url === '/auth/access_revoke') {
15+
config.headers.Authorization = `Bearer ${getAccessToken()}`;
16+
} else if (getAccessToken()) {
17+
await handleAccessTokenExpiration();
18+
if (getAccessToken()) {
19+
config.headers.Authorization = `Bearer ${getAccessToken()}`;
20+
} else {
21+
return { headers: {}, method: config.method, url: '' };
22+
}
23+
}
24+
return config;
25+
}, function (error) {
26+
return Promise.reject(error);
27+
});
1528

1629
export default {
1730
install() {
18-
Vue.prototype.$http = axiosInstance;
31+
Vue.prototype.$http = Axios;
1932
},
2033
};

frontend/src/router/index.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,27 @@ import AccountVerified from '../views/auth/AccountVerified.vue';
88
import AccountVerifiedError from '../views/auth/AccountVerifiedError.vue';
99
import ResetPassword from '../views/auth/ResetPassword.vue';
1010
import ResetPasswordError from '../views/auth/ResetPasswordError.vue';
11+
import Onboarding from '../views/app/Onboarding.vue';
12+
import store from '../store/index';
1113

1214
Vue.use(VueRouter);
1315

16+
function loggedInRedirectBrowse(to, from, next) {
17+
if (store.getters.getLoggedInUser) {
18+
next('/browse');
19+
} else {
20+
next();
21+
}
22+
}
23+
24+
function notLoggedInRedirectLogin(to, from, next) {
25+
if (store.getters.getLoggedInUser) {
26+
next();
27+
} else {
28+
next('/accounts/signin');
29+
}
30+
}
31+
1432
const routes = [
1533
{
1634
path: '/',
@@ -21,36 +39,49 @@ const routes = [
2139
path: '/accounts/signup',
2240
name: 'SignUp',
2341
component: SignUp,
42+
beforeEnter: loggedInRedirectBrowse,
2443
},
2544
{
2645
path: '/accounts/signin',
2746
name: 'SignIn',
2847
component: SignIn,
48+
beforeEnter: loggedInRedirectBrowse,
2949
},
3050
{
3151
path: '/accounts/password/forgot',
3252
name: 'ForgotPassword',
3353
component: ForgotPassword,
54+
beforeEnter: loggedInRedirectBrowse,
3455
},
3556
{
3657
path: '/accounts/password/reset',
3758
name: 'ResetPassword',
3859
component: ResetPassword,
60+
beforeEnter: loggedInRedirectBrowse,
3961
},
4062
{
4163
path: '/accounts/password/reseterror',
4264
name: 'ResetPasswordError',
4365
component: ResetPasswordError,
66+
beforeEnter: loggedInRedirectBrowse,
4467
},
4568
{
4669
path: '/accounts/verify',
4770
name: 'AccountVerified',
4871
component: AccountVerified,
72+
beforeEnter: loggedInRedirectBrowse,
4973
},
5074
{
5175
path: '/accounts/verify/error',
5276
name: 'AccountVerifiedError',
5377
component: AccountVerifiedError,
78+
beforeEnter: loggedInRedirectBrowse,
79+
},
80+
{
81+
path: '/onboarding',
82+
name: 'Onboarding',
83+
component: Onboarding,
84+
beforeEnter: notLoggedInRedirectLogin,
5485
},
5586
];
5687

frontend/src/store/index.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,32 @@
11
import Vue from 'vue';
22
import Vuex from 'vuex';
3+
import createPersistedState from 'vuex-persistedstate';
4+
import SecureLS from 'secure-ls';
5+
import user from './modules/user';
6+
7+
const ls = new SecureLS({ isCompression: false });
38

49
Vue.use(Vuex);
510

611
export default new Vuex.Store({
12+
strict: true,
713
state: {
814
},
915
mutations: {
1016
},
1117
actions: {
1218
},
1319
modules: {
20+
user,
1421
},
22+
plugins: [
23+
createPersistedState({
24+
paths: ['user'],
25+
storage: {
26+
getItem: (key) => ls.get(key),
27+
setItem: (key, value) => ls.set(key, value),
28+
removeItem: (key) => ls.remove(key),
29+
},
30+
}),
31+
],
1532
});

frontend/src/store/modules/user.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/* eslint-disable no-shadow */
2+
3+
const state = {
4+
user: null,
5+
};
6+
7+
const getters = {
8+
getLoggedInUser(state) {
9+
return state.user;
10+
},
11+
};
12+
13+
const mutations = {
14+
login(state, user) {
15+
state.user = user;
16+
},
17+
logout(state) {
18+
state.user = null;
19+
},
20+
};
21+
22+
const actions = {
23+
login(state, user) {
24+
state.commit('login', user);
25+
},
26+
logout(state) {
27+
state.commit('logout');
28+
},
29+
};
30+
31+
export default {
32+
state,
33+
getters,
34+
mutations,
35+
actions,
36+
};

0 commit comments

Comments
 (0)