chore(frontend): Documentation & refactoring
Adding documentation to functions & some further refactoring.
This commit is contained in:
		
							parent
							
								
									26d5c09bb4
								
							
						
					
					
						commit
						d64b4505c8
					
				
					 4 changed files with 73 additions and 30 deletions
				
			
		|  | @ -1,24 +0,0 @@ | |||
| import apiClient from "@/services/api-client.ts"; | ||||
| import type {AuthState} from "@/services/auth/auth-types.ts"; | ||||
| 
 | ||||
| export function configureApiClientAuthInterceptors(authState: AuthState, renewToken: () => Promise<any>) { | ||||
|     apiClient.interceptors.request.use(async (reqConfig) => { | ||||
|         const token = authState?.user?.access_token; | ||||
|         if (token) { | ||||
|             reqConfig.headers.Authorization = `Bearer ${token}`; | ||||
|         } | ||||
|         return reqConfig; | ||||
|     }, (error) => Promise.reject(error)); | ||||
| 
 | ||||
|     apiClient.interceptors.response.use( | ||||
|         response => response, | ||||
|         async (error) => { | ||||
|             if (error.response?.status === 401) { | ||||
|                 console.log("Access token expired, trying to refresh..."); | ||||
|                 await renewToken(); | ||||
|                 return apiClient(error.config); // Retry the request
 | ||||
|             } | ||||
|             return Promise.reject(error); | ||||
|         } | ||||
|     ); | ||||
| } | ||||
|  | @ -1,6 +1,9 @@ | |||
| import apiClient from "@/services/api-client.ts"; | ||||
| import type {FrontendAuthConfig} from "@/services/auth/auth-types.ts"; | ||||
| 
 | ||||
