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 apiClient from "@/services/api-client.ts"; | ||||||
| import type {FrontendAuthConfig} from "@/services/auth/auth-types.ts"; | import type {FrontendAuthConfig} from "@/services/auth/auth-types.ts"; | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * Fetch the authentication configuration from the backend. | ||||||
|  |  */ | ||||||
| export async function loadAuthConfig() { | export async function loadAuthConfig() { | ||||||
|     const authConfig = (await apiClient.get<FrontendAuthConfig>("auth/config")).data; |     const authConfig = (await apiClient.get<FrontendAuthConfig>("auth/config")).data; | ||||||
|     return { |     return { | ||||||
|  |  | ||||||
|  | @ -6,16 +6,22 @@ import {computed, reactive} from "vue"; | ||||||
| import type {AuthState, Role, UserManagersForRoles} from "@/services/auth/auth-types.ts"; | import type {AuthState, Role, UserManagersForRoles} from "@/services/auth/auth-types.ts"; | ||||||
| import {User, UserManager} from "oidc-client-ts"; | import {User, UserManager} from "oidc-client-ts"; | ||||||
| import {loadAuthConfig} from "@/services/auth/auth-config-loader.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 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 authConfig = await loadAuthConfig(); | ||||||
|  | const router = useRouter(); | ||||||
| 
 | 
 | ||||||
| const userManagers: UserManagersForRoles = { | const userManagers: UserManagersForRoles = { | ||||||
|     student: new UserManager(authConfig.student), |     student: new UserManager(authConfig.student), | ||||||
|     teacher: new UserManager(authConfig.teacher), |     teacher: new UserManager(authConfig.teacher), | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * Load the information about who is currently logged in from the IDP. | ||||||
|  |  */ | ||||||
| async function loadUser(): Promise<User | null> { | async function loadUser(): Promise<User | null> { | ||||||
|     const activeRole = authStorage.getActiveRole(); |     const activeRole = authStorage.getActiveRole(); | ||||||
|     if (!activeRole) { |     if (!activeRole) { | ||||||
|  | @ -28,6 +34,9 @@ async function loadUser(): Promise<User | null> { | ||||||
|     return user; |     return user; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * Information about the current authentication state. | ||||||
|  |  */ | ||||||
| const authState = reactive<AuthState>({ | const authState = reactive<AuthState>({ | ||||||
|     user: null, |     user: null, | ||||||
|     accessToken: null, |     accessToken: null, | ||||||
|  | @ -36,34 +45,56 @@ const authState = reactive<AuthState>({ | ||||||
| 
 | 
 | ||||||
| const isLoggedIn = computed(() => authState.user !== null); | 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> { | 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.
 |     // Storing it in local storage so that it won't be lost when redirecting outside of the app.
 | ||||||
|     authStorage.setActiveRole(role); |     authStorage.setActiveRole(role); | ||||||
|     await userManagers[role].signinRedirect(); |     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> { | async function handleLoginCallback(): Promise<void> { | ||||||
|     const activeRole = authStorage.getActiveRole(); |     const activeRole = authStorage.getActiveRole(); | ||||||
|     if (!activeRole) { |     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; |     authState.user = await userManagers[activeRole].signinCallback() || null; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * Refresh an expired authorization token. | ||||||
|  |  */ | ||||||
| async function renewToken() { | async function renewToken() { | ||||||
|     const activeRole = authStorage.getActiveRole(); |     const activeRole = authStorage.getActiveRole(); | ||||||
|     if (!activeRole) { |     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 { |     try { | ||||||
|         return await userManagers[activeRole].signinSilent(); |         return await userManagers[activeRole].signinSilent(); | ||||||
|     } catch (error) { |     } catch (error) { | ||||||
|         console.log("Can't renew the token:"); |         console.log("Can't renew the token:"); | ||||||
|         console.log(error); |         console.log(error); | ||||||
|         await loginAs(activeRole); |         await initiateLogin(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /** | ||||||
|  |  * End the session of the current user. | ||||||
|  |  */ | ||||||
| async function logout(): Promise<void> { | async function logout(): Promise<void> { | ||||||
|     const activeRole = authStorage.getActiveRole(); |     const activeRole = authStorage.getActiveRole(); | ||||||
|     if (activeRole) { |     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"; | import type {Role} from "@/services/auth/auth-types.ts"; | ||||||
| 
 | 
 | ||||||
| export default { | export default { | ||||||
|  |     /** | ||||||
|  |      * Get the role the user is currently logged in as from the local persistent storage. | ||||||
|  |      */ | ||||||
|     getActiveRole(): Role | undefined { |     getActiveRole(): Role | undefined { | ||||||
|         return localStorage.getItem("activeRole") as 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) { |     setActiveRole(role: Role) { | ||||||
|         localStorage.setItem("activeRole", 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() { |     deleteActiveRole() { | ||||||
|         localStorage.removeItem("activeRole"); |         localStorage.removeItem("activeRole"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
		Reference in a new issue
	
	 Gerald Schmittinger
						Gerald Schmittinger