diff --git a/front/app/editor/course/[courseid]/activity/[activityid]/edit/page.tsx b/front/app/editor/course/[courseid]/activity/[activityid]/edit/page.tsx index 37c19000..1d20368d 100644 --- a/front/app/editor/course/[courseid]/activity/[activityid]/edit/page.tsx +++ b/front/app/editor/course/[courseid]/activity/[activityid]/edit/page.tsx @@ -9,6 +9,7 @@ import { getCourseMetadataWithAuthHeader } from "@services/courses/courses"; import { cookies } from "next/headers"; import { Metadata } from "next"; import { getActivityWithAuthHeader } from "@services/courses/activities"; +import { getAccessTokenFromRefreshTokenCookie, getNewAccessTokenUsingRefreshTokenServer } from "@services/auth/auth"; type MetadataProps = { params: { orgslug: string, courseid: string, activityid: string }; @@ -19,10 +20,9 @@ export async function generateMetadata( { params }: MetadataProps, ): Promise { const cookieStore = cookies(); - const access_token_cookie: any = cookieStore.get('access_token_cookie'); - + const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore) // Get Org context information - const course_meta = await getCourseMetadataWithAuthHeader(params.courseid, { revalidate: 0, tags: ['courses'] }, access_token_cookie ? access_token_cookie.value : null) + const course_meta = await getCourseMetadataWithAuthHeader(params.courseid, { revalidate: 0, tags: ['courses'] }, access_token ? access_token : null) return { title: `Edit - ${course_meta.course.name} Activity`, @@ -32,13 +32,13 @@ export async function generateMetadata( const EditActivity = async (params: any) => { const cookieStore = cookies(); - const access_token_cookie: any = cookieStore.get('access_token_cookie'); + const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore) const activityid = params.params.activityid; const courseid = params.params.courseid; const orgslug = params.params.orgslug; - const courseInfo = await getCourseMetadataWithAuthHeader(courseid, { revalidate: 0, tags: ['courses'] }, access_token_cookie ? access_token_cookie.value : null) - const activity = await getActivityWithAuthHeader(activityid, { revalidate: 0, tags: ['activities'] }, access_token_cookie ? access_token_cookie.value : null) + const courseInfo = await getCourseMetadataWithAuthHeader(courseid, { revalidate: 0, tags: ['courses'] }, access_token ? access_token : null) + const activity = await getActivityWithAuthHeader(activityid, { revalidate: 0, tags: ['activities'] }, access_token ? access_token : null) return ( diff --git a/front/app/orgs/[orgslug]/(withmenu)/collection/[collectionid]/page.tsx b/front/app/orgs/[orgslug]/(withmenu)/collection/[collectionid]/page.tsx index f0d77317..51efcfd5 100644 --- a/front/app/orgs/[orgslug]/(withmenu)/collection/[collectionid]/page.tsx +++ b/front/app/orgs/[orgslug]/(withmenu)/collection/[collectionid]/page.tsx @@ -1,4 +1,5 @@ import GeneralWrapperStyled from "@components/StyledElements/Wrappers/GeneralWrapper"; +import { getAccessTokenFromRefreshTokenCookie, getNewAccessTokenUsingRefreshTokenServer } from "@services/auth/auth"; import { getBackendUrl, getUriWithOrg } from "@services/config/config"; import { getCollectionByIdWithAuthHeader } from "@services/courses/collections"; import { getCourseThumbnailMediaDirectory } from "@services/media/media"; @@ -16,12 +17,13 @@ export async function generateMetadata( { params }: MetadataProps, ): Promise { const cookieStore = cookies(); - const access_token_cookie: any = cookieStore.get('access_token_cookie'); + const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore) + // Get Org context information const org = await getOrganizationContextInfo(params.orgslug, { revalidate: 1800, tags: ['organizations'] }); - const col = await getCollectionByIdWithAuthHeader(params.collectionid, access_token_cookie ? access_token_cookie.value : null, { revalidate: 0, tags: ['collections'] }); + const col = await getCollectionByIdWithAuthHeader(params.collectionid, access_token ? access_token : null, { revalidate: 0, tags: ['collections'] }); + - return { title: `Collection : ${col.name} — ${org.name}`, @@ -31,9 +33,9 @@ export async function generateMetadata( const CollectionPage = async (params: any) => { const cookieStore = cookies(); - const access_token_cookie: any = cookieStore.get('access_token_cookie'); + const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore) const orgslug = params.params.orgslug; - const col = await getCollectionByIdWithAuthHeader(params.params.collectionid, access_token_cookie ? access_token_cookie.value : null, { revalidate: 0, tags: ['collections'] }); + const col = await getCollectionByIdWithAuthHeader(params.params.collectionid, access_token ? access_token : null, { revalidate: 0, tags: ['collections'] }); const removeCoursePrefix = (courseid: string) => { return courseid.replace("course_", "") diff --git a/front/app/orgs/[orgslug]/(withmenu)/collections/page.tsx b/front/app/orgs/[orgslug]/(withmenu)/collections/page.tsx index 37d505dd..0e6310e2 100644 --- a/front/app/orgs/[orgslug]/(withmenu)/collections/page.tsx +++ b/front/app/orgs/[orgslug]/(withmenu)/collections/page.tsx @@ -9,6 +9,7 @@ import { cookies } from "next/headers"; import Link from "next/link"; import CollectionAdminEditsArea from "./admin"; import { getCourseThumbnailMediaDirectory } from "@services/media/media"; +import { getAccessTokenFromRefreshTokenCookie, getNewAccessTokenUsingRefreshTokenServer } from "@services/auth/auth"; type MetadataProps = { params: { orgslug: string, courseid: string }; @@ -19,7 +20,6 @@ export async function generateMetadata( { params }: MetadataProps, ): Promise { const cookieStore = cookies(); - const access_token_cookie: any = cookieStore.get('access_token_cookie'); // Get Org context information const org = await getOrganizationContextInfo(params.orgslug, { revalidate: 1800, tags: ['organizations'] }); return { @@ -35,11 +35,11 @@ const removeCollectionPrefix = (collectionid: string) => { const CollectionsPage = async (params: any) => { const cookieStore = cookies(); - const access_token_cookie: any = cookieStore.get('access_token_cookie'); + const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore) const orgslug = params.params.orgslug; const org = await getOrganizationContextInfo(orgslug, { revalidate: 1800, tags: ['organizations'] }); const org_id = org.org_id; - const collections = await getOrgCollectionsWithAuthHeader(org_id, access_token_cookie ? access_token_cookie.value : null, { revalidate: 0, tags: ['collections'] }); + const collections = await getOrgCollectionsWithAuthHeader(org_id, access_token ? access_token : null, { revalidate: 0, tags: ['collections'] }); return ( diff --git a/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/activity/[activityid]/page.tsx b/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/activity/[activityid]/page.tsx index 140a0f1a..0f642018 100644 --- a/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/activity/[activityid]/page.tsx +++ b/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/activity/[activityid]/page.tsx @@ -4,6 +4,7 @@ import { cookies } from "next/headers"; import ActivityClient from "./activity"; import { getOrganizationContextInfo } from "@services/organizations/orgs"; import { Metadata } from "next"; +import { getAccessTokenFromRefreshTokenCookie, getNewAccessTokenUsingRefreshTokenServer } from "@services/auth/auth"; type MetadataProps = { @@ -15,12 +16,12 @@ export async function generateMetadata( { params }: MetadataProps, ): Promise { const cookieStore = cookies(); - const access_token_cookie: any = cookieStore.get('access_token_cookie'); + const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore) // Get Org context information const org = await getOrganizationContextInfo(params.orgslug, { revalidate: 1800, tags: ['organizations'] }); - const course_meta = await getCourseMetadataWithAuthHeader(params.courseid, { revalidate: 0, tags: ['courses'] }, access_token_cookie ? access_token_cookie.value : null ) - const activity = await getActivityWithAuthHeader(params.activityid, { revalidate: 0, tags: ['activities'] }, access_token_cookie ? access_token_cookie.value : null) + const course_meta = await getCourseMetadataWithAuthHeader(params.courseid, { revalidate: 0, tags: ['courses'] }, access_token ? access_token : null) + const activity = await getActivityWithAuthHeader(params.activityid, { revalidate: 0, tags: ['activities'] }, access_token ? access_token : null) return { title: activity.name + ` — ${course_meta.course.name} Course`, @@ -30,13 +31,13 @@ export async function generateMetadata( const ActivityPage = async (params: any) => { const cookieStore = cookies(); - const access_token_cookie: any = cookieStore.get('access_token_cookie'); + const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore) const activityid = params.params.activityid; const courseid = params.params.courseid; const orgslug = params.params.orgslug; - const course_meta = await getCourseMetadataWithAuthHeader(courseid, { revalidate: 0, tags: ['courses'] }, access_token_cookie ? access_token_cookie.value : null) - const activity = await getActivityWithAuthHeader(activityid, { revalidate: 0, tags: ['activities'] }, access_token_cookie ? access_token_cookie.value : null) + const course_meta = await getCourseMetadataWithAuthHeader(courseid, { revalidate: 0, tags: ['courses'] }, access_token ? access_token : null) + const activity = await getActivityWithAuthHeader(activityid, { revalidate: 0, tags: ['activities'] }, access_token ? access_token : null) return ( <> { const cookieStore = cookies(); - const access_token_cookie: any = cookieStore.get('access_token_cookie'); + const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore) // Get Org context information const org = await getOrganizationContextInfo(params.orgslug, { revalidate: 1800, tags: ['organizations'] }); - const course_meta = await getCourseMetadataWithAuthHeader(params.courseid, { revalidate: 0, tags: ['courses'] }, access_token_cookie ? access_token_cookie.value : null) + const course_meta = await getCourseMetadataWithAuthHeader(params.courseid, { revalidate: 0, tags: ['courses'] }, access_token ? access_token : null) return { title: `Edit Course - ` + course_meta.course.name, @@ -31,7 +32,7 @@ function CourseEdit(params: any) { let subpage = params.params.subpage ? params.params.subpage : 'general'; return ( <> - + ); } diff --git a/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/page.tsx b/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/page.tsx index 2e07285e..f224e72e 100644 --- a/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/page.tsx +++ b/front/app/orgs/[orgslug]/(withmenu)/course/[courseid]/page.tsx @@ -4,6 +4,7 @@ import { cookies } from 'next/headers'; import { getCourseMetadataWithAuthHeader } from '@services/courses/courses'; import { getOrganizationContextInfo } from '@services/organizations/orgs'; import { Metadata } from 'next'; +import { getAccessTokenFromRefreshTokenCookie, getNewAccessTokenUsingRefreshTokenServer } from '@services/auth/auth'; type MetadataProps = { params: { orgslug: string, courseid: string }; @@ -14,12 +15,12 @@ export async function generateMetadata( { params }: MetadataProps, ): Promise { const cookieStore = cookies(); - const access_token_cookie: any = cookieStore.get('access_token_cookie'); - + const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore) + // Get Org context information const org = await getOrganizationContextInfo(params.orgslug, { revalidate: 1800, tags: ['organizations'] }); - const course_meta = await getCourseMetadataWithAuthHeader(params.courseid, { revalidate: 0, tags: ['courses'] }, access_token_cookie ? access_token_cookie.value : null) + const course_meta = await getCourseMetadataWithAuthHeader(params.courseid, { revalidate: 0, tags: ['courses'] }, access_token ? access_token : null) return { title: course_meta.course.name + ` — ${org.name}`, @@ -31,11 +32,11 @@ export async function generateMetadata( const CoursePage = async (params: any) => { const cookieStore = cookies(); - const access_token_cookie: any = cookieStore.get('access_token_cookie'); const courseid = params.params.courseid const orgslug = params.params.orgslug; - const course_meta = await getCourseMetadataWithAuthHeader(courseid, { revalidate: 0, tags: ['courses'] }, access_token_cookie ? access_token_cookie.value : null) - + const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore) + const course_meta = await getCourseMetadataWithAuthHeader(courseid, { revalidate: 0, tags: ['courses'] }, access_token ? access_token : null) + return (
diff --git a/front/app/orgs/[orgslug]/(withmenu)/courses/page.tsx b/front/app/orgs/[orgslug]/(withmenu)/courses/page.tsx index 8b4e5371..e0ff84f9 100644 --- a/front/app/orgs/[orgslug]/(withmenu)/courses/page.tsx +++ b/front/app/orgs/[orgslug]/(withmenu)/courses/page.tsx @@ -5,6 +5,7 @@ import { getOrgCoursesWithAuthHeader } from "@services/courses/courses"; import { Metadata } from "next"; import { getOrganizationContextInfo } from "@services/organizations/orgs"; import { cookies } from "next/headers"; +import { getAccessTokenFromRefreshTokenCookie, getNewAccessTokenUsingRefreshTokenServer } from "@services/auth/auth"; type MetadataProps = { params: { orgslug: string }; @@ -27,8 +28,8 @@ const CoursesPage = async (params: any) => { const orgslug = params.params.orgslug; const org = await getOrganizationContextInfo(orgslug, { revalidate: 1800, tags: ['organizations'] }); const cookieStore = cookies(); - const access_token_cookie: any = cookieStore.get('access_token_cookie'); - const courses = await getOrgCoursesWithAuthHeader(orgslug, { revalidate: 0, tags: ['courses'] }, access_token_cookie ? access_token_cookie.value : null); + const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore) + const courses = await getOrgCoursesWithAuthHeader(orgslug, { revalidate: 0, tags: ['courses'] }, access_token ? access_token : null); return (
diff --git a/front/app/orgs/[orgslug]/(withmenu)/page.tsx b/front/app/orgs/[orgslug]/(withmenu)/page.tsx index 45af8588..63a5fe65 100644 --- a/front/app/orgs/[orgslug]/(withmenu)/page.tsx +++ b/front/app/orgs/[orgslug]/(withmenu)/page.tsx @@ -12,6 +12,7 @@ import { cookies } from 'next/headers'; import GeneralWrapperStyled from '@components/StyledElements/Wrappers/GeneralWrapper'; import TypeOfContentTitle from '@components/StyledElements/Titles/TypeOfContentTitle'; import { getCourseThumbnailMediaDirectory } from '@services/media/media'; +import { getAccessTokenFromRefreshTokenCookie, getNewAccessTokenUsingRefreshTokenServer } from '@services/auth/auth'; type MetadataProps = { params: { orgslug: string }; @@ -34,11 +35,11 @@ export async function generateMetadata( const OrgHomePage = async (params: any) => { const orgslug = params.params.orgslug; const cookieStore = cookies(); - const access_token_cookie: any = cookieStore.get('access_token_cookie'); - const courses = await getOrgCoursesWithAuthHeader(orgslug, { revalidate: 0, tags: ['courses'] }, access_token_cookie ? access_token_cookie.value : null); + const access_token = await getAccessTokenFromRefreshTokenCookie(cookieStore) + const courses = await getOrgCoursesWithAuthHeader(orgslug, { revalidate: 0, tags: ['courses'] }, access_token ? access_token : null); const org = await getOrganizationContextInfo(orgslug, { revalidate: 1800, tags: ['organizations'] }); - const collections = await getOrgCollectionsWithAuthHeader(org.org_id, access_token_cookie ? access_token_cookie.value : null, { revalidate: 0, tags: ['courses'] }); + const collections = await getOrgCollectionsWithAuthHeader(org.org_id, access_token ? access_token : null, { revalidate: 0, tags: ['courses'] }); // function to remove "course_" from the course_id diff --git a/front/components/Objects/Menu/ProfileArea.tsx b/front/components/Objects/Menu/ProfileArea.tsx index e912f092..5440ea42 100644 --- a/front/components/Objects/Menu/ProfileArea.tsx +++ b/front/components/Objects/Menu/ProfileArea.tsx @@ -4,9 +4,10 @@ import styled from "styled-components"; import Link from "next/link"; import Avvvatars from "avvvatars-react"; import { GearIcon } from "@radix-ui/react-icons"; -import { getRefreshToken, getUserInfo } from "@services/auth/auth"; +import { getNewAccessTokenUsingRefreshToken, getUserInfo } from "@services/auth/auth"; import { usePathname } from "next/navigation"; import { useRouter } from "next/router"; +import path from "path"; export interface Auth { access_token: string; @@ -27,12 +28,16 @@ function ProfileArea() { const [auth, setAuth] = React.useState({ access_token: "", isAuthenticated: false, userInfo: {}, isLoading: true }); async function checkRefreshToken() { - let data = await getRefreshToken(); + let data = await getNewAccessTokenUsingRefreshToken(); if (data) { return data.access_token; } } + React.useEffect(() => { + checkAuth(); + console.log("pathname", pathname); + }, [pathname]); async function checkAuth() { try { diff --git a/front/components/Security/AuthProvider.tsx b/front/components/Security/AuthProvider.tsx index c8bcee55..1322c296 100644 --- a/front/components/Security/AuthProvider.tsx +++ b/front/components/Security/AuthProvider.tsx @@ -1,6 +1,6 @@ "use client"; import React, { useEffect } from "react"; -import { getRefreshToken, getUserInfo } from "../../services/auth/auth"; +import { getNewAccessTokenUsingRefreshToken, getUserInfo } from "../../services/auth/auth"; import { useRouter, usePathname } from "next/navigation"; export const AuthContext: any = React.createContext({}); @@ -21,8 +21,15 @@ const AuthProvider = ({ children }: any) => { const [auth, setAuth] = React.useState({ access_token: "", isAuthenticated: false, userInfo: {}, isLoading: true }); + function deleteCookie(cookieName: string) { + console.log("Deleting cookie: " + cookieName); + document.cookie = cookieName + "=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"; + } + + async function checkRefreshToken() { - let data = await getRefreshToken(); + deleteCookie("access_token_cookie"); + let data = await getNewAccessTokenUsingRefreshToken(); if (data) { return data.access_token; } @@ -61,13 +68,13 @@ const AuthProvider = ({ children }: any) => { } useEffect(() => { - if (auth.isLoading) { - checkAuth(); - } + checkRefreshToken(); + checkAuth(); + console.log("pathname", pathname); return () => { auth.isLoading = false; }; - }, []); + }, [pathname]); return {children}; }; diff --git a/front/services/auth/auth.ts b/front/services/auth/auth.ts index d09a6746..b07027be 100644 --- a/front/services/auth/auth.ts +++ b/front/services/auth/auth.ts @@ -1,4 +1,5 @@ import { getAPIUrl } from "@services/config/config"; +import { NextApiRequestCookies } from "next/dist/server/api-utils"; interface LoginAndGetTokenResponse { access_token: "string"; @@ -44,7 +45,7 @@ export async function getUserInfo(token: string): Promise { .catch((error) => console.log("error", error)); } -export async function getRefreshToken(): Promise { +export async function getNewAccessTokenUsingRefreshToken(): Promise { const requestOptions: any = { method: "POST", redirect: "follow", @@ -56,6 +57,28 @@ export async function getRefreshToken(): Promise { .catch((error) => console.log("error", error)); } +export async function getNewAccessTokenUsingRefreshTokenServer(refresh_token_cookie: any): Promise { + const requestOptions: any = { + method: "POST", + redirect: "follow", + headers: { + Cookie: `refresh_token_cookie=${refresh_token_cookie}`, + }, + credentials: "include", + }; + return fetch(`${getAPIUrl()}auth/refresh`, requestOptions) + .then((result) => result.json()) + .catch((error) => console.log("error", error)); +} + +// cookies + +export async function getAccessTokenFromRefreshTokenCookie(cookieStore: any) { + const refresh_token_cookie: any = cookieStore.get("refresh_token_cookie"); + const access_token_cookie: any = await getNewAccessTokenUsingRefreshTokenServer(refresh_token_cookie?.value); + return access_token_cookie && refresh_token_cookie ? access_token_cookie.access_token : null; +} + // signup interface NewAccountBody { diff --git a/front/services/config/config.ts b/front/services/config/config.ts index 4fca83ce..caab8422 100644 --- a/front/services/config/config.ts +++ b/front/services/config/config.ts @@ -1,6 +1,6 @@ export const LEARNHOUSE_HTTP_PROTOCOL = process.env.NEXT_PUBLIC_LEARNHOUSE_HTTPS === "true" ? "https://" : "http://"; const LEARNHOUSE_API_URL = `${process.env.NEXT_PUBLIC_LEARNHOUSE_API_URL}`; -const LEARNHOUSE_BACKEND_URL = `${process.env.NEXT_PUBLIC_LEARNHOUSE_BACKEND_URL}`; +export const LEARNHOUSE_BACKEND_URL = `${process.env.NEXT_PUBLIC_LEARNHOUSE_BACKEND_URL}`; export const LEARNHOUSE_DOMAIN = process.env.NEXT_PUBLIC_LEARNHOUSE_DOMAIN; export const getAPIUrl = () => LEARNHOUSE_API_URL; diff --git a/src/routers/auth.py b/src/routers/auth.py index 2cc82973..54e4dc7a 100644 --- a/src/routers/auth.py +++ b/src/routers/auth.py @@ -9,7 +9,7 @@ router = APIRouter() @router.post("/refresh") -def refresh(Authorize: AuthJWT = Depends()): +def refresh(response: Response,Authorize: AuthJWT = Depends()): """ The jwt_refresh_token_required() function insures a valid refresh token is present in the request before running any code below that function. @@ -20,6 +20,8 @@ def refresh(Authorize: AuthJWT = Depends()): current_user = Authorize.get_jwt_subject() new_access_token = Authorize.create_access_token(subject=current_user) # type: ignore + + response.set_cookie(key="access_token_cookie", value=new_access_token, httponly=False, domain=get_learnhouse_config().hosting_config.cookie_config.domain) return {"access_token": new_access_token}