wcz-layout
Version:
1 lines • 7.77 kB
Source Map (JSON)
{"version":3,"file":"middleware.mjs","names":["createMiddleware","jose","permissions","serverEnv","getSessionUser","buildUser","hasPermission","TokenPayload","User","resolveUser","request","Request","Promise","authHeader","headers","get","startsWith","payload","verifyToken","substring","length","error","Response","json","message","Error","status","requiredAuthenticationMiddleware","server","user","next","context","publicAuthenticationMiddleware","authenticationMiddleware","options","optional","authorizationMiddleware","permissionKey","middleware","name","token","jwtVerify","getJWKS","issuer","ENTRA_TENANT_ID","audience","ENTRA_CLIENT_ID","jwksCache","ReturnType","createRemoteJWKSet","URL","createMiddleware","z","validationMiddleware","schema","ZodType","T","server","next","request","json","result","safeParse","success","fieldErrors","flattenError","error","firstFieldName","Object","keys","firstErrorMessage","name","charAt","toUpperCase","slice","message","replace","toLowerCase","Response","status","context","data"],"sources":["../src/middleware/authMiddleware.ts","../src/middleware/validationMiddleware.ts","../src/middleware/csrfMiddleware.ts"],"sourcesContent":["import { createMiddleware } from \"@tanstack/react-start\";\nimport * as jose from \"jose\";\nimport type { permissions } from \"virtual:wcz-layout\";\nimport { serverEnv } from \"~/env\";\nimport { getSessionUser } from \"~/lib/auth/user\";\nimport { buildUser, hasPermission } from \"~/lib/utils\";\nimport type { TokenPayload } from \"~/models/TokenPayload\";\nimport type { User } from \"~/models/User\";\n\nasync function resolveUser(request: Request): Promise<User | null> {\n const authHeader = request.headers.get(\"Authorization\");\n\n if (authHeader?.startsWith(\"Bearer \")) {\n let payload: TokenPayload;\n try {\n payload = await verifyToken(authHeader.substring(\"Bearer \".length));\n } catch (error) {\n throw Response.json(\n { message: error instanceof Error ? error.message : \"Unauthorized: Invalid access token\" },\n { status: 401 },\n );\n }\n return buildUser(payload);\n }\n\n return getSessionUser();\n}\n\nconst requiredAuthenticationMiddleware = createMiddleware().server<{ user: User }>(\n async ({ next, request }) => {\n const user = await resolveUser(request);\n\n if (!user)\n throw Response.json({ message: \"Unauthorized: User not signed in\" }, { status: 401 });\n\n return next({ context: { user } });\n },\n);\n\nconst publicAuthenticationMiddleware = createMiddleware().server<{ user: User | null }>(\n async ({ next, request }) => {\n const user = await resolveUser(request);\n return next({ context: { user } });\n },\n);\n\nexport function authenticationMiddleware(options?: {\n optional?: false;\n}): typeof requiredAuthenticationMiddleware;\n\nexport function authenticationMiddleware(options: {\n optional: true;\n}): typeof publicAuthenticationMiddleware;\n\nexport function authenticationMiddleware(options: {\n optional: boolean;\n}): typeof requiredAuthenticationMiddleware | typeof publicAuthenticationMiddleware;\n\nexport function authenticationMiddleware(options?: { optional?: boolean }) {\n return options?.optional ? publicAuthenticationMiddleware : requiredAuthenticationMiddleware;\n}\n\nexport const authorizationMiddleware = (permissionKey: keyof typeof permissions) =>\n createMiddleware()\n .middleware([authenticationMiddleware()])\n .server(async ({ next, context }) => {\n if (hasPermission(context.user, permissionKey)) return next();\n throw Response.json(\n {\n message: `Forbidden: User ${context.user.name} is not authorized to access this resource`,\n },\n { status: 403 },\n );\n });\n\nasync function verifyToken(token: string): Promise<TokenPayload> {\n const { payload } = await jose.jwtVerify(token, getJWKS(), {\n issuer: `https://login.microsoftonline.com/${serverEnv.ENTRA_TENANT_ID}/v2.0`,\n audience: serverEnv.ENTRA_CLIENT_ID,\n });\n return payload as unknown as TokenPayload;\n}\n\nlet jwksCache: ReturnType<typeof jose.createRemoteJWKSet> | null = null;\n\nfunction getJWKS() {\n jwksCache ??= jose.createRemoteJWKSet(\n new URL(`https://login.microsoftonline.com/${serverEnv.ENTRA_TENANT_ID}/discovery/v2.0/keys`),\n );\n return jwksCache;\n}\n","import { createMiddleware } from \"@tanstack/react-start\";\r\nimport { z } from \"zod\";\r\n\r\nexport const validationMiddleware = <T>(schema: z.ZodType<T>) =>\r\n createMiddleware().server(async ({ next, request }) => {\r\n const json = await request.json();\r\n const result = schema.safeParse(json);\r\n if (!result.success) {\r\n const { fieldErrors } = z.flattenError(result.error);\r\n const firstFieldName = Object.keys(fieldErrors)[0];\r\n const firstErrorMessage = fieldErrors[firstFieldName as keyof typeof fieldErrors]?.[0];\r\n\r\n if (firstFieldName && firstErrorMessage) {\r\n const name = firstFieldName.charAt(0).toUpperCase() + firstFieldName.slice(1);\r\n const message = firstErrorMessage.replace(/^Invalid input:\\s*/i, \"\").toLowerCase();\r\n return Response.json({ message: `${name} - ${message}` }, { status: 400 });\r\n }\r\n\r\n return Response.json({ message: \"Validation failed\" }, { status: 400 });\r\n }\r\n return await next({ context: { data: result.data } });\r\n });\r\n","import { createCsrfMiddleware } from \"@tanstack/react-start\";\n\nexport const csrfMiddleware = createCsrfMiddleware({\n filter: (ctx) => ctx.handlerType === \"serverFn\",\n});\n"],"mappings":"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"}