fix(framework): Ensure that CORS and Auth middleware is applied for routes only defined in middlewares.ts (#10339)

This commit is contained in:
Kasper Fabricius Kristensen
2024-12-04 14:49:43 +01:00
committed by GitHub
parent 582f52b31d
commit a5263083fa
4 changed files with 125 additions and 41 deletions

View File

@@ -0,0 +1,5 @@
---
"@medusajs/framework": patch
---
fix(framework): Apply CORS and auth middleware for global middleware that is not already applied by routes

View File

@@ -59,6 +59,7 @@
"@mikro-orm/postgresql": "5.9.7",
"@swc/core": "^1.7.28",
"@swc/jest": "^0.2.36",
"@types/cors": "^2.8.17",
"@types/jsonwebtoken": "^8.5.9",
"awilix": "^8.0.1",
"ioredis": "^5.4.1",

View File

@@ -183,6 +183,44 @@ function getBodyParserMiddleware(args?: ParserConfigArgs) {
]
}
function createCorsOptions(origin: string): cors.CorsOptions {
return {
origin: parseCorsOrigins(origin),
credentials: true,
}
}
function applyCors(
router: Router,
route: string | RegExp,
corsConfig: cors.CorsOptions
) {
router.use(route, cors(corsConfig))
}
function getRouteContext(
path: string | RegExp
): "admin" | "store" | "auth" | null {
/**
* We cannot reliably guess the route context from a regex, so we skip it.
*/
if (path instanceof RegExp) {
return null
}
if (path.startsWith("/admin")) {
return "admin"
}
if (path.startsWith("/store")) {
return "store"
}
if (path.startsWith("/auth")) {
return "auth"
}
return null
}
// TODO this router would need a proper rework, but it is out of scope right now
export class ApiRoutesLoader {
@@ -589,13 +627,15 @@ export class ApiRoutesLoader {
/**
* Applies middleware that checks if a valid publishable key is set on store request
*/
applyStorePublishableKeyMiddleware(route: string) {
applyStorePublishableKeyMiddleware(route: string | RegExp) {
let middleware = ensurePublishableApiKeyMiddleware as unknown as
| RequestHandler
| MiddlewareFunction
if (ApiRoutesLoader.traceMiddleware) {
middleware = ApiRoutesLoader.traceMiddleware(middleware, { route: route })
middleware = ApiRoutesLoader.traceMiddleware(middleware, {
route: String(route),
})
}
this.#router.use(route, middleware as RequestHandler)
@@ -606,7 +646,7 @@ export class ApiRoutesLoader {
* needed to pass the middleware via the trace calls
*/
applyAuthMiddleware(
route: string,
route: string | RegExp,
actorType: string | string[],
authType: AuthType | AuthType[],
options?: { allowUnauthenticated?: boolean; allowUnregistered?: boolean }
@@ -617,7 +657,7 @@ export class ApiRoutesLoader {
if (ApiRoutesLoader.traceMiddleware) {
authenticateMiddleware = ApiRoutesLoader.traceMiddleware(
authenticateMiddleware,
{ route: route }
{ route: String(route) }
)
}
@@ -632,6 +672,14 @@ export class ApiRoutesLoader {
*/
applyRouteSpecificMiddlewares(): void {
const prioritizedRoutes = prioritize([...this.#routesMap.values()])
const handledPaths = new Set<string>()
const middlewarePaths = new Set<string | RegExp>()
const globalRoutes = this.#globalMiddlewaresDescriptor?.config?.routes ?? []
for (const route of globalRoutes) {
middlewarePaths.add(route.matcher)
}
for (const descriptor of prioritizedRoutes) {
if (!descriptor.config?.routes?.length) {
@@ -639,58 +687,33 @@ export class ApiRoutesLoader {
}
const config = descriptor.config
const routes = descriptor.config.routes
/**
* Apply default store and admin middlewares if
* not opted out of.
*/
handledPaths.add(descriptor.route)
if (config.shouldAppendAdminCors) {
/**
* Apply the admin cors
*/
this.#router.use(
applyCors(
this.#router,
descriptor.route,
cors({
origin: parseCorsOrigins(
configManager.config.projectConfig.http.adminCors
),
credentials: true,
})
createCorsOptions(configManager.config.projectConfig.http.adminCors)
)
}
if (config.shouldAppendAuthCors) {
/**
* Apply the auth cors
*/
this.#router.use(
applyCors(
this.#router,
descriptor.route,
cors({
origin: parseCorsOrigins(
configManager.config.projectConfig.http.authCors
),
credentials: true,
})
createCorsOptions(configManager.config.projectConfig.http.authCors)
)
}
if (config.shouldAppendStoreCors) {
/**
* Apply the store cors
*/
this.#router.use(
applyCors(
this.#router,
descriptor.route,
cors({
origin: parseCorsOrigins(
configManager.config.projectConfig.http.storeCors
),
credentials: true,
})
createCorsOptions(configManager.config.projectConfig.http.storeCors)
)
}
// Apply other middlewares
if (config.routeType === "store") {
this.applyStorePublishableKeyMiddleware(descriptor.route)
}
@@ -715,7 +738,7 @@ export class ApiRoutesLoader {
])
}
for (const route of routes) {
for (const route of descriptor.config.routes) {
/**
* Apply the body parser middleware if the route
* has not opted out of it.
@@ -723,6 +746,51 @@ export class ApiRoutesLoader {
this.applyBodyParserMiddleware(descriptor.route, route.method!)
}
}
/**
* Apply CORS and auth middleware for paths defined in global middleware but not already handled by routes.
*/
for (const path of middlewarePaths) {
if (typeof path === "string" && handledPaths.has(path)) {
continue
}
const context = getRouteContext(path)
if (!context) {
continue
}
switch (context) {
case "admin":
applyCors(
this.#router,
path,
createCorsOptions(configManager.config.projectConfig.http.adminCors)
)
this.applyAuthMiddleware(path, "user", [
"bearer",
"session",
"api-key",
])
break
case "store":
applyCors(
this.#router,
path,
createCorsOptions(configManager.config.projectConfig.http.storeCors)
)
this.applyStorePublishableKeyMiddleware(path)
break
case "auth":
applyCors(
this.#router,
path,
createCorsOptions(configManager.config.projectConfig.http.authCors)
)
break
}
}
}
/**

View File

@@ -5754,6 +5754,7 @@ __metadata:
"@opentelemetry/api": ^1.9.0
"@swc/core": ^1.7.28
"@swc/jest": ^0.2.36
"@types/cors": ^2.8.17
"@types/express": ^4.17.17
"@types/jsonwebtoken": ^8.5.9
awilix: ^8.0.1
@@ -13414,6 +13415,15 @@ __metadata:
languageName: node
linkType: hard
"@types/cors@npm:^2.8.17":
version: 2.8.17
resolution: "@types/cors@npm:2.8.17"
dependencies:
"@types/node": "*"
checksum: 457364c28c89f3d9ed34800e1de5c6eaaf344d1bb39af122f013322a50bc606eb2aa6f63de4e41a7a08ba7ef454473926c94a830636723da45bf786df032696d
languageName: node
linkType: hard
"@types/doctrine@npm:^0.0.9":
version: 0.0.9
resolution: "@types/doctrine@npm:0.0.9"