| /** | ||||
|  * Fetch the authentication configuration from the backend. | ||||
|  */ | ||||
| export async function loadAuthConfig() { | ||||
|     const authConfig = (await apiClient.get<FrontendAuthConfig>("auth/config")).data; | ||||
|     return { | ||||
|  |  | |||
|  | @ -6,16 +6,22 @@ import {computed, reactive} from "vue"; | |||
| import type {AuthState, Role, UserManagersForRoles} from "@/services/auth/auth-types.ts"; | ||||
| import {User, UserManager} from "oidc-client-ts"; | ||||
| import {loadAuthConfig} from "@/services/auth/auth-config-loader.ts"; | ||||
| import {configureApiClientAuthInterceptors} from "@/services/auth/auth-api-client-interceptors.ts"; | ||||
| import authStorage from "./auth-storage.ts" | ||||
| import {useRouter} from "vue-router"; | ||||
| import {loginRoute} from "@/config.ts"; | ||||
| import apiClient from "@/services/api-client.ts"; | ||||
| 
 | ||||
| const authConfig = await loadAuthConfig(); | ||||
| const router = useRouter(); | ||||
| 
 | ||||
| const userManagers: UserManagersForRoles = { | ||||
|     student: new UserManager(authConfig.student), | ||||
|     teacher: new UserManager(authConfig.teacher), | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * Load the information about who is currently logged in from the IDP. | ||||
|  */ | ||||
| async function loadUser(): Promise<User | null> { | ||||
|     const activeRole = authStorage.getActiveRole(); | ||||
|     if (!activeRole) { | ||||
|  | @ -28,6 +34,9 @@ async function loadUser(): Promise<User | null> { | |||
|     return user; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Information about the current authentication state. | ||||
|  */ | ||||
| const authState = reactive<AuthState>({ | ||||
|     user: null, | ||||
|     accessToken: null, | ||||
|  | @ -36,34 +45,56 @@ const authState = reactive<AuthState>({ | |||
| 
 | ||||
| const isLoggedIn = computed(() => authState.user !== null); | ||||
| 
 | ||||
| /** | ||||
|  * Redirect the user to the login page where he/she can choose whether to log in as a student or teacher. | ||||
|  */ | ||||
| async function initiateLogin() { | ||||
|     await router.push(loginRoute); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Redirect the user to the IDP for the given role so that he can log in there. | ||||
|  * Only call this function when the user is not logged in yet! | ||||
|  */ | ||||
| async function loginAs(role: Role): Promise<void> { | ||||
|     // Storing it in local storage so that it won't be lost when redirecting outside of the app.
 | ||||
|     authStorage.setActiveRole(role); | ||||
|     await userManagers[role].signinRedirect(); | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * To be called when the user is redirected to the callback-endpoint by the IDP after a successful login. | ||||
|  */ | ||||
| async function handleLoginCallback(): Promise<void> { | ||||
|     const activeRole = authStorage.getActiveRole(); | ||||
|     if (!activeRole) { | ||||
|         throw new Error("Can't renew the token: Not logged in!"); | ||||
|         throw new Error("Login callback received, but the user is not logging in!"); | ||||
|     } | ||||
|     authState.user = await userManagers[activeRole].signinCallback() || null; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Refresh an expired authorization token. | ||||
|  */ | ||||
| async function renewToken() { | ||||
|     const activeRole = authStorage.getActiveRole(); | ||||
|     if (!activeRole) { | ||||
|         throw new Error("Can't renew the token: Not logged in!"); | ||||
|         console.log("Can't renew the token: Not logged in!"); | ||||
|         await initiateLogin(); | ||||
|         return; | ||||
|     } | ||||
|     try { | ||||
|         return await userManagers[activeRole].signinSilent(); | ||||
|     } catch (error) { | ||||
|         console.log("Can't renew the token:"); | ||||
|         console.log(error); | ||||
|         await loginAs(activeRole); | ||||
|         await initiateLogin(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * End the session of the current user. | ||||
|  */ | ||||
| async function logout(): Promise<void> { | ||||
|     const activeRole = authStorage.getActiveRole(); | ||||
|     if (activeRole) { | ||||
|  | @ -72,6 +103,26 @@ async function logout(): Promise<void> { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| configureApiClientAuthInterceptors(authState, renewToken); | ||||
| // Registering interceptor to add the authorization header to each request when the user is logged in.
 | ||||
| apiClient.interceptors.request.use(async (reqConfig) => { | ||||
|     const token = authState?.user?.access_token; | ||||
|     if (token) { | ||||
|         reqConfig.headers.Authorization = `Bearer ${token}`; | ||||
|     } | ||||
|     return reqConfig; | ||||
| }, (error) => Promise.reject(error)); | ||||
| 
 | ||||
| export default {authState, isLoggedIn, loadUser, handleLoginCallback, loginAs, logout}; | ||||
| // Registering interceptor to refresh the token when a request failed because it was expired.
 | ||||
| apiClient.interceptors.response.use( | ||||
|     response => response, | ||||
|     async (error) => { | ||||
|         if (error.response?.status === 401) { | ||||
|             console.log("Access token expired, trying to refresh..."); | ||||
|             await renewToken(); | ||||
|             return apiClient(error.config); // Retry the request
 | ||||
|         } | ||||
|         return Promise.reject(error); | ||||
|     } | ||||
| ); | ||||
| 
 | ||||
| export default {authState, isLoggedIn, initiateLogin, loadUser, handleLoginCallback, loginAs, logout}; | ||||
|  |  | |||
|  | @ -1,12 +1,25 @@ | |||
| import type {Role} from "@/services/auth/auth-types.ts"; | ||||
| 
 | ||||
| export default { | ||||
|     /** | ||||
|      * Get the role the user is currently logged in as from the local persistent storage. | ||||
|      */ | ||||
|     getActiveRole(): Role | undefined { | ||||
|         return localStorage.getItem("activeRole") as Role | undefined; | ||||
|     }, | ||||
| 
 | ||||
|     /** | ||||
|      * Set the role the user is currently logged in as from the local persistent storage. | ||||
|      * This should happen when the user logs in with another account. | ||||
|      */ | ||||
|     setActiveRole(role: Role) { | ||||
|         localStorage.setItem("activeRole", role); | ||||
|     }, | ||||
| 
 | ||||
|     /** | ||||
|      * Remove the saved current role from the local persistent storage. | ||||
|      * This should happen when the user is logged out. | ||||
|      */ | ||||
|     deleteActiveRole() { | ||||
|         localStorage.removeItem("activeRole"); | ||||
|     } | ||||
|  |  | |||
		Reference in a new issue
	
	 Gerald Schmittinger
						Gerald Schmittinger