style: fix linting issues met Prettier
This commit is contained in:
		
							parent
							
								
									b12c743440
								
							
						
					
					
						commit
						57cd8466fe
					
				
					 23 changed files with 4817 additions and 4248 deletions
				
			
		|  | @ -12,8 +12,8 @@ import submissionRouter from './routes/submission.js'; | ||||||
| import classRouter from './routes/class.js'; | import classRouter from './routes/class.js'; | ||||||
| import questionRouter from './routes/question.js'; | import questionRouter from './routes/question.js'; | ||||||
| import authRouter from './routes/auth.js'; | import authRouter from './routes/auth.js'; | ||||||
| import {authenticateUser} from "./middleware/auth/auth.js"; | import { authenticateUser } from './middleware/auth/auth.js'; | ||||||
| import cors from "./middleware/cors.js"; | import cors from './middleware/cors.js'; | ||||||
| import { getLogger, Logger } from './logging/initalize.js'; | import { getLogger, Logger } from './logging/initalize.js'; | ||||||
| import { responseTimeLogger } from './logging/responseTimeLogger.js'; | import { responseTimeLogger } from './logging/responseTimeLogger.js'; | ||||||
| import responseTime from 'response-time'; | import responseTime from 'response-time'; | ||||||
|  |  | ||||||
|  | @ -1,19 +1,19 @@ | ||||||
| import {EnvVars, getEnvVar} from "../util/envvars.js"; | import { EnvVars, getEnvVar } from '../util/envvars.js'; | ||||||
| 
 | 
 | ||||||
| type FrontendIdpConfig = { | type FrontendIdpConfig = { | ||||||
|     authority: string, |     authority: string; | ||||||
|     clientId: string, |     clientId: string; | ||||||
|     scope: string, |     scope: string; | ||||||
|     responseType: string |     responseType: string; | ||||||
| } | }; | ||||||
| 
 | 
 | ||||||
| type FrontendAuthConfig = { | type FrontendAuthConfig = { | ||||||
|     student: FrontendIdpConfig, |     student: FrontendIdpConfig; | ||||||
|     teacher: FrontendIdpConfig |     teacher: FrontendIdpConfig; | ||||||
| } | }; | ||||||
| 
 | 
 | ||||||
| const SCOPE = "openid profile email"; | const SCOPE = 'openid profile email'; | ||||||
| const RESPONSE_TYPE = "code"; | const RESPONSE_TYPE = 'code'; | ||||||
| 
 | 
 | ||||||
