diff --git a/app/js/jwt.js b/app/js/jwt.js deleted file mode 100644 index 43c2e6c..0000000 --- a/app/js/jwt.js +++ /dev/null @@ -1,22 +0,0 @@ -function parseJwt (token) { - var base64Url = token.split('.')[1]; - var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/'); - var jsonPayload = decodeURIComponent(window.atob(base64).split('').map(function(c) { - return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); - }).join('')); - - return JSON.parse(jsonPayload); -} - -function isJwtExpired (token) { - var jwt = parseJwt(token); - return jwt.exp < Date.now() / 1000; -} - -async function refreshAccessToken (refreshToken) { - const data = await fetch('/api/refresh', { - method: 'POST', - headers: {'Authorization': 'Bearer ' + refreshToken} - }); - return data.access_token; -} \ No newline at end of file diff --git a/app/main.py b/app/main.py index 4280275..9ef3e03 100644 --- a/app/main.py +++ b/app/main.py @@ -2,6 +2,7 @@ from fastapi import FastAPI, Depends, Request, Path from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import RedirectResponse, JSONResponse from fastapi.templating import Jinja2Templates +from fastapi.staticfiles import StaticFiles from app.routes.auth_routes import router as auth_router from app.routes.links_routes import router as links_router from app.routes.user_routes import router as user_router @@ -35,6 +36,7 @@ app.add_middleware( allow_credentials=True, ) +app.mount("/static", StaticFiles(directory="app/static"), name="static") templates = Jinja2Templates(directory="app/templates") # Import routes diff --git a/app/routes/auth_routes.py b/app/routes/auth_routes.py index 3054e22..ceb68b1 100644 --- a/app/routes/auth_routes.py +++ b/app/routes/auth_routes.py @@ -33,7 +33,7 @@ async def login_for_access_token( detail="Incorrect username or password", headers={"WWW-Authenticate": "Bearer"}, ) - access_token_expires = timedelta(minutes=15) + access_token_expires = timedelta(minutes=1) access_token = create_access_token( data={"sub": user.id, "username": user.username, "refresh": False}, expires_delta=access_token_expires, @@ -63,7 +63,7 @@ async def refresh_access_token( """ Return a new access token if the refresh token is valid """ - access_token_expires = timedelta(minutes=30) + access_token_expires = timedelta(minutes=1) access_token = create_access_token( data={"sub": current_user.id, "refresh": False}, expires_delta=access_token_expires, diff --git a/app/routes/links_routes.py b/app/routes/links_routes.py index 833c699..874f3c2 100644 --- a/app/routes/links_routes.py +++ b/app/routes/links_routes.py @@ -16,7 +16,7 @@ from app.util.authentication import get_current_user router = APIRouter(prefix="/links", tags=["links"]) -@router.get("/", summary="Get all of the links associated with your account") +@router.get("", summary="Get all of the links associated with your account") async def get_links( current_user: Annotated[User, Depends(get_current_user)], db=Depends(get_db), @@ -35,7 +35,7 @@ async def get_links( return links -@router.post("/", summary="Create a new link") +@router.post("", summary="Create a new link") async def create_link( url: URLSchema, current_user: Annotated[User, Depends(get_current_user)], diff --git a/app/static/js/jwt.js b/app/static/js/jwt.js new file mode 100644 index 0000000..1e07703 --- /dev/null +++ b/app/static/js/jwt.js @@ -0,0 +1,58 @@ +// Description: This file contains functions to access the API with JWT authentication. + +/** + * Accept a full URL, method, and body to send to the API. + * - If successful, return the response + * - If first fail, attempt to refresh JWT token and try again + * - If second fail, return false + * @param {*} endpoint API endpoint + * @param {*} method String (GET, POST, PUT, DELETE) + * @param {*} body Data to send to the API + * @returns boolean + */ +async function accessAPI(endpoint, method, body) { + let response = await fetch(`/api${endpoint}`, { + method: method, + body: body, + }); + + if (response.ok) { + let data = await response.json(); + data = await data; + console.log(data); + return data; + } else if (response.status === 401) { + console.log('REFRESHING TOKEN') + if (await refreshAccessToken()) { + // Try the request again + let response = await fetch(`/api${endpoint}`, { + method: method, + body: body, + }); + if (response.ok) { + let data = await response.json(); + data = await data; + console.log("REFRESHED DATA") + return data; + } + } + } + return false; +} + +/** + * Attempt to refresh the JWT token + * @returns boolean + */ +async function refreshAccessToken () { + const response = await fetch('/api/auth/refresh', { + method: 'POST', + }); + if (response.ok) { + console.log("TOKEN REFRESH") + return true; + } else { + console.log("TOKEN REFRESH FAILED") + return false; + } +} \ No newline at end of file diff --git a/app/templates/dashboard.html b/app/templates/dashboard.html index 0a383af..fa7458a 100644 --- a/app/templates/dashboard.html +++ b/app/templates/dashboard.html @@ -6,12 +6,13 @@
Link | Visits | Redirect | @@ -36,20 +37,30 @@ margin-top: 100px; } + table { margin: 20px 0 20px 0; text-align: center; font-size: 25px; - width: 1250px; + width: 1000px; color: #ccc; border-collapse: collapse; overflow: hidden; } + /* Center all sub tables */ + .log-table-row table { + margin: 0 auto; + } + + .log-table-row table { + width: 90%; + } + table th { background-color: #415eac; - padding: 10px; border: 2px solid #ccc; + padding: 10px; } .link-table-row { @@ -91,25 +102,6 @@
---|