Skip to content

Commit

Permalink
Fixing client bad handling of credentials
Browse files Browse the repository at this point in the history
  • Loading branch information
jaesivsm committed Jun 15, 2023
1 parent c23dc19 commit 27f3a63
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 26 deletions.
17 changes: 15 additions & 2 deletions jarr/api/auth.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import random
from datetime import datetime, timedelta, timezone

from flask import render_template
from flask_jwt_extended import (create_access_token, create_refresh_token,
Expand All @@ -18,8 +19,10 @@
{
"access_token": fields.String(
description="The token that must be place "
"in the Authorization header"
"in the Authorization header",
required=True,
),
"access_token_expires_at": fields.String(required=True),
"refresh_token": fields.String(),
},
)
Expand All @@ -42,6 +45,12 @@
)


def _get_declared_expiration_delay(factor=3 / 4) -> str:
declared_delay_sec = conf.auth.expiration_sec * factor
declared_delay = datetime.utcnow() + timedelta(seconds=declared_delay_sec)
return declared_delay.replace(tzinfo=timezone.utc).isoformat()


@auth_ns.route("")
class LoginResource(Resource):
@staticmethod
Expand All @@ -68,6 +77,7 @@ def post():
return {
"access_token": f"Bearer {access_token}",
"refresh_token": f"Bearer {refresh_token}",
"access_token_expires_at": _get_declared_expiration_delay(),
}, 200


Expand All @@ -86,7 +96,10 @@ def post():
{"last_connection": utc_now(), "renew_password_token": ""},
)
SERVER.labels(method="get", uri="/auth/refresh", result="2XX").inc()
return {"access_token": f"Bearer {access_token}"}, 200
return {
"access_token": f"Bearer {access_token}",
"access_token_expires_at": _get_declared_expiration_delay(),
}, 200


@auth_ns.route("/recovery")
Expand Down
2 changes: 1 addition & 1 deletion jarr/metaconf.yml
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ parameters:
help_txt: Salt for cryptografic purpose.
required: true
- expiration_sec:
default: 1200
default: 3600
help_txt: Expiration delay in second of the JWT token.
- refresh_token_expiration_days:
default: 30
Expand Down
31 changes: 12 additions & 19 deletions jsclient/src/authSlice.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,40 @@ import { apiUrl } from "./const";
import { storageGet, storageSet, storageRemove } from "./storageUtils";
import { authError } from "./features/noauth/noAuthSlice";


const refreshDelay = 10 * 60 * 1000; // 10 mn in miliseconds

const authSlice = createSlice({
name: "auth",
initialState: { accessToken: null,
accessTokenExpiresAt: null,
refreshToken: storageGet("refreshToken", "local"),
refreshedAt: null,
},
reducers: {
loginFailed: (state, action) => ({ accessToken: null, refreshToken: null }),
tokenAcquired: (state, action) => {
const accessToken = action.payload.data["access_token"];
const accessTokenExpiresAt = new Date(action.payload.data["access_token_expires_at"]).getTime();
const refreshToken = action.payload.data["refresh_token"];
if (refreshToken) {
storageSet("refreshToken", refreshToken, "local");
return { ...state, accessToken, refreshedAt: new Date().getTime() };
return { ...state, accessToken, refreshToken, accessTokenExpiresAt };
} else {
return { ...state, accessToken, refreshToken, refreshedAt: new Date().getTime() };
return { ...state, accessToken, accessTokenExpiresAt };
}
},
tokenExpire: (state, action) => {
storageRemove("refreshToken", "local");
return { ...state, accessToken: null, refreshToken: null};
},
doLogout: () => {
storageRemove("left-menu-open", "session");
purgeCredentials: () => {
storageRemove("refreshToken", "local");
return { accessToken: null, refreshToken: null };
return { accessToken: null, refreshToken: null, accessTokenExpiresAt: null };
},
},
});

export const { loginFailed, tokenAcquired, tokenExpire, doLogout } = authSlice.actions;
export const { tokenAcquired, purgeCredentials} = authSlice.actions;

export default authSlice.reducer;

export const doRetryOnTokenExpiration = async (payload, dispatch, getState) => {
const now = new Date();
const now = new Date().getTime();
const state = getState();
if (!state.auth.refreshedAt
|| (now.getTime() - state.auth.refreshedAt) > refreshDelay) {
if (!state.auth.accessTokenExpiresAt
|| state.auth.accessTokenExpiresAt <= now) {
// token has expired, trying to refresh it
try {
const result = await axios({
Expand All @@ -56,7 +48,7 @@ export const doRetryOnTokenExpiration = async (payload, dispatch, getState) => {
dispatch(tokenAcquired(result));
payload.headers = { "Authorization": result.data["access_token"] };
} catch (err) { // failed to refresh it, logging out
dispatch(loginFailed());
dispatch(purgeCredentials());
dispatch(authError({ statusText: "EXPIRED" }));
throw err;
}
Expand All @@ -70,6 +62,7 @@ export const doRetryOnTokenExpiration = async (payload, dispatch, getState) => {
if (err.response && err.response.status === 401
&& err.response.data && err.response.data.message
&& err.response.data.message === "Invalid token, Signature has expired") {
dispatch(purgeCredentials());
dispatch(authError({ statusText: "EXPIRED" }));
} else {
return err.response;
Expand Down
5 changes: 3 additions & 2 deletions jsclient/src/features/noauth/noAuthSlice.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import axios from "axios";
import { createSlice } from "@reduxjs/toolkit";
import { apiUrl } from "../../const";
import { tokenAcquired, loginFailed } from "../../authSlice";
import { tokenAcquired, purgeCredentials } from "../../authSlice";

const INITIAL_STATE = { loading: false,
loginError: null,
Expand Down Expand Up @@ -66,7 +66,7 @@ export const doLogin = (
dispatch(responseRecieved());
dispatch(tokenAcquired(result));
} catch (err) {
dispatch(loginFailed());
dispatch(purgeCredentials());
dispatch(authError(err.response));
}
};
Expand Down Expand Up @@ -136,6 +136,7 @@ export const doValidOAuth = (code): AppThunk => async (dispatch) => {
dispatch(responseRecieved());
dispatch(tokenAcquired(result));
} catch (err) {
dispatch(purgeCredentials());
dispatch(authError(err.response));
}
};
4 changes: 2 additions & 2 deletions jsclient/src/features/topmenu/TopMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import SettingsIcon from "@material-ui/icons/Settings";
import ExitToAppIcon from "@material-ui/icons/ExitToApp";
// jarr
import topMenuStyle from "./topMenuStyle";
import { doLogout } from "../../authSlice";
import { purgeCredentials } from "../../authSlice";
import { toggleMenu } from "../feedlist/slice";
import { markedAllAsRead } from "../clusterlist/slice";
import doListClusters from "../../hooks/doListClusters";
Expand Down Expand Up @@ -59,7 +59,7 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(doFetchObjForEdit("user"));
},
logout() {
dispatch(doLogout());
dispatch(purgeCredentials());
},
});

Expand Down

0 comments on commit 27f3a63

Please sign in to comment.