Skip to content

Commit

Permalink
Integrate refresh token (#8)
Browse files Browse the repository at this point in the history
* Refactor Access Token

* Refresh tokens on 401 status

* Logout unauthorized user (#6)

Co-authored-by: Harshit Raj <[email protected]>

* Fix codeflow

Co-authored-by: Abhishek Shree <[email protected]>
  • Loading branch information
1-Harshit and abhishekshree authored Nov 28, 2021
1 parent 3e3e216 commit 7a1abf1
Show file tree
Hide file tree
Showing 20 changed files with 234 additions and 52 deletions.
5 changes: 4 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ function App() {
const [checkLoggedIn, setCheckLoggedIn] = useState(false);

useEffect(() => {
setCheckLoggedIn(false);
dispatch(setCurrentScreen(ScreenType.LOGIN));
dispatch(setIsAuthenticated(false));
isLoggedIn().then(({Status, RollNo}) => {
setCheckLoggedIn(true);
if (Status) {
Expand All @@ -39,7 +42,7 @@ function App() {
dispatch(setRollNo(RollNo));
}
});
});
}, [dispatch]);

return (
(fontsLoaded && checkLoggedIn) ?
Expand Down
26 changes: 25 additions & 1 deletion src/api/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,37 @@ const postLoginStatus = async (token: string): Promise<Response> => {
return response;
};

const getTokenRefreshed = async (accessToken: string, refreshToken: string): Promise<Response> => {
let payload, status, token;
await axios.get(API.BACKEND.BASE_URL + API.BACKEND.ENDPOINT.REFRESH_TOKEN, {
headers: {
"cookie": accessToken + ";" + refreshToken
}
}).then(res => {
token = res.headers["set-cookie"][0];
payload = res.data;
status = res.status;
}).catch(err => {
payload = err?.response?.data.error ?? API.BACKEND.ERROR.NETWORK.PAYLOAD;
status = err?.response?.status ?? API.BACKEND.ERROR.NETWORK.STATUS;
});
const response: Response = {
Payload: payload,
Status: status,
Token: token
};
return response;
};

export {
OTPParams,
SignupParams,
LoginParams,
Response,
postOTP,
postSignup,
postLogin,
postLogout,
postLoginStatus
postLoginStatus,
getTokenRefreshed
};
7 changes: 5 additions & 2 deletions src/callbacks/auth/checkLogin.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { postLoginStatus } from "api/auth";
import { getToken } from "secure-store";
import { getAccessToken } from "secure-store";

import { refreshToken } from "./refresh";

interface CheckStatus {
RollNo: string;
Status: boolean;
}

export const isLoggedIn = async (): Promise<CheckStatus> => {
const token = await getToken();
await refreshToken();
const token = await getAccessToken();
let rollNo = "";
let status = false;
if (token) {
Expand Down
19 changes: 13 additions & 6 deletions src/callbacks/auth/login.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import { showMessage } from "react-native-flash-message";

import { saveToken } from "secure-store";
import { saveAccessToken, saveRefreshToken } from "secure-store";
import { auth } from "api";

export const login = async (params: auth.LoginParams): Promise<boolean> => {
const response = await auth.postLogin(params);
if (response.Status === 200) {
const tokenArr = response.Token.split(";");
if (tokenArr.length > 0) {
saveToken(tokenArr[0]); // Save new token
}
return true;
return await saveToken(response.Token);
} else {
showMessage({
message: response.Payload,
Expand All @@ -19,3 +15,14 @@ export const login = async (params: auth.LoginParams): Promise<boolean> => {
return false;
}
};

export const saveToken = async (tokens: string): Promise<boolean> => {
const tokenArr = tokens.split(";");
if (tokenArr.length > 0) {
await saveAccessToken(tokenArr[0]);
await saveRefreshToken(tokenArr[3]);
return true;
} else {
return false;
}
};
22 changes: 22 additions & 0 deletions src/callbacks/auth/refresh.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { getTokenRefreshed, Response } from "api/auth";
import { getAccessToken, getRefreshToken } from "secure-store";

import { saveToken } from "./login";

export const refreshToken = async (): Promise<boolean> => {
const accessToken = await getAccessToken();
const refreshToken = await getRefreshToken();
let res: Response;

if (accessToken && refreshToken) {
res = await getTokenRefreshed(accessToken, refreshToken);
} else {
return false;
}

if (res.Status === 200) {
return await saveToken(res.Token);
}

return false;
};
19 changes: 16 additions & 3 deletions src/callbacks/history/history.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
import { showMessage } from "react-native-flash-message";

import { history } from "api";
import { getToken } from "secure-store";
import { getAccessToken } from "secure-store";
import { refreshToken } from "callbacks/auth/refresh";
import { API } from "constant";

export const getHistory = async (rollNo: string): Promise<history.TransactionHistory[]> => {
const token = await getToken();
const token = await getAccessToken();
if(token) {
const res = await history.getTransactionHistory({ RollNo: rollNo }, token);
if(res.Status == 200){
return res.Payload;
} else {
} else if(res.Status == 401) {
const ok = await refreshToken();
if(ok) {
return getHistory(rollNo);
} else {
showMessage({
message: API.BACKEND.ERROR.EXPIRED.PAYLOAD,
type: "danger",
});
}
}
else {
showMessage({
message: res.Error,
type: "danger",
Expand Down
16 changes: 14 additions & 2 deletions src/callbacks/user/name.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
import { showMessage } from "react-native-flash-message";

import { user } from "api";
import { getToken } from "secure-store";
import { getAccessToken } from "secure-store";
import { API } from "constant";
import { refreshToken } from "callbacks/auth/refresh";

export const getName = async (rollNo: string): Promise<string> => {
const token = await getToken();
const token = await getAccessToken();
if(token) {
const res = await user.getUsername(rollNo, token);
if(res.Status == 200){
return res.Payload;
} else if(res.Status == 401) {
const ok = await refreshToken();
if(ok) {
return getName(rollNo);
} else {
showMessage({
message: API.BACKEND.ERROR.EXPIRED.PAYLOAD,
type: "danger",
});
}
} else {
showMessage({
message: res.Payload,
Expand Down
16 changes: 14 additions & 2 deletions src/callbacks/wallet/balance.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
import { showMessage } from "react-native-flash-message";

import { wallet } from "api";
import { getToken } from "secure-store";
import { getAccessToken } from "secure-store";
import { refreshToken } from "callbacks/auth/refresh";
import { API } from "constant";

export const getBalance = async (rollNo: string): Promise<number> => {
const token = await getToken();
const token = await getAccessToken();
if(token) {
const res = await wallet.getWalletBalance(rollNo, token);
if(res.Status == 200){
return parseInt(res.Payload);
} else if(res.Status == 401) {
const ok = await refreshToken();
if(ok) {
return getBalance(rollNo);
} else {
showMessage({
message: API.BACKEND.ERROR.EXPIRED.PAYLOAD,
type: "danger",
});
}
} else {
showMessage({
message: res.Payload,
Expand Down
16 changes: 14 additions & 2 deletions src/callbacks/wallet/redeem.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
import { showMessage } from "react-native-flash-message";

import { wallet } from "api";
import { getToken } from "secure-store";
import { getAccessToken } from "secure-store";
import { refreshToken } from "callbacks/auth/refresh";
import { API } from "constant";

export const redeemRequest = async (params: wallet.RedeemNewParams): Promise<string> => {
const token = await getToken();
const token = await getAccessToken();
if (token) {
const res = await wallet.postNewRedeem(params, token);
if (res.Status === 200) {
return res.Payload;
} else if(res.Status == 401) {
const ok = await refreshToken();
if(ok) {
return redeemRequest(params);
} else {
showMessage({
message: API.BACKEND.ERROR.EXPIRED.PAYLOAD,
type: "danger",
});
}
}else {
showMessage({
message: res.Payload,
Expand Down
16 changes: 14 additions & 2 deletions src/callbacks/wallet/tax.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
import { showMessage } from "react-native-flash-message";

import { wallet } from "api";
import { getToken } from "secure-store";
import { getAccessToken } from "secure-store";
import { refreshToken } from "callbacks/auth/refresh";
import { API } from "constant";

export const getTax = async (params: wallet.TransferTaxParams): Promise<number> => {
const token = await getToken();
const token = await getAccessToken();
if(token) {
const res = await wallet.postTransferTax(params, token);
if(res.Status == 200){
return parseInt(res.Payload);
} else if(res.Status == 401) {
const ok = await refreshToken();
if(ok) {
return getTax(params);
} else {
showMessage({
message: API.BACKEND.ERROR.EXPIRED.PAYLOAD,
type: "danger",
});
}
} else {
showMessage({
message: res.Payload,
Expand Down
16 changes: 14 additions & 2 deletions src/callbacks/wallet/transfer.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
import { showMessage } from "react-native-flash-message";

import { wallet } from "api";
import { getToken } from "secure-store";
import { getAccessToken } from "secure-store";
import { refreshToken } from "callbacks/auth/refresh";
import { API } from "constant";

export const transfer = async (params: wallet.WalletTransferParams): Promise<string> => {
const token = await getToken();
const token = await getAccessToken();
if (token) {
const res = await wallet.postWalletTransfer(params, token);
if (res.Status === 200) {
return res.Payload;
} else if(res.Status == 401) {
const ok = await refreshToken();
if(ok) {
return transfer(params);
} else {
showMessage({
message: API.BACKEND.ERROR.EXPIRED.PAYLOAD,
type: "danger",
});
}
}else {
showMessage({
message: res.Payload,
Expand Down
5 changes: 5 additions & 0 deletions src/constant/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const BACKEND = {
"LOGIN": "/auth/login",
"LOGOUT": "/auth/logout",
"CHECK_LOGIN": "/auth/check",
"REFRESH_TOKEN": "/auth/refresh",
"USERNAME": "/user/name",
"WALLET_TRANSFER": "/wallet/transfer",
"WALLET_BALANCE": "/wallet/balance",
Expand All @@ -18,6 +19,10 @@ const BACKEND = {
"PAYLOAD": "Oops! Server didn't Respond. Please try again later.",
"STATUS": 500
},
EXPIRED: {
"PAYLOAD": "Oops! Session Expired. Please login again.",
"STATUS": 401
}
},
};

Expand Down
7 changes: 4 additions & 3 deletions src/screens/Account/AccountScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Button, Text, WalletBalance, UserInfo } from "components";
import { ScreenType } from "screens/screen.types";
import { setCurrentScreen, setIsAuthenticated } from "redux-store/actions";
import { LABELS } from "constant";
import { deleteToken, getToken } from "secure-store";
import { deleteAccessToken, deleteRefreshToken, getRefreshToken } from "secure-store";
import { postLogout } from "api/auth";

import styles from "../screen.styles";
Expand All @@ -33,11 +33,12 @@ const AccountScreen: () => JSX.Element = () => {
};

const onLogout = () => {
getToken().then(token => {
getRefreshToken().then(token => {
if (token) {
postLogout(token).then((res) => {
if (res.Status == 200) {
deleteToken();
deleteAccessToken();
deleteRefreshToken();
}
});
}
Expand Down
14 changes: 12 additions & 2 deletions src/screens/Home/HomeScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import { View } from "react-native";
import * as Animatable from "react-native-animatable";

import { AppState } from "redux-store/reducers";
import { setCoins, setCurrentScreen, setName } from "redux-store/actions";
import { setCoins, setCurrentScreen, setIsAuthenticated, setName } from "redux-store/actions";
import { History, Text } from "components";
import { ScreenType } from "screens/screen.types";
import { NavCard, WalletBalance } from "components/Card";
import { history } from "api";
import LABELS from "constant/labels";
import { getBalance, getHistory, getName } from "callbacks";
import { refreshToken } from "callbacks/auth/refresh";

import styles from "../screen.styles";

Expand All @@ -37,8 +38,8 @@ const HomeScreen: () => JSX.Element = () => {
const [transaction, setTransaction] = useState<history.TransactionHistory[]>([]);

const getDetails = async (rollNo: string): Promise<[history.TransactionHistory[], number, string]> => {
const balance = await getBalance(rollNo); // to prevent async refresh tokens
const historyList = getHistory(rollNo);
const balance = getBalance(rollNo);
const name = getName(rollNo);

return Promise.all([historyList, balance, name]);
Expand All @@ -47,6 +48,15 @@ const HomeScreen: () => JSX.Element = () => {
useEffect(() => {
setIsFetched(false);
getDetails(rollNo).then(([historyList, balance, name]) => {
if(historyList === [] || balance === 0 || name === "") {
refreshToken().then((ok) => {
if (!ok) {
dispatch(setIsAuthenticated(false));
dispatch(setCurrentScreen(ScreenType.LOGIN));
return;
}
});
}
setTransaction(historyList);
dispatch(setCoins(balance));
dispatch(setName(name));
Expand Down
Loading

0 comments on commit 7a1abf1

Please sign in to comment.