| export function getFrontendAuthConfig(): FrontendAuthConfig { | export function getFrontendAuthConfig(): FrontendAuthConfig { | ||||||
|     return { |     return { | ||||||
|  | @ -21,13 +21,13 @@ export function getFrontendAuthConfig(): FrontendAuthConfig { | ||||||
|             authority: getEnvVar(EnvVars.IdpStudentUrl), |             authority: getEnvVar(EnvVars.IdpStudentUrl), | ||||||
|             clientId: getEnvVar(EnvVars.IdpStudentClientId), |             clientId: getEnvVar(EnvVars.IdpStudentClientId), | ||||||
|             scope: SCOPE, |             scope: SCOPE, | ||||||
|             responseType: RESPONSE_TYPE |             responseType: RESPONSE_TYPE, | ||||||
|         }, |         }, | ||||||
|         teacher: { |         teacher: { | ||||||
|             authority: getEnvVar(EnvVars.IdpTeacherUrl), |             authority: getEnvVar(EnvVars.IdpTeacherUrl), | ||||||
|             clientId: getEnvVar(EnvVars.IdpTeacherClientId), |             clientId: getEnvVar(EnvVars.IdpTeacherClientId), | ||||||
|             scope: SCOPE, |             scope: SCOPE, | ||||||
|             responseType: RESPONSE_TYPE |             responseType: RESPONSE_TYPE, | ||||||
|         }, |         }, | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,13 +1,13 @@ | ||||||
| export class UnauthorizedException extends Error { | export class UnauthorizedException extends Error { | ||||||
|     status = 401; |     status = 401; | ||||||
|     constructor(message: string = "Unauthorized") { |     constructor(message: string = 'Unauthorized') { | ||||||
|         super(message); |         super(message); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export class ForbiddenException extends Error { | export class ForbiddenException extends Error { | ||||||
|     status = 403; |     status = 403; | ||||||
|     constructor(message: string = "Forbidden") { |     constructor(message: string = 'Forbidden') { | ||||||
|         super(message); |         super(message); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,25 +1,25 @@ | ||||||
| import {EnvVars, getEnvVar} from "../../util/envvars.js"; | import { EnvVars, getEnvVar } from '../../util/envvars.js'; | ||||||
| import { expressjwt } from 'express-jwt'; | import { expressjwt } from 'express-jwt'; | ||||||
| import {JwtPayload} from 'jsonwebtoken' | import { JwtPayload } from 'jsonwebtoken'; | ||||||
| import jwksClient from 'jwks-rsa'; | import jwksClient from 'jwks-rsa'; | ||||||
| import * as express from "express"; | import * as express from 'express'; | ||||||
| import * as jwt from "jsonwebtoken"; | import * as jwt from 'jsonwebtoken'; | ||||||
| import {AuthenticatedRequest} from "./authenticated-request.js"; | import { AuthenticatedRequest } from './authenticated-request.js'; | ||||||
| import {AuthenticationInfo} from "./authentication-info.js"; | import { AuthenticationInfo } from './authentication-info.js'; | ||||||
| import {ForbiddenException, UnauthorizedException} from "../../exceptions"; | import { ForbiddenException, UnauthorizedException } from '../../exceptions'; | ||||||
| 
 | 
 | ||||||
| const JWKS_CACHE = true; | const JWKS_CACHE = true; | ||||||
| const JWKS_RATE_LIMIT = true; | const JWKS_RATE_LIMIT = true; | ||||||
| const REQUEST_PROPERTY_FOR_JWT_PAYLOAD = "jwtPayload"; | const REQUEST_PROPERTY_FOR_JWT_PAYLOAD = 'jwtPayload'; | ||||||
| const JWT_ALGORITHM = "RS256"; // Not configurable via env vars since supporting other algorithms would
 | const JWT_ALGORITHM = 'RS256'; // Not configurable via env vars since supporting other algorithms would
 | ||||||
| // Require additional libraries to be added.
 | // Require additional libraries to be added.
 | ||||||
| 
 | 
 | ||||||
| const JWT_PROPERTY_NAMES = { | const JWT_PROPERTY_NAMES = { | ||||||
|     username: "preferred_username", |     username: 'preferred_username', | ||||||
|     firstName: "given_name", |     firstName: 'given_name', | ||||||
|     lastName: "family_name", |     lastName: 'family_name', | ||||||
|     name: "name", |     name: 'name', | ||||||
|     email: "email" |     email: 'email', | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| function createJwksClient(uri: string): jwksClient.JwksClient { | function createJwksClient(uri: string): jwksClient.JwksClient { | ||||||
|  | @ -38,7 +38,7 @@ const idpConfigs = { | ||||||
|     teacher: { |     teacher: { | ||||||
|         issuer: getEnvVar(EnvVars.IdpTeacherUrl), |         issuer: getEnvVar(EnvVars.IdpTeacherUrl), | ||||||
|         jwksClient: createJwksClient(getEnvVar(EnvVars.IdpTeacherJwksEndpoint)), |         jwksClient: createJwksClient(getEnvVar(EnvVars.IdpTeacherJwksEndpoint)), | ||||||
|     } |     }, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | @ -47,26 +47,26 @@ const idpConfigs = { | ||||||
| const verifyJwtToken = expressjwt({ | const verifyJwtToken = expressjwt({ | ||||||
|     secret: async (_: express.Request, token: jwt.Jwt | undefined) => { |     secret: async (_: express.Request, token: jwt.Jwt | undefined) => { | ||||||
|         if (!token?.payload || !(token.payload as JwtPayload).iss) { |         if (!token?.payload || !(token.payload as JwtPayload).iss) { | ||||||
|             throw new Error("Invalid token"); |             throw new Error('Invalid token'); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         const issuer = (token.payload as JwtPayload).iss; |         const issuer = (token.payload as JwtPayload).iss; | ||||||
| 
 | 
 | ||||||
|         const idpConfig = Object.values(idpConfigs).find(config => config.issuer === issuer); |         const idpConfig = Object.values(idpConfigs).find((config) => config.issuer === issuer); | ||||||
|         if (!idpConfig) { |         if (!idpConfig) { | ||||||
|             throw new Error("Issuer not accepted."); |             throw new Error('Issuer not accepted.'); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         const signingKey = await idpConfig.jwksClient.getSigningKey(token.header.kid); |         const signingKey = await idpConfig.jwksClient.getSigningKey(token.header.kid); | ||||||
|         if (!signingKey) { |         if (!signingKey) { | ||||||
|             throw new Error("Signing key not found."); |             throw new Error('Signing key not found.'); | ||||||
|         } |         } | ||||||
|         return signingKey.getPublicKey(); |         return signingKey.getPublicKey(); | ||||||
|     }, |     }, | ||||||
|     audience: getEnvVar(EnvVars.IdpAudience), |     audience: getEnvVar(EnvVars.IdpAudience), | ||||||
|     algorithms: [JWT_ALGORITHM], |     algorithms: [JWT_ALGORITHM], | ||||||
|     credentialsRequired: false, |     credentialsRequired: false, | ||||||
|     requestProperty: REQUEST_PROPERTY_FOR_JWT_PAYLOAD |     requestProperty: REQUEST_PROPERTY_FOR_JWT_PAYLOAD, | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | @ -77,12 +77,12 @@ function getAuthenticationInfo(req: AuthenticatedRequest): AuthenticationInfo | | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     const issuer = req.jwtPayload.iss; |     const issuer = req.jwtPayload.iss; | ||||||
|     let accountType: "student" | "teacher"; |     let accountType: 'student' | 'teacher'; | ||||||
| 
 | 
 | ||||||
|     if (issuer === idpConfigs.student.issuer) { |     if (issuer === idpConfigs.student.issuer) { | ||||||
|         accountType = "student"; |         accountType = 'student'; | ||||||
|     } else if (issuer === idpConfigs.teacher.issuer) { |     } else if (issuer === idpConfigs.teacher.issuer) { | ||||||
|         accountType = "teacher"; |         accountType = 'teacher'; | ||||||
|     } else { |     } else { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  | @ -93,7 +93,7 @@ function getAuthenticationInfo(req: AuthenticatedRequest): AuthenticationInfo | | ||||||
|         firstName: req.jwtPayload[JWT_PROPERTY_NAMES.firstName], |         firstName: req.jwtPayload[JWT_PROPERTY_NAMES.firstName], | ||||||
|         lastName: req.jwtPayload[JWT_PROPERTY_NAMES.lastName], |         lastName: req.jwtPayload[JWT_PROPERTY_NAMES.lastName], | ||||||
|         email: req.jwtPayload[JWT_PROPERTY_NAMES.email], |         email: req.jwtPayload[JWT_PROPERTY_NAMES.email], | ||||||
|     } |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | @ -113,7 +113,9 @@ export const authenticateUser = [verifyJwtToken, addAuthenticationInfo]; | ||||||
|  * @param accessCondition Predicate over the current AuthenticationInfo. Access is only granted when this evaluates |  * @param accessCondition Predicate over the current AuthenticationInfo. Access is only granted when this evaluates | ||||||
|  *                        to true. |  *                        to true. | ||||||
|  */ |  */ | ||||||
| export const authorize = (accessCondition: (auth: AuthenticationInfo) => boolean) => (req: AuthenticatedRequest, res: express.Response, next: express.NextFunction): void => { | export const authorize = | ||||||
|  |     (accessCondition: (auth: AuthenticationInfo) => boolean) => | ||||||
|  |     (req: AuthenticatedRequest, res: express.Response, next: express.NextFunction): void => { | ||||||
|         if (!req.auth) { |         if (!req.auth) { | ||||||
|             throw new UnauthorizedException(); |             throw new UnauthorizedException(); | ||||||
|         } else if (!accessCondition(req.auth)) { |         } else if (!accessCondition(req.auth)) { | ||||||
|  | @ -121,19 +123,19 @@ export const authorize = (accessCondition: (auth: AuthenticationInfo) => boolean | ||||||
|         } else { |         } else { | ||||||
|             next(); |             next(); | ||||||
|         } |         } | ||||||
|     } |     }; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Middleware which rejects all unauthenticated users, but accepts all authenticated users. |  * Middleware which rejects all unauthenticated users, but accepts all authenticated users. | ||||||
|  */ |  */ | ||||||
| export const authenticatedOnly = authorize(_ => true); | export const authenticatedOnly = authorize((_) => true); | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Middleware which rejects requests from unauthenticated users or users that aren't students. |  * Middleware which rejects requests from unauthenticated users or users that aren't students. | ||||||
|  */ |  */ | ||||||
| export const studentsOnly = authorize(auth => auth.accountType === "student"); | export const studentsOnly = authorize((auth) => auth.accountType === 'student'); | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Middleware which rejects requests from unauthenticated users or users that aren't teachers. |  * Middleware which rejects requests from unauthenticated users or users that aren't teachers. | ||||||
|  */ |  */ | ||||||
| export const teachersOnly = authorize(auth => auth.accountType === "teacher"); | export const teachersOnly = authorize((auth) => auth.accountType === 'teacher'); | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| import { Request } from "express"; | import { Request } from 'express'; | ||||||
| import { JwtPayload } from "jsonwebtoken"; | import { JwtPayload } from 'jsonwebtoken'; | ||||||
| import {AuthenticationInfo} from "./authentication-info.js"; | import { AuthenticationInfo } from './authentication-info.js'; | ||||||
| 
 | 
 | ||||||
| export interface AuthenticatedRequest extends Request { | export interface AuthenticatedRequest extends Request { | ||||||
|     // Properties are optional since the user is not necessarily authenticated.
 |     // Properties are optional since the user is not necessarily authenticated.
 | ||||||
|  |  | ||||||
|  | @ -2,10 +2,10 @@ | ||||||
|  * Object with information about the user who is currently logged in. |  * Object with information about the user who is currently logged in. | ||||||
|  */ |  */ | ||||||
| export type AuthenticationInfo = { | export type AuthenticationInfo = { | ||||||
|     accountType: "student" | "teacher", |     accountType: 'student' | 'teacher'; | ||||||
|     username: string, |     username: string; | ||||||
|     name?: string, |     name?: string; | ||||||
|     firstName?: string, |     firstName?: string; | ||||||
|     lastName?: string, |     lastName?: string; | ||||||
|     email?: string |     email?: string; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| import cors from "cors"; | import cors from 'cors'; | ||||||
| import {EnvVars, getEnvVar} from "../util/envvars.js"; | import { EnvVars, getEnvVar } from '../util/envvars.js'; | ||||||
| 
 | 
 | ||||||
| export default cors({ | export default cors({ | ||||||
|     origin: getEnvVar(EnvVars.CorsAllowedOrigins).split(','), |     origin: getEnvVar(EnvVars.CorsAllowedOrigins).split(','), | ||||||
|     allowedHeaders: getEnvVar(EnvVars.CorsAllowedHeaders).split(',') |     allowedHeaders: getEnvVar(EnvVars.CorsAllowedHeaders).split(','), | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| import express from 'express' | import express from 'express'; | ||||||
| import {getFrontendAuthConfig} from "../controllers/auth.js"; | import { getFrontendAuthConfig } from '../controllers/auth.js'; | ||||||
| import {authenticatedOnly, studentsOnly, teachersOnly} from "../middleware/auth/auth.js"; | import { authenticatedOnly, studentsOnly, teachersOnly } from '../middleware/auth/auth.js'; | ||||||
| const router = express.Router(); | const router = express.Router(); | ||||||
| 
 | 
 | ||||||
| // Returns auth configuration for frontend
 | // Returns auth configuration for frontend
 | ||||||
|  | @ -9,15 +9,15 @@ router.get('/config', (req, res) => { | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| router.get('/testAuthenticatedOnly', authenticatedOnly, (req, res) => { | router.get('/testAuthenticatedOnly', authenticatedOnly, (req, res) => { | ||||||
|     res.json({message: "If you see this, you should be authenticated!"}); |     res.json({ message: 'If you see this, you should be authenticated!' }); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| router.get('/testStudentsOnly', studentsOnly, (req, res) => { | router.get('/testStudentsOnly', studentsOnly, (req, res) => { | ||||||
|     res.json({message: "If you see this, you should be a student!"}); |     res.json({ message: 'If you see this, you should be a student!' }); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| router.get('/testTeachersOnly', teachersOnly, (req, res) => { | router.get('/testTeachersOnly', teachersOnly, (req, res) => { | ||||||
|     res.json({message: "If you see this, you should be a teacher!"}); |     res.json({ message: 'If you see this, you should be a teacher!' }); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| export default router; | export default router; | ||||||
|  |  | ||||||
|  | @ -23,7 +23,7 @@ export const EnvVars: { [key: string]: EnvVar } = { | ||||||
|     IdpTeacherJwksEndpoint: { key: TEACHER_IDP_PREFIX + 'JWKS_ENDPOINT', required: true }, |     IdpTeacherJwksEndpoint: { key: TEACHER_IDP_PREFIX + 'JWKS_ENDPOINT', required: true }, | ||||||
|     IdpAudience: { key: IDP_PREFIX + 'AUDIENCE', defaultValue: 'account' }, |     IdpAudience: { key: IDP_PREFIX + 'AUDIENCE', defaultValue: 'account' }, | ||||||
|     CorsAllowedOrigins: { key: CORS_PREFIX + 'ALLOWED_ORIGINS', defaultValue: '' }, |     CorsAllowedOrigins: { key: CORS_PREFIX + 'ALLOWED_ORIGINS', defaultValue: '' }, | ||||||
|     CorsAllowedHeaders: { key: CORS_PREFIX + 'ALLOWED_HEADERS', defaultValue: 'Authorization,Content-Type'} |     CorsAllowedHeaders: { key: CORS_PREFIX + 'ALLOWED_HEADERS', defaultValue: 'Authorization,Content-Type' }, | ||||||
| } as const; | } as const; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  |  | ||||||
|  | @ -36,25 +36,20 @@ services: | ||||||
|         environment: |         environment: | ||||||
|             KC_HOSTNAME: localhost |             KC_HOSTNAME: localhost | ||||||
|             KC_HOSTNAME_PORT: 7080 |             KC_HOSTNAME_PORT: 7080 | ||||||
|             KC_HOSTNAME_STRICT_BACKCHANNEL: "true" |             KC_HOSTNAME_STRICT_BACKCHANNEL: 'true' | ||||||
|             KC_BOOTSTRAP_ADMIN_USERNAME: admin |             KC_BOOTSTRAP_ADMIN_USERNAME: admin | ||||||
|             KC_BOOTSTRAP_ADMIN_PASSWORD: admin |             KC_BOOTSTRAP_ADMIN_PASSWORD: admin | ||||||
|             KC_HEALTH_ENABLED: "true" |             KC_HEALTH_ENABLED: 'true' | ||||||
|             KC_LOG_LEVEL: info |             KC_LOG_LEVEL: info | ||||||
|         healthcheck: |         healthcheck: | ||||||
|             test: [ "CMD", "curl", "-f", "http://localhost:7080/health/ready" ] |             test: ['CMD', 'curl', '-f', 'http://localhost:7080/health/ready'] | ||||||
|             interval: 15s |             interval: 15s | ||||||
|             timeout: 2s |             timeout: 2s | ||||||
|             retries: 15 |             retries: 15 | ||||||
|         command: [ |         command: ['start-dev', '--http-port', '7080', '--https-port', '7443', '--import-realm'] | ||||||
|             "start-dev", |  | ||||||
|             "--http-port", "7080", |  | ||||||
|             "--https-port", "7443", |  | ||||||
|             "--import-realm" |  | ||||||
|         ] |  | ||||||
|         ports: |         ports: | ||||||
|             - "7080:7080" |             - '7080:7080' | ||||||
|             - "7443:7443" |             - '7443:7443' | ||||||
|         depends_on: |         depends_on: | ||||||
|             - db |             - db | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| export const apiConfig = { | export const apiConfig = { | ||||||
|     baseUrl: window.location.hostname == "localhost" ? "http://localhost:3000" : window.location.origin |     baseUrl: window.location.hostname == "localhost" ? "http://localhost:3000" : window.location.origin, | ||||||
| } | }; | ||||||
| 
 | 
 | ||||||
| export const loginRoute = "/login"; | export const loginRoute = "/login"; | ||||||
|  |  | ||||||
|  | @ -32,7 +32,7 @@ const router = createRouter({ | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|             path: "/callback", |             path: "/callback", | ||||||
|             component: CallbackPage |             component: CallbackPage, | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|             path: "/student/:id", |             path: "/student/:id", | ||||||
|  |  | ||||||
|  | @ -22,6 +22,6 @@ export async function loadAuthConfig() { | ||||||
|             response_type: authConfig.teacher.responseType, |             response_type: authConfig.teacher.responseType, | ||||||
|             scope: authConfig.teacher.scope, |             scope: authConfig.teacher.scope, | ||||||
|             post_logout_redirect_uri: window.location.origin, |             post_logout_redirect_uri: window.location.origin, | ||||||
|         } |         }, | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ import {computed, reactive} from "vue"; | ||||||
| import type { AuthState, Role, UserManagersForRoles } from "@/services/auth/auth.d.ts"; | import type { AuthState, Role, UserManagersForRoles } from "@/services/auth/auth.d.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 authStorage from "./auth-storage.ts" | import authStorage from "./auth-storage.ts"; | ||||||
| import { loginRoute } from "@/config.ts"; | import { loginRoute } from "@/config.ts"; | ||||||
| import apiClient from "@/services/api-client.ts"; | import apiClient from "@/services/api-client.ts"; | ||||||
| import router from "@/router"; | import router from "@/router"; | ||||||
|  | @ -40,7 +40,7 @@ async function loadUser(): Promise<User | null> { | ||||||
| const authState = reactive<AuthState>({ | const authState = reactive<AuthState>({ | ||||||
|     user: null, |     user: null, | ||||||
|     accessToken: null, |     accessToken: null, | ||||||
|     activeRole: authStorage.getActiveRole() || null |     activeRole: authStorage.getActiveRole() || null, | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const isLoggedIn = computed(() => authState.user !== null); | const isLoggedIn = computed(() => authState.user !== null); | ||||||
|  | @ -70,7 +70,7 @@ async function handleLoginCallback(): Promise<void> { | ||||||
|     if (!activeRole) { |     if (!activeRole) { | ||||||
|         throw new Error("Login callback received, but the user is not logging 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; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  | @ -104,17 +104,20 @@ async function logout(): Promise<void> { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Registering interceptor to add the authorization header to each request when the user is logged in.
 | // Registering interceptor to add the authorization header to each request when the user is logged in.
 | ||||||
| apiClient.interceptors.request.use(async (reqConfig) => { | apiClient.interceptors.request.use( | ||||||
|  |     async (reqConfig) => { | ||||||
|         const token = authState?.user?.access_token; |         const token = authState?.user?.access_token; | ||||||
|         if (token) { |         if (token) { | ||||||
|             reqConfig.headers.Authorization = `Bearer ${token}`; |             reqConfig.headers.Authorization = `Bearer ${token}`; | ||||||
|         } |         } | ||||||
|         return reqConfig; |         return reqConfig; | ||||||
| }, (error) => Promise.reject(error)); |     }, | ||||||
|  |     (error) => Promise.reject(error), | ||||||
|  | ); | ||||||
| 
 | 
 | ||||||
| // Registering interceptor to refresh the token when a request failed because it was expired.
 | // Registering interceptor to refresh the token when a request failed because it was expired.
 | ||||||
| apiClient.interceptors.response.use( | apiClient.interceptors.response.use( | ||||||
|     response => response, |     (response) => response, | ||||||
|     async (error: AxiosError<{ message?: string }>) => { |     async (error: AxiosError<{ message?: string }>) => { | ||||||
|         if (error.response?.status === 401) { |         if (error.response?.status === 401) { | ||||||
|             if (error.response!.data.message === "token_expired") { |             if (error.response!.data.message === "token_expired") { | ||||||
|  | @ -122,11 +125,10 @@ apiClient.interceptors.response.use( | ||||||
|                 await renewToken(); |                 await renewToken(); | ||||||
|                 return apiClient(error.config!); // Retry the request
 |                 return apiClient(error.config!); // Retry the request
 | ||||||
|             } // Apparently, the user got a 401 because he was not logged in yet at all. Redirect him to login.
 |             } // Apparently, the user got a 401 because he was not logged in yet at all. Redirect him to login.
 | ||||||
|                 await initiateLogin() |             await initiateLogin(); | ||||||
|              |  | ||||||
|         } |         } | ||||||
|         return Promise.reject(error); |         return Promise.reject(error); | ||||||
|     } |     }, | ||||||
| ); | ); | ||||||
| 
 | 
 | ||||||
| export default { authState, isLoggedIn, initiateLogin, loadUser, handleLoginCallback, loginAs, logout }; | export default { authState, isLoggedIn, initiateLogin, loadUser, handleLoginCallback, loginAs, logout }; | ||||||
|  |  | ||||||
|  | @ -22,5 +22,5 @@ export default { | ||||||
|      */ |      */ | ||||||
|     deleteActiveRole() { |     deleteActiveRole() { | ||||||
|         localStorage.removeItem("activeRole"); |         localStorage.removeItem("activeRole"); | ||||||
|     } |     }, | ||||||
| } | }; | ||||||
|  |  | ||||||
							
								
								
									
										20
									
								
								frontend/src/services/auth/auth.d.ts
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								frontend/src/services/auth/auth.d.ts
									
										
									
									
										vendored
									
									
								
							|  | @ -1,22 +1,22 @@ | ||||||
| import { type User, UserManager } from "oidc-client-ts"; | import { type User, UserManager } from "oidc-client-ts"; | ||||||
| 
 | 
 | ||||||
| export type AuthState = { | export type AuthState = { | ||||||
|     user: User | null, |     user: User | null; | ||||||
|     accessToken: string | null, |     accessToken: string | null; | ||||||
|     activeRole: Role | null |     activeRole: Role | null; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export type FrontendAuthConfig = { | export type FrontendAuthConfig = { | ||||||
|     student: FrontendIdpConfig, |     student: FrontendIdpConfig; | ||||||
|     teacher: FrontendIdpConfig |     teacher: FrontendIdpConfig; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export type FrontendIdpConfig = { | export type FrontendIdpConfig = { | ||||||
|     authority: string, |     authority: string; | ||||||
|     clientId: string, |     clientId: string; | ||||||
|     scope: string, |     scope: string; | ||||||
|     responseType: string |     responseType: string; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export type Role = "student" | "teacher"; | export type Role = "student" | "teacher"; | ||||||
| export type UserManagersForRoles = {student: UserManager, teacher: UserManager}; | export type UserManagersForRoles = { student: UserManager; teacher: UserManager }; | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
|     import { useRouter } from "vue-router"; |     import { useRouter } from "vue-router"; | ||||||
|     import { onMounted } from "vue"; |     import { onMounted } from "vue"; | ||||||
|     import auth from "../services/auth/auth-service.ts" |     import auth from "../services/auth/auth-service.ts"; | ||||||
| 
 | 
 | ||||||
|     const router = useRouter(); |     const router = useRouter(); | ||||||
| 
 | 
 | ||||||
|  | @ -19,6 +19,4 @@ | ||||||
|     <p>Logging you in...</p> |     <p>Logging you in...</p> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <style scoped> | <style scoped></style> | ||||||
| 
 |  | ||||||
| </style> |  | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ | ||||||
|     const testResponse = ref(null); |     const testResponse = ref(null); | ||||||
| 
 | 
 | ||||||
|     async function testAuthenticated() { |     async function testAuthenticated() { | ||||||
|         testResponse.value = await apiClient.get("/auth/testAuthenticatedOnly") |         testResponse.value = await apiClient.get("/auth/testAuthenticatedOnly"); | ||||||
|     } |     } | ||||||
| </script> | </script> | ||||||
| 
 | 
 | ||||||
|  | @ -16,13 +16,13 @@ | ||||||
|         <b>Welcome to the dwengo homepage</b> |         <b>Welcome to the dwengo homepage</b> | ||||||
|         <div v-if="auth.isLoggedIn.value"> |         <div v-if="auth.isLoggedIn.value"> | ||||||
|             <p>Hello {{ auth.authState.user?.profile.name }}!</p> |             <p>Hello {{ auth.authState.user?.profile.name }}!</p> | ||||||
|             <p>Your access token for the backend is: <code>{{auth.authState.user?.access_token}}</code></p> |             <p> | ||||||
|  |                 Your access token for the backend is: <code>{{ auth.authState.user?.access_token }}</code> | ||||||
|  |             </p> | ||||||
|         </div> |         </div> | ||||||
| 
 | 
 | ||||||
|         <v-btn @click="testAuthenticated">Send test request</v-btn> |         <v-btn @click="testAuthenticated">Send test request</v-btn> | ||||||
|         <p v-if="testResponse">Response from the test request: {{ testResponse }}</p> |         <p v-if="testResponse">Response from the test request: {{ testResponse }}</p> | ||||||
|     </main> |     </main> | ||||||
| </template> | </template> | ||||||
| <style scoped> | <style scoped></style> | ||||||
| 
 |  | ||||||
| </style> |  | ||||||
|  |  | ||||||
|  | @ -23,12 +23,12 @@ | ||||||
|             <v-btn @click="loginAsTeacher">Login as teacher</v-btn> |             <v-btn @click="loginAsTeacher">Login as teacher</v-btn> | ||||||
|         </div> |         </div> | ||||||
|         <div v-if="auth.isLoggedIn.value"> |         <div v-if="auth.isLoggedIn.value"> | ||||||
|             <p>You are currently logged in as {{ auth.authState.user!.profile.name }} ({{ auth.authState.activeRole }})</p> |             <p> | ||||||
|  |                 You are currently logged in as {{ auth.authState.user!.profile.name }} ({{ auth.authState.activeRole }}) | ||||||
|  |             </p> | ||||||
|             <v-btn @click="performLogout">Logout</v-btn> |             <v-btn @click="performLogout">Logout</v-btn> | ||||||
|         </div> |         </div> | ||||||
|     </main> |     </main> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <style scoped> | <style scoped></style> | ||||||
| 
 |  | ||||||
| </style> |  | ||||||
|  |  | ||||||
|  | @ -1,7 +1,9 @@ | ||||||
| # Testdata in de IDP | # Testdata in de IDP | ||||||
|  | 
 | ||||||
| De IDP in `docker-compose.yml` is zo geconfigureerd dat hij automatisch bij het starten een testconfiguratie inlaadt. Deze houdt in: | De IDP in `docker-compose.yml` is zo geconfigureerd dat hij automatisch bij het starten een testconfiguratie inlaadt. Deze houdt in: | ||||||
| * Een realm `student` die de IDP voor leerlingen representeert. | 
 | ||||||
|   * Hierin de gebruiker met username `testleerling1`, wachtwoord `password`. | - Een realm `student` die de IDP voor leerlingen representeert. | ||||||
| * Een realm `teacher` die de IDP voor leerkrachten representeert. |     - Hierin de gebruiker met username `testleerling1`, wachtwoord `password`. | ||||||
|   * Hierin de gebruiker met username `testleerkracht1`, wachtwoord `password`. | - Een realm `teacher` die de IDP voor leerkrachten representeert. | ||||||
| * De admin-account (in de realm `master`) heeft username `admin` en wachtwoord `admin`. |     - Hierin de gebruiker met username `testleerkracht1`, wachtwoord `password`. | ||||||
|  | - De admin-account (in de realm `master`) heeft username `admin` en wachtwoord `admin`. | ||||||
|  |  | ||||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
		Reference in a new issue
	
	 Lint Action
						Lint Action