feat: use requests with auth header for home

This commit is contained in:
swve 2023-05-22 16:51:51 +00:00
parent 3457b57c58
commit 9f28dfe7e8
7 changed files with 83 additions and 40 deletions

View file

@ -1,17 +1,16 @@
export const dynamic = 'force-dynamic'; export const dynamic = 'force-dynamic';
import { Metadata, ResolvingMetadata } from 'next'; import { Metadata, ResolvingMetadata } from 'next';
import { Menu } from "@components/UI/Elements/Menu";
import { getBackendUrl, getUriWithOrg } from "@services/config/config"; import { getBackendUrl, getUriWithOrg } from "@services/config/config";
import { getOrgCourses } from "@services/courses/courses"; import { getCourse, getOrgCourses, getOrgCoursesWithAuthHeader } from "@services/courses/courses";
import CoursesLogo from "public/svg/courses.svg"; import CoursesLogo from "public/svg/courses.svg";
import CollectionsLogo from "public/svg/collections.svg"; import CollectionsLogo from "public/svg/collections.svg";
import Link from "next/link"; import Link from "next/link";
import Image from "next/image"; import Image from "next/image";
import { log } from "console"; import { getOrgCollections, getOrgCollectionsWithAuthHeader } from "@services/courses/collections";
import AuthProvider from "@components/Security/AuthProvider";
import { getOrgCollections } from "@services/courses/collections";
import { getOrganizationContextInfo } from '@services/organizations/orgs'; import { getOrganizationContextInfo } from '@services/organizations/orgs';
import { cookies } from 'next/headers';
type MetadataProps = { type MetadataProps = {
params: { orgslug: string }; params: { orgslug: string };
searchParams: { [key: string]: string | string[] | undefined }; searchParams: { [key: string]: string | string[] | undefined };
@ -21,6 +20,7 @@ export async function generateMetadata(
{ params }: MetadataProps, { params }: MetadataProps,
): Promise<Metadata> { ): Promise<Metadata> {
// Get Org context information // Get Org context information
const org = await getOrganizationContextInfo(params.orgslug, { revalidate: 1800, tags: ['organizations'] }); const org = await getOrganizationContextInfo(params.orgslug, { revalidate: 1800, tags: ['organizations'] });
return { return {
@ -31,8 +31,12 @@ export async function generateMetadata(
const OrgHomePage = async (params: any) => { const OrgHomePage = async (params: any) => {
const orgslug = params.params.orgslug; const orgslug = params.params.orgslug;
const courses = await getOrgCourses(orgslug, { revalidate: 360 , tags: ['courses'] }); const cookieStore = cookies();
const collections = await getOrgCollections(); const access_token_cookie: any = cookieStore.get('access_token_cookie');
const courses = await getOrgCoursesWithAuthHeader(orgslug, { revalidate: 360, tags: ['courses'] }, access_token_cookie.value);
const collections = await getOrgCollectionsWithAuthHeader(access_token_cookie.value);
// function to remove "course_" from the course_id // function to remove "course_" from the course_id
function removeCoursePrefix(course_id: string) { function removeCoursePrefix(course_id: string) {

View file

@ -12,8 +12,7 @@ export async function loginAndGetToken(username: string, password: string): Prom
// Request Config // Request Config
// get origin // get origin
const origin = window.location.origin; const HeadersConfig = new Headers({ "Content-Type": "application/x-www-form-urlencoded" });
const HeadersConfig = new Headers({ "Content-Type": "application/x-www-form-urlencoded" , Origin: origin });
const urlencoded = new URLSearchParams({ username: username, password: password }); const urlencoded = new URLSearchParams({ username: username, password: password });
const requestOptions: any = { const requestOptions: any = {

View file

@ -1,5 +1,5 @@
import { getAPIUrl } from "../config/config"; import { getAPIUrl } from "../config/config";
import { RequestBody, errorHandling } from "@services/utils/ts/requests"; import { RequestBody, RequestBodyWithAuthHeader, errorHandling } from "@services/utils/ts/requests";
/* /*
This file includes only POST, PUT, DELETE requests This file includes only POST, PUT, DELETE requests
@ -19,7 +19,7 @@ export async function createCollection(collection: any) {
return res; return res;
} }
// Get collections // Get collections
// TODO : add per org filter // TODO : add per org filter
export async function getOrgCollections() { export async function getOrgCollections() {
const result: any = await fetch(`${getAPIUrl()}collections/page/1/limit/10`, { next: { revalidate: 10 } }); const result: any = await fetch(`${getAPIUrl()}collections/page/1/limit/10`, { next: { revalidate: 10 } });
@ -27,3 +27,8 @@ export async function getOrgCollections() {
return res; return res;
} }
export async function getOrgCollectionsWithAuthHeader(access_token: string) {
const result: any = await fetch(`${getAPIUrl()}collections/page/1/limit/10`, RequestBodyWithAuthHeader("GET", null, { revalidate: 10 }, access_token));
const res = await errorHandling(result);
return res;
}

View file

@ -1,5 +1,5 @@
import { getAPIUrl } from "@services/config/config"; import { getAPIUrl } from "@services/config/config";
import { RequestBody, RequestBodyForm, errorHandling } from "@services/utils/ts/requests"; import { RequestBody, RequestBodyForm, RequestBodyWithAuthHeader, errorHandling } from "@services/utils/ts/requests";
/* /*
This file includes only POST, PUT, DELETE requests This file includes only POST, PUT, DELETE requests
@ -9,10 +9,17 @@ import { RequestBody, RequestBodyForm, errorHandling } from "@services/utils/ts/
export async function getOrgCourses(org_id: number, next: any) { export async function getOrgCourses(org_id: number, next: any) {
const result: any = await fetch(`${getAPIUrl()}courses/org_slug/${org_id}/page/1/limit/10`, RequestBody("GET", null, next)); const result: any = await fetch(`${getAPIUrl()}courses/org_slug/${org_id}/page/1/limit/10`, RequestBody("GET", null, next));
const res = await errorHandling(result); const res = await errorHandling(result);
return res; return res;
} }
export async function getOrgCoursesWithAuthHeader(org_id: number, next: any, access_token: string) {
const result: any = await fetch(`${getAPIUrl()}courses/org_slug/${org_id}/page/1/limit/10`, RequestBodyWithAuthHeader("GET", null, next, access_token));
const res = await errorHandling(result);
return res;
}
export async function getCourse(course_id: string, next: any) { export async function getCourse(course_id: string, next: any) {
const result: any = await fetch(`${getAPIUrl()}courses/${course_id}`, RequestBody("GET", null, next)); const result: any = await fetch(`${getAPIUrl()}courses/${course_id}`, RequestBody("GET", null, next));
const res = await errorHandling(result); const res = await errorHandling(result);

View file

@ -18,6 +18,20 @@ export const RequestBody = (method: string, data: any, next: any) => {
return options; return options;
}; };
export const RequestBodyWithAuthHeader = (method: string, data: any, next: any, token: string) => {
let HeadersConfig = new Headers({ Authorization: `Bearer ${token}` });
let options: any = {
method: method,
headers: HeadersConfig,
redirect: "follow",
credentials: "include",
body: data,
// Next.js
next: next,
};
return options;
};
export const RequestBodyForm = (method: string, data: any, next: any) => { export const RequestBodyForm = (method: string, data: any, next: any) => {
let HeadersConfig = new Headers({}); let HeadersConfig = new Headers({});
let options: any = { let options: any = {

View file

@ -1,13 +1,14 @@
from urllib.request import Request from urllib.request import Request
from fastapi import Depends, APIRouter, HTTPException, status, Request from fastapi import Depends, APIRouter, HTTPException, Response, status, Request
from fastapi.security import OAuth2PasswordRequestForm from fastapi.security import OAuth2PasswordRequestForm
from src.security.auth import * from src.security.auth import *
from src.services.users.users import * from src.services.users.users import *
router = APIRouter() router = APIRouter()
@router.post('/refresh')
@router.post("/refresh")
def refresh(Authorize: AuthJWT = Depends()): def refresh(Authorize: AuthJWT = Depends()):
""" """
The jwt_refresh_token_required() function insures a valid refresh The jwt_refresh_token_required() function insures a valid refresh
@ -18,11 +19,17 @@ def refresh(Authorize: AuthJWT = Depends()):
Authorize.jwt_refresh_token_required() Authorize.jwt_refresh_token_required()
current_user = Authorize.get_jwt_subject() current_user = Authorize.get_jwt_subject()
new_access_token = Authorize.create_access_token(subject=current_user) # type: ignore new_access_token = Authorize.create_access_token(subject=current_user) # type: ignore
return {"access_token": new_access_token} return {"access_token": new_access_token}
@router.post('/login')
async def login(request: Request,Authorize: AuthJWT = Depends(), form_data: OAuth2PasswordRequestForm = Depends()): @router.post("/login")
async def login(
request: Request,
response: Response,
Authorize: AuthJWT = Depends(),
form_data: OAuth2PasswordRequestForm = Depends(),
):
user = await authenticate_user(request, form_data.username, form_data.password) user = await authenticate_user(request, form_data.username, form_data.password)
if not user: if not user:
raise HTTPException( raise HTTPException(
@ -34,10 +41,17 @@ async def login(request: Request,Authorize: AuthJWT = Depends(), form_data: OAut
access_token = Authorize.create_access_token(subject=form_data.username) access_token = Authorize.create_access_token(subject=form_data.username)
refresh_token = Authorize.create_refresh_token(subject=form_data.username) refresh_token = Authorize.create_refresh_token(subject=form_data.username)
Authorize.set_refresh_cookies(refresh_token) Authorize.set_refresh_cookies(refresh_token)
Authorize.set_access_cookies(access_token) # set cookies using fastapi
return {"access_token": access_token , "refresh_token": refresh_token} response.set_cookie(key="access_token_cookie", value=access_token , httponly=False)
@router.delete('/logout') result = {
"user": user,
"tokens": {"access_token": access_token, "refresh_token": refresh_token},
}
return result
@router.delete("/logout")
def logout(Authorize: AuthJWT = Depends()): def logout(Authorize: AuthJWT = Depends()):
""" """
Because the JWT are stored in an httponly cookie now, we cannot Because the JWT are stored in an httponly cookie now, we cannot
@ -47,4 +61,4 @@ def logout(Authorize: AuthJWT = Depends()):
Authorize.jwt_required() Authorize.jwt_required()
Authorize.unset_jwt_cookies() Authorize.unset_jwt_cookies()
return {"msg":"Successfully logout"} return {"msg": "Successfully logout"}

View file

@ -10,26 +10,26 @@ from fastapi_jwt_auth import AuthJWT
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/auth/login") oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/auth/login")
#### JWT Auth #################################################### #### JWT Auth ####################################################
class Settings(BaseModel): class Settings(BaseModel):
authjwt_secret_key: str = "secret" authjwt_secret_key: str = "secret"
authjwt_token_location = {"cookies"} authjwt_token_location = {"cookies", "headers"}
authjwt_cookie_csrf_protect = False authjwt_cookie_csrf_protect = False
authjwt_access_token_expires = False # (pre-alpha only) # TODO: set to 1 hour authjwt_access_token_expires = False # (pre-alpha only) # TODO: set to 1 hour
authjwt_cookie_samesite = "none" authjwt_cookie_samesite = "lax"
authjwt_cookie_secure = True authjwt_cookie_secure = True
authjwt_cookie_domain = ".localhost"
@AuthJWT.load_config # type: ignore
@AuthJWT.load_config # type: ignore
def get_config(): def get_config():
return Settings() return Settings()
#### JWT Auth #################################################### #### JWT Auth ####################################################
#### Classes #################################################### #### Classes ####################################################
@ -41,10 +41,11 @@ class Token(BaseModel):
class TokenData(BaseModel): class TokenData(BaseModel):
username: str | None = None username: str | None = None
#### Classes #################################################### #### Classes ####################################################
async def authenticate_user(request: Request,email: str, password: str): async def authenticate_user(request: Request, email: str, password: str):
user = await security_get_user(request, email) user = await security_get_user(request, email)
if not user: if not user:
return False return False
@ -64,29 +65,28 @@ def create_access_token(data: dict, expires_delta: timedelta | None = None):
return encoded_jwt return encoded_jwt
async def get_current_user(request: Request, Authorize: AuthJWT = Depends()): async def get_current_user(request: Request, Authorize: AuthJWT = Depends()):
credentials_exception = HTTPException( credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials", detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"}, headers={"WWW-Authenticate": "Bearer"},
) )
try: try:
Authorize.jwt_optional() Authorize.jwt_optional()
username = Authorize.get_jwt_subject() or None username = Authorize.get_jwt_subject() or None
token_data = TokenData(username=username) # type: ignore token_data = TokenData(username=username) # type: ignore
except JWTError: except JWTError:
raise credentials_exception raise credentials_exception
if username: if username:
user = await security_get_user(request, email=token_data.username) # type: ignore # treated as an email user = await security_get_user(request, email=token_data.username) # type: ignore # treated as an email
if user is None: if user is None:
raise credentials_exception raise credentials_exception
return PublicUser(**user.dict()) return PublicUser(**user.dict())
else: else:
return AnonymousUser() return AnonymousUser()
async def non_public_endpoint(current_user: PublicUser ):
async def non_public_endpoint(current_user: PublicUser):
if isinstance(current_user, AnonymousUser): if isinstance(current_user, AnonymousUser):
raise HTTPException(status_code=401, detail="Not authenticated") raise HTTPException(status_code=401, detail="Not authenticated")