Update login.py to include request parameter in refresh_token endpoint

This commit is contained in:
anovazzi1 2024-01-08 15:35:10 -03:00
commit a7427ef20b
11 changed files with 91 additions and 79 deletions

View file

@ -1,4 +1,4 @@
from fastapi import Response, APIRouter, Depends, HTTPException, status
from fastapi import Request, Response, APIRouter, Depends, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm
from sqlmodel import Session
@ -59,7 +59,8 @@ async def auto_login(db: Session = Depends(get_session), settings_service=Depend
@router.post("/refresh")
async def refresh_token(response: Response, token: str):
async def refresh_token(request: Request, response: Response):
token = request.cookies.get("refresh_token_lf")
if token:
tokens = create_refresh_token(token)
response.set_cookie("refresh_token_lf", tokens["refresh_token"], httponly=True, secure=True, samesite="strict")

View file

@ -284,7 +284,7 @@
{ "name": "Accept-Language", "value": "en-US,en;q=0.9" },
{ "name": "Authorization", "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20" },
{ "name": "Connection", "value": "keep-alive" },
{ "name": "Cookie", "value": "access_tkn_lflw=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20; refresh_tkn_lflw=auto" },
{ "name": "Cookie", "value": "access_token_lf=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20; refresh_tkn_lflw=auto" },
{ "name": "Host", "value": "localhost:3000" },
{ "name": "Referer", "value": "http://localhost:3000/flows" },
{ "name": "Sec-Fetch-Dest", "value": "empty" },
@ -338,7 +338,7 @@
{ "name": "Accept-Language", "value": "en-US,en;q=0.9" },
{ "name": "Authorization", "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20" },
{ "name": "Connection", "value": "keep-alive" },
{ "name": "Cookie", "value": "access_tkn_lflw=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20; refresh_tkn_lflw=auto" },
{ "name": "Cookie", "value": "access_token_lf=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20; refresh_tkn_lflw=auto" },
{ "name": "Host", "value": "localhost:3000" },
{ "name": "Referer", "value": "http://localhost:3000/flows" },
{ "name": "Sec-Fetch-Dest", "value": "empty" },
@ -392,7 +392,7 @@
{ "name": "Accept-Language", "value": "en-US,en;q=0.9" },
{ "name": "Authorization", "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20" },
{ "name": "Connection", "value": "keep-alive" },
{ "name": "Cookie", "value": "access_tkn_lflw=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20; refresh_tkn_lflw=auto" },
{ "name": "Cookie", "value": "access_token_lf=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20; refresh_tkn_lflw=auto" },
{ "name": "Host", "value": "localhost:3000" },
{ "name": "Referer", "value": "http://localhost:3000/flows" },
{ "name": "Sec-Fetch-Dest", "value": "empty" },
@ -446,7 +446,7 @@
{ "name": "Accept-Language", "value": "en-US,en;q=0.9" },
{ "name": "Authorization", "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20" },
{ "name": "Connection", "value": "keep-alive" },
{ "name": "Cookie", "value": "access_tkn_lflw=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20; refresh_tkn_lflw=auto" },
{ "name": "Cookie", "value": "access_token_lf=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20; refresh_tkn_lflw=auto" },
{ "name": "Host", "value": "localhost:3000" },
{ "name": "Referer", "value": "http://localhost:3000/flow/2920dde2-5c24-4fe0-9c06-ef86b5a16a99" },
{ "name": "Sec-Fetch-Dest", "value": "empty" },
@ -500,7 +500,7 @@
{ "name": "Accept-Language", "value": "en-US,en;q=0.9" },
{ "name": "Authorization", "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20" },
{ "name": "Connection", "value": "keep-alive" },
{ "name": "Cookie", "value": "access_tkn_lflw=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20; refresh_tkn_lflw=auto" },
{ "name": "Cookie", "value": "access_token_lf=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20; refresh_tkn_lflw=auto" },
{ "name": "Host", "value": "localhost:3000" },
{ "name": "Referer", "value": "http://localhost:3000/flow/2920dde2-5c24-4fe0-9c06-ef86b5a16a99" },
{ "name": "Sec-Fetch-Dest", "value": "empty" },
@ -554,7 +554,7 @@
{ "name": "Accept-Language", "value": "en-US,en;q=0.9" },
{ "name": "Authorization", "value": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20" },
{ "name": "Connection", "value": "keep-alive" },
{ "name": "Cookie", "value": "access_tkn_lflw=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20; refresh_tkn_lflw=auto" },
{ "name": "Cookie", "value": "access_token_lf=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkMjUzYmZiYS02MzY4LTQ0ZGMtODVmNy0wZDZkYTllNDU5NjgiLCJleHAiOjE3MzM4NTY4OTh9.5MFFb0JCck3ITSKXbxhwO9yAscnXcwXNTV70ZYBRB20; refresh_tkn_lflw=auto" },
{ "name": "Host", "value": "localhost:3000" },
{ "name": "Referer", "value": "http://localhost:3000/flow/2920dde2-5c24-4fe0-9c06-ef86b5a16a99" },
{ "name": "Sec-Fetch-Dest", "value": "empty" },

View file

@ -1,19 +1,15 @@
import { useContext, useEffect } from "react";
import { useContext } from "react";
import { Navigate } from "react-router-dom";
import { AuthContext } from "../../contexts/authContext";
export const ProtectedAdminRoute = ({ children }) => {
const {
isAdmin,
isAuthenticated,
logout,
userData,
autoLogin,
} = useContext(AuthContext);
const { isAdmin, isAuthenticated, logout, userData, autoLogin } =
useContext(AuthContext);
if (!isAuthenticated) {
logout();
return <Navigate to="/login" replace />;
logout().then(() => {
return <Navigate to="/login" replace />;
});
}
if ((userData && !isAdmin) || autoLogin) {

View file

@ -3,11 +3,11 @@ import { Navigate } from "react-router-dom";
import { AuthContext } from "../../contexts/authContext";
export const ProtectedRoute = ({ children }) => {
const { isAuthenticated, logout} =
useContext(AuthContext);
const { isAuthenticated, logout } = useContext(AuthContext);
if (!isAuthenticated) {
logout();
return <Navigate to="/login" replace />;
logout().then(() => {
return <Navigate to="/login" replace />;
});
}
return children;

View file

@ -166,7 +166,10 @@ export default function Header(): JSX.Element {
<button
className={
"h-7 w-7 rounded-full focus-visible:outline-0 " +
(userData?.profile_image ?? gradients[parseInt(userData?.id ?? "", 30) % gradients.length])
(userData?.profile_image ??
gradients[
parseInt(userData?.id ?? "", 30) % gradients.length
])
}
/>
</DropdownMenuTrigger>
@ -190,8 +193,9 @@ export default function Header(): JSX.Element {
<DropdownMenuItem
className="cursor-pointer"
onClick={() => {
logout();
navigate("/login");
logout().then(() => {
navigate("/login");
});
}}
>
Sign Out

View file

@ -1,6 +1,10 @@
import { createContext, useEffect, useState } from "react";
import Cookies from "universal-cookie";
import { autoLogin as autoLoginApi, getLoggedUser } from "../controllers/API";
import {
autoLogin as autoLoginApi,
getLoggedUser,
requestLogout,
} from "../controllers/API";
import useAlertStore from "../stores/alertStore";
import { Users } from "../types/api";
import { AuthContextType } from "../types/contexts/auth";
@ -10,9 +14,8 @@ const initialValue: AuthContextType = {
setIsAdmin: () => false,
isAuthenticated: false,
accessToken: null,
refreshToken: null,
login: () => {},
logout: () => {},
logout: () => new Promise(() => {}),
userData: null,
setUserData: () => {},
authenticationErrorCount: 0,
@ -28,14 +31,11 @@ export const AuthContext = createContext<AuthContextType>(initialValue);
export function AuthProvider({ children }): React.ReactElement {
const cookies = new Cookies();
const [accessToken, setAccessToken] = useState<string | null>(
cookies.get("access_tkn_lflw")
);
const [refreshToken, setRefreshToken] = useState<string | null>(
cookies.get("refresh_tkn_lflw")
cookies.get("access_token_lf") ?? null
);
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(
!!cookies.get("refresh_tkn_lflw") && !!cookies.get("access_tkn_lflw")
);
!!cookies.get("access_token_lf")
);
const [isAdmin, setIsAdmin] = useState<boolean>(false);
const [userData, setUserData] = useState<Users | null>(null);
const [autoLogin, setAutoLogin] = useState<boolean>(false);
@ -45,7 +45,7 @@ export function AuthProvider({ children }): React.ReactElement {
);
useEffect(() => {
const storedAccessToken = cookies.get("access_tkn_lflw");
const storedAccessToken = cookies.get("access_token_lf");
if (storedAccessToken) {
setAccessToken(storedAccessToken);
}
@ -65,7 +65,7 @@ export function AuthProvider({ children }): React.ReactElement {
.then((user) => {
if (user && user["access_token"]) {
user["refresh_token"] = "auto";
login(user["access_token"], user["refresh_token"]);
login(user["access_token"]);
setUserData(user);
setAutoLogin(true);
setLoading(false);
@ -81,7 +81,7 @@ export function AuthProvider({ children }): React.ReactElement {
});
}, [setUserData, setLoading, autoLogin, setIsAdmin]);
function getUser(){
function getUser() {
getLoggedUser()
.then((user) => {
setUserData(user);
@ -95,25 +95,26 @@ export function AuthProvider({ children }): React.ReactElement {
});
}
function login(newAccessToken: string, refreshToken: string) {
cookies.set("access_tkn_lflw", newAccessToken, { path: "/" });
cookies.set("refresh_tkn_lflw", refreshToken, { path: "/" });
function login(newAccessToken: string) {
setAccessToken(newAccessToken);
setRefreshToken(refreshToken);
setIsAuthenticated(true);
setTimeout(() => {getUser();}, 500)
setTimeout(() => {
getUser();
}, 500);
}
function logout() {
cookies.remove("access_tkn_lflw", { path: "/" });
cookies.remove("refresh_tkn_lflw", { path: "/" });
cookies.remove("apikey_tkn_lflw", { path: "/" });
setIsAdmin(false);
setUserData(null);
setAccessToken(null);
setRefreshToken(null);
setIsAuthenticated(false);
async function logout() {
try {
await requestLogout();
cookies.remove("apikey_tkn_lflw", { path: "/" });
setIsAdmin(false);
setUserData(null);
setAccessToken(null);
setIsAuthenticated(false);
} catch (error) {
console.error(error);
throw error;
}
}
function storeApiKey(apikey: string) {
@ -129,7 +130,6 @@ export function AuthProvider({ children }): React.ReactElement {
setIsAdmin,
isAuthenticated,
accessToken,
refreshToken,
login,
logout,
setUserData,

View file

@ -13,7 +13,7 @@ const api: AxiosInstance = axios.create({
function ApiInterceptor() {
const setErrorData = useAlertStore((state) => state.setErrorData);
let { accessToken, login, logout, authenticationErrorCount } =
let { accessToken, login, logout, authenticationErrorCount, autoLogin } =
useContext(AuthContext);
const navigate = useNavigate();
const cookies = new Cookies();
@ -23,44 +23,48 @@ function ApiInterceptor() {
(response) => response,
async (error: AxiosError) => {
if (error.response?.status === 401) {
const refreshToken = cookies.get("refresh_tkn_lflw");
if (refreshToken && refreshToken !== "auto") {
const accessToken = cookies.get("access_token_lf");
if (accessToken && !autoLogin) {
authenticationErrorCount = authenticationErrorCount + 1;
if (authenticationErrorCount > 3) {
authenticationErrorCount = 0;
logout();
navigate("/login");
logout().then(() => {
navigate("/login");
});
}
try {
const res = await renewAccessToken(refreshToken);
const res = await renewAccessToken();
if (res?.data?.access_token && res?.data?.refresh_token) {
login(res?.data?.access_token, res?.data?.refresh_token);
login(res?.data?.access_token);
}
if (error?.config?.headers) {
delete error.config.headers["Authorization"];
error.config.headers["Authorization"] = `Bearer ${cookies.get(
"access_tkn_lflw"
"access_token_lf"
)}`;
const response = await axios.request(error.config);
return response;
}
} catch (error) {
if (axios.isAxiosError(error) && error.response?.status === 401) {
logout();
navigate("/login");
logout().then(() => {
navigate("/login");
});
} else {
console.error(error);
logout();
navigate("/login");
logout().then(() => {
navigate("/login");
});
}
}
}
if (!refreshToken && error?.config?.url?.includes("login")) {
if (!accessToken && error?.config?.url?.includes("login")) {
return Promise.reject(error);
} else {
logout();
navigate("/login");
logout().then(() => {
navigate("/login");
});
}
} else {
// if (URL_EXCLUDED_FROM_ERROR_RETRIES.includes(error.config?.url)) {

View file

@ -411,11 +411,9 @@ export async function autoLogin() {
}
}
export async function renewAccessToken(token: string) {
export async function renewAccessToken() {
try {
if (token) {
return await api.post(`${BASE_URL_API}refresh?token=${token}`);
}
return await api.post(`${BASE_URL_API}refresh`);
} catch (error) {
throw error;
}
@ -842,3 +840,13 @@ export async function updateFlowStore(
throw error;
}
}
export async function requestLogout() {
try {
const response = await api.post(`${BASE_URL_API}logout`);
return response.data;
} catch (error) {
console.error(error);
throw error;
}
}

View file

@ -4,7 +4,7 @@ import { Button } from "../../../components/ui/button";
import { Input } from "../../../components/ui/input";
import { CONTROL_LOGIN_STATE } from "../../../constants/constants";
import { AuthContext } from "../../../contexts/authContext";
import { getLoggedUser, onLogin } from "../../../controllers/API";
import { onLogin } from "../../../controllers/API";
import useAlertStore from "../../../stores/alertStore";
import { LoginType } from "../../../types/api";
import {
@ -34,7 +34,7 @@ export default function LoginAdminPage() {
};
onLogin(user)
.then((user) => {
login(user.access_token, user.refresh_token);
login(user.access_token);
navigate("/admin/");
})
.catch((error) => {

View file

@ -6,7 +6,7 @@ import { Button } from "../../components/ui/button";
import { Input } from "../../components/ui/input";
import { CONTROL_LOGIN_STATE } from "../../constants/constants";
import { AuthContext } from "../../contexts/authContext";
import { getLoggedUser, onLogin } from "../../controllers/API";
import { onLogin } from "../../controllers/API";
import useAlertStore from "../../stores/alertStore";
import { LoginType } from "../../types/api";
import {
@ -37,7 +37,7 @@ export default function LoginPage(): JSX.Element {
};
onLogin(user)
.then((user) => {
login(user.access_token, user.refresh_token);
login(user.access_token);
navigate("/");
})
.catch((error) => {

View file

@ -5,9 +5,8 @@ export type AuthContextType = {
setIsAdmin: (isAdmin: boolean) => void;
isAuthenticated: boolean;
accessToken: string | null;
refreshToken: string | null;
login: (accessToken: string, refreshToken: string) => void;
logout: () => void;
login: (accessToken: string) => void;
logout: () => Promise<void>;
userData: Users | null;
setUserData: (userData: Users | null) => void;
authenticationErrorCount: number;