feat: Destroy session + introduce http config (#7336)
This commit is contained in:
1 parent
ce75755ac6
commit
bf4724b8e6
26 files changed
+568
-396
No files matched your search
@@ -12,7 +12,10 @@ const adminHeaders = {
|
|||||||
jest.setTimeout(30000)
|
jest.setTimeout(30000)
|
||||||
|
|
||||||
medusaIntegrationTestRunner({
|
medusaIntegrationTestRunner({
|
||||||
env: { MEDUSA_FF_MEDUSA_V2: true },
|
force_modules_migration: true,
|
||||||
|
env: {
|
||||||
|
MEDUSA_FF_MEDUSA_V2: true,
|
||||||
|
},
|
||||||
testSuite: ({ dbConnection, getContainer, api }) => {
|
testSuite: ({ dbConnection, getContainer, api }) => {
|
||||||
let container
|
let container
|
||||||
|
|
||||||
@@ -58,21 +61,48 @@ medusaIntegrationTestRunner({
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO: Remove in V2, as this is no longer supported
|
it("should test the entire authentication lifecycle", async () => {
|
||||||
it("creates admin JWT token correctly", async () => {
|
// sign in
|
||||||
breaking(async () => {
|
const response = await api.post("/auth/admin/emailpass", {
|
||||||
const response = await api
|
email: "admin@medusa.js",
|
||||||
.post("/admin/auth/token", {
|
password: "secret_password",
|
||||||
email: "admin@medusa.js",
|
|
||||||
password: "secret_password",
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.log(err)
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(response.status).toEqual(200)
|
|
||||||
expect(response.data.access_token).toEqual(expect.any(String))
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
expect(response.status).toEqual(200)
|
||||||
|
expect(response.data).toEqual({ token: expect.any(String) })
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
headers: { ["authorization"]: `Bearer ${response.data.token}` },
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert token to session
|
||||||
|
const cookieRequest = await api.post("/auth/session", {}, headers)
|
||||||
|
expect(cookieRequest.status).toEqual(200)
|
||||||
|
|
||||||
|
// extract cookie
|
||||||
|
const [cookie] = cookieRequest.headers["set-cookie"][0].split(";")
|
||||||
|
|
||||||
|
const cookieHeader = {
|
||||||
|
headers: { Cookie: cookie },
|
||||||
|
}
|
||||||
|
|
||||||
|
// perform cookie authenticated request
|
||||||
|
const authedRequest = await api.get(
|
||||||
|
"/admin/products?limit=1",
|
||||||
|
cookieHeader
|
||||||
|
)
|
||||||
|
expect(authedRequest.status).toEqual(200)
|
||||||
|
|
||||||
|
// sign out
|
||||||
|
const signOutRequest = await api.delete("/auth/session", cookieHeader)
|
||||||
|
expect(signOutRequest.status).toEqual(200)
|
||||||
|
|
||||||
|
// attempt to perform authenticated request
|
||||||
|
const unAuthedRequest = await api
|
||||||
|
.get("/admin/products?limit=1", cookieHeader)
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(unAuthedRequest.response.status).toEqual(401)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -24,10 +24,12 @@ module.exports = {
|
|||||||
redis_url: redisUrl,
|
redis_url: redisUrl,
|
||||||
database_url: DB_URL,
|
database_url: DB_URL,
|
||||||
database_type: "postgres",
|
database_type: "postgres",
|
||||||
jwt_secret: "test",
|
http: {
|
||||||
cookie_secret: "test",
|
compression: {
|
||||||
http_compression: {
|
enabled: enableResponseCompression,
|
||||||
enabled: enableResponseCompression,
|
},
|
||||||
|
jwtSecret: "test",
|
||||||
|
cookieSecret: "test",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
featureFlags: {
|
featureFlags: {
|
||||||
@@ -39,75 +41,70 @@ module.exports = {
|
|||||||
options: { ttl: cacheTTL },
|
options: { ttl: cacheTTL },
|
||||||
},
|
},
|
||||||
workflows: true,
|
workflows: true,
|
||||||
// We don't want to load the modules if v2 is not enabled, as they run data operations and migrations on load.
|
[Modules.AUTH]: {
|
||||||
...(enableMedusaV2
|
scope: "internal",
|
||||||
? {
|
resources: "shared",
|
||||||
[Modules.AUTH]: {
|
resolve: "@medusajs/auth",
|
||||||
scope: "internal",
|
options: {
|
||||||
resources: "shared",
|
providers: [
|
||||||
resolve: "@medusajs/auth",
|
{
|
||||||
options: {
|
name: "emailpass",
|
||||||
providers: [
|
scopes: {
|
||||||
{
|
admin: {},
|
||||||
name: "emailpass",
|
store: {},
|
||||||
scopes: {
|
|
||||||
admin: {},
|
|
||||||
store: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[Modules.USER]: {
|
],
|
||||||
scope: "internal",
|
},
|
||||||
resources: "shared",
|
},
|
||||||
resolve: "@medusajs/user",
|
[Modules.USER]: {
|
||||||
|
scope: "internal",
|
||||||
|
resources: "shared",
|
||||||
|
resolve: "@medusajs/user",
|
||||||
|
options: {
|
||||||
|
jwt_secret: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[Modules.CACHE]: {
|
||||||
|
resolve: "@medusajs/cache-inmemory",
|
||||||
|
options: { ttl: 0 }, // Cache disabled
|
||||||
|
},
|
||||||
|
[Modules.STOCK_LOCATION]: {
|
||||||
|
resolve: "@medusajs/stock-location-next",
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
[Modules.INVENTORY]: {
|
||||||
|
resolve: "@medusajs/inventory-next",
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
[Modules.FILE]: {
|
||||||
|
resolve: "@medusajs/file",
|
||||||
|
options: {
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
resolve: "@medusajs/file-local-next",
|
||||||
options: {
|
options: {
|
||||||
jwt_secret: "test",
|
config: {
|
||||||
|
local: {},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[Modules.CACHE]: {
|
],
|
||||||
resolve: "@medusajs/cache-inmemory",
|
},
|
||||||
options: { ttl: 0 }, // Cache disabled
|
},
|
||||||
},
|
[Modules.PRODUCT]: true,
|
||||||
[Modules.STOCK_LOCATION]: {
|
[Modules.PRICING]: true,
|
||||||
resolve: "@medusajs/stock-location-next",
|
[Modules.PROMOTION]: true,
|
||||||
options: {},
|
[Modules.CUSTOMER]: true,
|
||||||
},
|
[Modules.SALES_CHANNEL]: true,
|
||||||
[Modules.INVENTORY]: {
|
[Modules.CART]: true,
|
||||||
resolve: "@medusajs/inventory-next",
|
[Modules.WORKFLOW_ENGINE]: true,
|
||||||
options: {},
|
[Modules.REGION]: true,
|
||||||
},
|
[Modules.API_KEY]: true,
|
||||||
[Modules.FILE]: {
|
[Modules.STORE]: true,
|
||||||
resolve: "@medusajs/file",
|
[Modules.TAX]: true,
|
||||||
options: {
|
[Modules.CURRENCY]: true,
|
||||||
providers: [
|
[Modules.PAYMENT]: true,
|
||||||
{
|
[Modules.FULFILLMENT]: true,
|
||||||
resolve: "@medusajs/file-local-next",
|
|
||||||
options: {
|
|
||||||
config: {
|
|
||||||
local: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[Modules.PRODUCT]: true,
|
|
||||||
[Modules.PRICING]: true,
|
|
||||||
[Modules.PROMOTION]: true,
|
|
||||||
[Modules.CUSTOMER]: true,
|
|
||||||
[Modules.SALES_CHANNEL]: true,
|
|
||||||
[Modules.CART]: true,
|
|
||||||
[Modules.WORKFLOW_ENGINE]: true,
|
|
||||||
[Modules.REGION]: true,
|
|
||||||
[Modules.API_KEY]: true,
|
|
||||||
[Modules.STORE]: true,
|
|
||||||
[Modules.TAX]: true,
|
|
||||||
[Modules.CURRENCY]: true,
|
|
||||||
[Modules.PAYMENT]: true,
|
|
||||||
[Modules.FULFILLMENT]: true,
|
|
||||||
}
|
|
||||||
: {}),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -34,7 +34,7 @@ medusaIntegrationTestRunner({
|
|||||||
const authService: IAuthModuleService = appContainer.resolve(
|
const authService: IAuthModuleService = appContainer.resolve(
|
||||||
ModuleRegistrationName.AUTH
|
ModuleRegistrationName.AUTH
|
||||||
)
|
)
|
||||||
const { jwt_secret } =
|
const { http } =
|
||||||
appContainer.resolve("configModule").projectConfig
|
appContainer.resolve("configModule").projectConfig
|
||||||
const authUser = await authService.create({
|
const authUser = await authService.create({
|
||||||
entity_id: "store_user",
|
entity_id: "store_user",
|
||||||
@@ -42,7 +42,7 @@ medusaIntegrationTestRunner({
|
|||||||
scope: "store",
|
scope: "store",
|
||||||
})
|
})
|
||||||
|
|
||||||
const token = jwt.sign(authUser, jwt_secret)
|
const token = jwt.sign(authUser, http.jwtSecret)
|
||||||
|
|
||||||
const response = await api.post(
|
const response = await api.post(
|
||||||
`/store/customers`,
|
`/store/customers`,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export const createAuthenticatedCustomer = async (
|
|||||||
appContainer: MedusaContainer,
|
appContainer: MedusaContainer,
|
||||||
customerData: Partial<CreateCustomerDTO> = {}
|
customerData: Partial<CreateCustomerDTO> = {}
|
||||||
) => {
|
) => {
|
||||||
const { jwt_secret } = appContainer.resolve("configModule").projectConfig
|
const { http } = appContainer.resolve("configModule").projectConfig
|
||||||
const authService = appContainer.resolve(ModuleRegistrationName.AUTH)
|
const authService = appContainer.resolve(ModuleRegistrationName.AUTH)
|
||||||
const customerModuleService = appContainer.resolve(
|
const customerModuleService = appContainer.resolve(
|
||||||
ModuleRegistrationName.CUSTOMER
|
ModuleRegistrationName.CUSTOMER
|
||||||
@@ -27,7 +27,7 @@ export const createAuthenticatedCustomer = async (
|
|||||||
app_metadata: { customer_id: customer.id },
|
app_metadata: { customer_id: customer.id },
|
||||||
})
|
})
|
||||||
|
|
||||||
const token = jwt.sign(authUser, jwt_secret)
|
const token = jwt.sign(authUser, http.jwtSecret)
|
||||||
|
|
||||||
return { customer, authUser, jwt: token }
|
return { customer, authUser, jwt: token }
|
||||||
}
|
}
|
||||||
@@ -38,8 +38,10 @@ module.exports = {
|
|||||||
projectConfig: {
|
projectConfig: {
|
||||||
database_url: DB_URL,
|
database_url: DB_URL,
|
||||||
database_type: "postgres",
|
database_type: "postgres",
|
||||||
jwt_secret: "test",
|
http: {
|
||||||
cookie_secret: "test",
|
jwtSecret: "test",
|
||||||
|
cookieSecret: "test",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
featureFlags: {
|
featureFlags: {
|
||||||
medusa_v2: enableMedusaV2,
|
medusa_v2: enableMedusaV2,
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
UIMatch,
|
UIMatch,
|
||||||
useLocation,
|
useLocation,
|
||||||
useMatches,
|
useMatches,
|
||||||
|
useNavigate,
|
||||||
} from "react-router-dom"
|
} from "react-router-dom"
|
||||||
|
|
||||||
import { Skeleton } from "../../common/skeleton"
|
import { Skeleton } from "../../common/skeleton"
|
||||||
@@ -27,6 +28,8 @@ import { useMe } from "../../../hooks/api/users"
|
|||||||
import { useSearch } from "../../../providers/search-provider"
|
import { useSearch } from "../../../providers/search-provider"
|
||||||
import { useSidebar } from "../../../providers/sidebar-provider"
|
import { useSidebar } from "../../../providers/sidebar-provider"
|
||||||
import { useTheme } from "../../../providers/theme-provider"
|
import { useTheme } from "../../../providers/theme-provider"
|
||||||
|
import { useLogout } from "../../../hooks/api/auth"
|
||||||
|
import { queryClient } from "../../../lib/medusa"
|
||||||
|
|
||||||
export const Shell = ({ children }: PropsWithChildren) => {
|
export const Shell = ({ children }: PropsWithChildren) => {
|
||||||
return (
|
return (
|
||||||
@@ -200,20 +203,19 @@ const ThemeToggle = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Logout = () => {
|
const Logout = () => {
|
||||||
// const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
// const { mutateAsync: logoutMutation } = useAdminDeleteSession()
|
const { mutateAsync: logoutMutation } = useLogout()
|
||||||
|
|
||||||
const handleLayout = async () => {
|
const handleLayout = async () => {
|
||||||
// await logoutMutation(undefined, {
|
await logoutMutation(undefined, {
|
||||||
// onSuccess: () => {
|
onSuccess: () => {
|
||||||
// /**
|
/**
|
||||||
// * When the user logs out, we want to clear the query cache
|
* When the user logs out, we want to clear the query cache
|
||||||
// */
|
*/
|
||||||
// queryClient.clear()
|
queryClient.clear()
|
||||||
// navigate("/login")
|
navigate("/login")
|
||||||
// },
|
},
|
||||||
// })
|
})
|
||||||
// noop
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -14,3 +14,10 @@ export const useEmailPassLogin = (
|
|||||||
...options,
|
...options,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const useLogout = (options?: UseMutationOptions<void, Error>) => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: () => sdk.auth.logout(),
|
||||||
|
...options,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -30,4 +30,12 @@ export class Auth {
|
|||||||
this.client.setToken(token)
|
this.client.setToken(token)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logout = async () => {
|
||||||
|
await this.client.fetch("/auth/session", {
|
||||||
|
method: "DELETE",
|
||||||
|
})
|
||||||
|
|
||||||
|
this.client.clearToken()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -109,6 +109,28 @@ export class Client {
|
|||||||
this.setToken_(token)
|
this.setToken_(token)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clearToken() {
|
||||||
|
this.clearToken_()
|
||||||
|
}
|
||||||
|
|
||||||
|
protected clearToken_() {
|
||||||
|
const { storageMethod, storageKey } = this.getTokenStorageInfo_()
|
||||||
|
switch (storageMethod) {
|
||||||
|
case "local": {
|
||||||
|
window.localStorage.removeItem(storageKey)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case "session": {
|
||||||
|
window.sessionStorage.removeItem(storageKey)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case "memory": {
|
||||||
|
this.token = ""
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected initClient(): ClientFetch {
|
protected initClient(): ClientFetch {
|
||||||
const defaultHeaders = new Headers({
|
const defaultHeaders = new Headers({
|
||||||
"content-type": "application/json",
|
"content-type": "application/json",
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ type SessionOptions = {
|
|||||||
*/
|
*/
|
||||||
saveUninitialized?: boolean
|
saveUninitialized?: boolean
|
||||||
/**
|
/**
|
||||||
* The secret to sign the session ID cookie. By default, the value of `cookie_secret` is used.
|
* The secret to sign the session ID cookie. By default, the value of `http.cookieSecret` is used.
|
||||||
* Refer to [express-session’s documentation](https://www.npmjs.com/package/express-session#secret) for details.
|
* Refer to [express-session’s documentation](https://www.npmjs.com/package/express-session#secret) for details.
|
||||||
*/
|
*/
|
||||||
secret?: string
|
secret?: string
|
||||||
@@ -111,193 +111,6 @@ export type HttpCompressionOptions = {
|
|||||||
* Essential configurations related to the Medusa backend, such as database and CORS configurations.
|
* Essential configurations related to the Medusa backend, such as database and CORS configurations.
|
||||||
*/
|
*/
|
||||||
export type ProjectConfigOptions = {
|
export type ProjectConfigOptions = {
|
||||||
/**
|
|
||||||
* The Medusa backend’s API Routes are protected by Cross-Origin Resource Sharing (CORS). So, only allowed URLs or URLs matching a specified pattern can send requests to the backend’s API Routes.
|
|
||||||
*
|
|
||||||
* `store_cors` is a string used to specify the accepted URLs or patterns for store API Routes. It can either be one accepted origin, or a comma-separated list of accepted origins.
|
|
||||||
*
|
|
||||||
* Every origin in that list must either be:
|
|
||||||
*
|
|
||||||
* 1. A URL. For example, `http://localhost:8000`. The URL must not end with a backslash;
|
|
||||||
* 2. Or a regular expression pattern that can match more than one origin. For example, `.example.com`. The regex pattern that the backend tests for is `^([\/~@;%#'])(.*?)\1([gimsuy]*)$`.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* Some example values of common use cases:
|
|
||||||
*
|
|
||||||
* ```bash
|
|
||||||
* # Allow different ports locally starting with 800
|
|
||||||
* STORE_CORS=/http:\/\/localhost:800\d+$/
|
|
||||||
*
|
|
||||||
* # Allow any origin ending with vercel.app. For example, storefront.vercel.app
|
|
||||||
* STORE_CORS=/vercel\.app$/
|
|
||||||
*
|
|
||||||
* # Allow all HTTP requests
|
|
||||||
* STORE_CORS=/http:\/\/.+/
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* Then, set the configuration in `medusa-config.js`:
|
|
||||||
*
|
|
||||||
* ```js title="medusa-config.js"
|
|
||||||
* module.exports = {
|
|
||||||
* projectConfig: {
|
|
||||||
* store_cors: process.env.STORE_CORS,
|
|
||||||
* // ...
|
|
||||||
* },
|
|
||||||
* // ...
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* If you’re adding the value directly within `medusa-config.js`, make sure to add an extra escaping `/` for every backslash in the pattern. For example:
|
|
||||||
*
|
|
||||||
* ```js title="medusa-config.js"
|
|
||||||
* module.exports = {
|
|
||||||
* projectConfig: {
|
|
||||||
* store_cors: "/vercel\\.app$/",
|
|
||||||
* // ...
|
|
||||||
* },
|
|
||||||
* // ...
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
store_cors?: string
|
|
||||||
/**
|
|
||||||
* The Medusa backend’s API Routes are protected by Cross-Origin Resource Sharing (CORS). So, only allowed URLs or URLs matching a specified pattern can send requests to the backend’s API Routes.
|
|
||||||
*
|
|
||||||
* `admin_cors` is a string used to specify the accepted URLs or patterns for admin API Routes. It can either be one accepted origin, or a comma-separated list of accepted origins.
|
|
||||||
*
|
|
||||||
* Every origin in that list must either be:
|
|
||||||
*
|
|
||||||
* 1. A URL. For example, `http://localhost:7001`. The URL must not end with a backslash;
|
|
||||||
* 2. Or a regular expression pattern that can match more than one origin. For example, `.example.com`. The regex pattern that the backend tests for is `^([\/~@;%#'])(.*?)\1([gimsuy]*)$`.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* Some example values of common use cases:
|
|
||||||
*
|
|
||||||
* ```bash
|
|
||||||
* # Allow different ports locally starting with 700
|
|
||||||
* ADMIN_CORS=/http:\/\/localhost:700\d+$/
|
|
||||||
*
|
|
||||||
* # Allow any origin ending with vercel.app. For example, admin.vercel.app
|
|
||||||
* ADMIN_CORS=/vercel\.app$/
|
|
||||||
*
|
|
||||||
* # Allow all HTTP requests
|
|
||||||
* ADMIN_CORS=/http:\/\/.+/
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* Then, set the configuration in `medusa-config.js`:
|
|
||||||
*
|
|
||||||
* ```js title="medusa-config.js"
|
|
||||||
* module.exports = {
|
|
||||||
* projectConfig: {
|
|
||||||
* admin_cors: process.env.ADMIN_CORS,
|
|
||||||
* // ...
|
|
||||||
* },
|
|
||||||
* // ...
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* If you’re adding the value directly within `medusa-config.js`, make sure to add an extra escaping `/` for every backslash in the pattern. For example:
|
|
||||||
*
|
|
||||||
* ```js title="medusa-config.js"
|
|
||||||
* module.exports = {
|
|
||||||
* projectConfig: {
|
|
||||||
* admin_cors: "/http:\\/\\/localhost:700\\d+$/",
|
|
||||||
* // ...
|
|
||||||
* },
|
|
||||||
* // ...
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
admin_cors?: string
|
|
||||||
/**
|
|
||||||
* The Medusa backend’s API Routes are protected by Cross-Origin Resource Sharing (CORS). So, only allowed URLs or URLs matching a specified pattern can send requests to the backend’s API Routes.
|
|
||||||
*
|
|
||||||
* `auth_cors` is a string used to specify the accepted URLs or patterns for API Routes starting with `/auth`. It can either be one accepted origin, or a comma-separated list of accepted origins.
|
|
||||||
*
|
|
||||||
* Every origin in that list must either be:
|
|
||||||
*
|
|
||||||
* 1. A URL. For example, `http://localhost:7001`. The URL must not end with a backslash;
|
|
||||||
* 2. Or a regular expression pattern that can match more than one origin. For example, `.example.com`. The regex pattern that the backend tests for is `^([\/~@;%#'])(.*?)\1([gimsuy]*)$`.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* Some example values of common use cases:
|
|
||||||
*
|
|
||||||
* ```bash
|
|
||||||
* # Allow different ports locally starting with 700
|
|
||||||
* AUTH_CORS=/http:\/\/localhost:700\d+$/
|
|
||||||
*
|
|
||||||
* # Allow any origin ending with vercel.app. For example, admin.vercel.app
|
|
||||||
* AUTH_CORS=/vercel\.app$/
|
|
||||||
*
|
|
||||||
* # Allow all HTTP requests
|
|
||||||
* AUTH_CORS=/http:\/\/.+/
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* Then, set the configuration in `medusa-config.js`:
|
|
||||||
*
|
|
||||||
* ```js title="medusa-config.js"
|
|
||||||
* module.exports = {
|
|
||||||
* projectConfig: {
|
|
||||||
* auth_cors: process.env.AUTH_CORS,
|
|
||||||
* // ...
|
|
||||||
* },
|
|
||||||
* // ...
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* If you’re adding the value directly within `medusa-config.js`, make sure to add an extra escaping `/` for every backslash in the pattern. For example:
|
|
||||||
*
|
|
||||||
* ```js title="medusa-config.js"
|
|
||||||
* module.exports = {
|
|
||||||
* projectConfig: {
|
|
||||||
* auth_cors: "/http:\\/\\/localhost:700\\d+$/",
|
|
||||||
* // ...
|
|
||||||
* },
|
|
||||||
* // ...
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
auth_cors?: string
|
|
||||||
/**
|
|
||||||
* A random string used to create cookie tokens. Although this configuration option is not required, it’s highly recommended to set it for better security.
|
|
||||||
*
|
|
||||||
* In a development environment, if this option is not set, the default secret is `supersecret` However, in production, if this configuration is not set, an error is thrown and
|
|
||||||
* the backend crashes.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```js title="medusa-config.js"
|
|
||||||
* module.exports = {
|
|
||||||
* projectConfig: {
|
|
||||||
* cookie_secret: process.env.COOKIE_SECRET ||
|
|
||||||
* "supersecret",
|
|
||||||
* // ...
|
|
||||||
* },
|
|
||||||
* // ...
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
cookie_secret?: string
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A random string used to create authentication tokens. Although this configuration option is not required, it’s highly recommended to set it for better security.
|
|
||||||
*
|
|
||||||
* In a development environment, if this option is not set the default secret is `supersecret` However, in production, if this configuration is not set an error, an
|
|
||||||
* error is thrown and the backend crashes.
|
|
||||||
*
|
|
||||||
* @example
|
|
||||||
* ```js title="medusa-config.js"
|
|
||||||
* module.exports = {
|
|
||||||
* projectConfig: {
|
|
||||||
* jwt_secret: process.env.JWT_SECRET ||
|
|
||||||
* "supersecret",
|
|
||||||
* // ...
|
|
||||||
* },
|
|
||||||
* // ...
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
jwt_secret?: string
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name of the database to connect to. If specified in `database_url`, then it’s not required to include it.
|
* The name of the database to connect to. If specified in `database_url`, then it’s not required to include it.
|
||||||
*
|
*
|
||||||
@@ -562,6 +375,7 @@ export type ProjectConfigOptions = {
|
|||||||
session_options?: SessionOptions
|
session_options?: SessionOptions
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @deprecated - use `http.compression` instead
|
||||||
* Configure HTTP compression from the application layer. If you have access to the HTTP server, the recommended approach would be to enable it there.
|
* Configure HTTP compression from the application layer. If you have access to the HTTP server, the recommended approach would be to enable it there.
|
||||||
* However, some platforms don't offer access to the HTTP layer and in those cases, this is a good alternative.
|
* However, some platforms don't offer access to the HTTP layer and in those cases, this is a good alternative.
|
||||||
*
|
*
|
||||||
@@ -624,6 +438,268 @@ export type ProjectConfigOptions = {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
worker_mode?: "shared" | "worker" | "server"
|
worker_mode?: "shared" | "worker" | "server"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure the application's http-specific settings
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```js title="medusa-config.js"
|
||||||
|
* module.exports = {
|
||||||
|
* projectConfig: {
|
||||||
|
* http: {
|
||||||
|
* cookieSecret: "some-super-secret",
|
||||||
|
* compression: { ... },
|
||||||
|
* }
|
||||||
|
* // ...
|
||||||
|
* },
|
||||||
|
* // ...
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
http: {
|
||||||
|
/**
|
||||||
|
* A random string used to create authentication tokens in the http layer. Although this configuration option is not required, it’s highly recommended to set it for better security.
|
||||||
|
*
|
||||||
|
* In a development environment, if this option is not set the default secret is `supersecret` However, in production, if this configuration is not set an error, an
|
||||||
|
* error is thrown and the backend crashes.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```js title="medusa-config.js"
|
||||||
|
* module.exports = {
|
||||||
|
* projectConfig: {
|
||||||
|
* http: {
|
||||||
|
* cookieSecret: "supersecret"
|
||||||
|
* }
|
||||||
|
* },
|
||||||
|
* // ...
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
jwtSecret?: string
|
||||||
|
/**
|
||||||
|
* The expiration time for the JWT token. If not provided, the default value is `24h`.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```js title="medusa-config.js"
|
||||||
|
* module.exports = {
|
||||||
|
* projectConfig: {
|
||||||
|
* http: {
|
||||||
|
* jwtExpiresIn: "2d"
|
||||||
|
* }
|
||||||
|
* },
|
||||||
|
* // ...
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
jwtExpiresIn?: string
|
||||||
|
/**
|
||||||
|
* A random string used to create cookie tokens in the http layer. Although this configuration option is not required, it’s highly recommended to set it for better security.
|
||||||
|
*
|
||||||
|
* In a development environment, if this option is not set, the default secret is `supersecret` However, in production, if this configuration is not set, an error is thrown and
|
||||||
|
* the backend crashes.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```js title="medusa-config.js"
|
||||||
|
* module.exports = {
|
||||||
|
* projectConfig: {
|
||||||
|
* http: {
|
||||||
|
* cookieSecret: "supersecret"
|
||||||
|
* }
|
||||||
|
* // ...
|
||||||
|
* },
|
||||||
|
* // ...
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
cookieSecret?: string
|
||||||
|
/**
|
||||||
|
* The Medusa backend’s API Routes are protected by Cross-Origin Resource Sharing (CORS). So, only allowed URLs or URLs matching a specified pattern can send requests to the backend’s API Routes.
|
||||||
|
*
|
||||||
|
* `cors` is a string used to specify the accepted URLs or patterns for API Routes starting with `/auth`. It can either be one accepted origin, or a comma-separated list of accepted origins.
|
||||||
|
*
|
||||||
|
* Every origin in that list must either be:
|
||||||
|
*
|
||||||
|
* 1. A URL. For example, `http://localhost:7001`. The URL must not end with a backslash;
|
||||||
|
* 2. Or a regular expression pattern that can match more than one origin. For example, `.example.com`. The regex pattern that the backend tests for is `^([\/~@;%#'])(.*?)\1([gimsuy]*)$`.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* Some example values of common use cases:
|
||||||
|
*
|
||||||
|
* ```bash
|
||||||
|
* # Allow different ports locally starting with 700
|
||||||
|
* AUTH_CORS=/http:\/\/localhost:700\d+$/
|
||||||
|
*
|
||||||
|
* # Allow any origin ending with vercel.app. For example, admin.vercel.app
|
||||||
|
* AUTH_CORS=/vercel\.app$/
|
||||||
|
*
|
||||||
|
* # Allow all HTTP requests
|
||||||
|
* AUTH_CORS=/http:\/\/.+/
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Then, set the configuration in `medusa-config.js`:
|
||||||
|
*
|
||||||
|
* ```js title="medusa-config.js"
|
||||||
|
* module.exports = {
|
||||||
|
* projectConfig: {
|
||||||
|
* http: {
|
||||||
|
* authCors: process.env.AUTH_CORS
|
||||||
|
* }
|
||||||
|
* // ...
|
||||||
|
* },
|
||||||
|
* // ...
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* If you’re adding the value directly within `medusa-config.js`, make sure to add an extra escaping `/` for every backslash in the pattern. For example:
|
||||||
|
*
|
||||||
|
* ```js title="medusa-config.js"
|
||||||
|
* module.exports = {
|
||||||
|
* projectConfig: {
|
||||||
|
* http: {
|
||||||
|
* authCors: "/http:\\/\\/localhost:700\\d+$/",
|
||||||
|
* }
|
||||||
|
* // ...
|
||||||
|
* },
|
||||||
|
* // ...
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
authCors: string
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Configure HTTP compression from the application layer. If you have access to the HTTP server, the recommended approach would be to enable it there.
|
||||||
|
* However, some platforms don't offer access to the HTTP layer and in those cases, this is a good alternative.
|
||||||
|
*
|
||||||
|
* Its value is an object that has the following properties:
|
||||||
|
*
|
||||||
|
* If you enable HTTP compression and you want to disable it for specific API Routes, you can pass in the request header `"x-no-compression": true`.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```js title="medusa-config.js"
|
||||||
|
* module.exports = {
|
||||||
|
* projectConfig: {
|
||||||
|
* http: {
|
||||||
|
* compression: {
|
||||||
|
* enabled: true,
|
||||||
|
* level: 6,
|
||||||
|
* memLevel: 8,
|
||||||
|
* threshold: 1024,
|
||||||
|
* }
|
||||||
|
* },
|
||||||
|
* // ...
|
||||||
|
* },
|
||||||
|
* // ...
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
compression?: HttpCompressionOptions
|
||||||
|
/**
|
||||||
|
* The Medusa backend’s API Routes are protected by Cross-Origin Resource Sharing (CORS). So, only allowed URLs or URLs matching a specified pattern can send requests to the backend’s API Routes.
|
||||||
|
*
|
||||||
|
* `store_cors` is a string used to specify the accepted URLs or patterns for store API Routes. It can either be one accepted origin, or a comma-separated list of accepted origins.
|
||||||
|
*
|
||||||
|
* Every origin in that list must either be:
|
||||||
|
*
|
||||||
|
* 1. A URL. For example, `http://localhost:8000`. The URL must not end with a backslash;
|
||||||
|
* 2. Or a regular expression pattern that can match more than one origin. For example, `.example.com`. The regex pattern that the backend tests for is `^([\/~@;%#'])(.*?)\1([gimsuy]*)$`.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* Some example values of common use cases:
|
||||||
|
*
|
||||||
|
* ```bash
|
||||||
|
* # Allow different ports locally starting with 800
|
||||||
|
* STORE_CORS=/http:\/\/localhost:800\d+$/
|
||||||
|
*
|
||||||
|
* # Allow any origin ending with vercel.app. For example, storefront.vercel.app
|
||||||
|
* STORE_CORS=/vercel\.app$/
|
||||||
|
*
|
||||||
|
* # Allow all HTTP requests
|
||||||
|
* STORE_CORS=/http:\/\/.+/
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Then, set the configuration in `medusa-config.js`:
|
||||||
|
*
|
||||||
|
* ```js title="medusa-config.js"
|
||||||
|
* module.exports = {
|
||||||
|
* projectConfig: {
|
||||||
|
* http: {
|
||||||
|
* storeCors: process.env.STORE_CORS,
|
||||||
|
* }
|
||||||
|
* // ...
|
||||||
|
* },
|
||||||
|
* // ...
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* If you’re adding the value directly within `medusa-config.js`, make sure to add an extra escaping `/` for every backslash in the pattern. For example:
|
||||||
|
*
|
||||||
|
* ```js title="medusa-config.js"
|
||||||
|
* module.exports = {
|
||||||
|
* projectConfig: {
|
||||||
|
* http: {
|
||||||
|
* storeCors: "/vercel\\.app$/",
|
||||||
|
* }
|
||||||
|
* // ...
|
||||||
|
* },
|
||||||
|
* // ...
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
storeCors: string
|
||||||
|
/**
|
||||||
|
* The Medusa backend’s API Routes are protected by Cross-Origin Resource Sharing (CORS). So, only allowed URLs or URLs matching a specified pattern can send requests to the backend’s API Routes.
|
||||||
|
*
|
||||||
|
* `admin_cors` is a string used to specify the accepted URLs or patterns for admin API Routes. It can either be one accepted origin, or a comma-separated list of accepted origins.
|
||||||
|
*
|
||||||
|
* Every origin in that list must either be:
|
||||||
|
*
|
||||||
|
* 1. A URL. For example, `http://localhost:7001`. The URL must not end with a backslash;
|
||||||
|
* 2. Or a regular expression pattern that can match more than one origin. For example, `.example.com`. The regex pattern that the backend tests for is `^([\/~@;%#'])(.*?)\1([gimsuy]*)$`.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* Some example values of common use cases:
|
||||||
|
*
|
||||||
|
* ```bash
|
||||||
|
* # Allow different ports locally starting with 700
|
||||||
|
* ADMIN_CORS=/http:\/\/localhost:700\d+$/
|
||||||
|
*
|
||||||
|
* # Allow any origin ending with vercel.app. For example, admin.vercel.app
|
||||||
|
* ADMIN_CORS=/vercel\.app$/
|
||||||
|
*
|
||||||
|
* # Allow all HTTP requests
|
||||||
|
* ADMIN_CORS=/http:\/\/.+/
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Then, set the configuration in `medusa-config.js`:
|
||||||
|
*
|
||||||
|
* ```js title="medusa-config.js"
|
||||||
|
* module.exports = {
|
||||||
|
* projectConfig: {
|
||||||
|
* http: {
|
||||||
|
* adminCors: process.env.ADMIN_CORS,
|
||||||
|
* }
|
||||||
|
* // ...
|
||||||
|
* },
|
||||||
|
* // ...
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* If you’re adding the value directly within `medusa-config.js`, make sure to add an extra escaping `/` for every backslash in the pattern. For example:
|
||||||
|
*
|
||||||
|
* ```js title="medusa-config.js"
|
||||||
|
* module.exports = {
|
||||||
|
* projectConfig: {
|
||||||
|
* http: {
|
||||||
|
* adminCors: process.env.ADMIN_CORS,
|
||||||
|
* }
|
||||||
|
* // ...
|
||||||
|
* },
|
||||||
|
* // ...
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
adminCors: string
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -53,19 +53,28 @@ export const POST = async (
|
|||||||
userData: req.validatedBody,
|
userData: req.validatedBody,
|
||||||
authUserId: req.auth.auth_user_id,
|
authUserId: req.auth.auth_user_id,
|
||||||
},
|
},
|
||||||
|
throwOnError: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
const { errors } = await createUserAccountWorkflow(req.scope).run(input)
|
||||||
|
|
||||||
|
if (Array.isArray(errors) && errors[0]) {
|
||||||
|
throw errors[0].error
|
||||||
}
|
}
|
||||||
|
|
||||||
const { result } = await createUserAccountWorkflow(req.scope).run(input)
|
|
||||||
const user = await refetchUser(
|
const user = await refetchUser(
|
||||||
req.auth.auth_user_id,
|
req.auth.auth_user_id,
|
||||||
req.scope,
|
req.scope,
|
||||||
req.remoteQueryConfig.fields
|
req.remoteQueryConfig.fields
|
||||||
)
|
)
|
||||||
|
|
||||||
const { jwt_secret } = req.scope.resolve(
|
const { http } = req.scope.resolve(
|
||||||
ContainerRegistrationKeys.CONFIG_MODULE
|
ContainerRegistrationKeys.CONFIG_MODULE
|
||||||
).projectConfig
|
).projectConfig
|
||||||
const token = jwt.sign(user, jwt_secret)
|
|
||||||
|
const token = jwt.sign(user, http.jwtSecret, {
|
||||||
|
expiresIn: http.jwtExpiresIn,
|
||||||
|
})
|
||||||
|
|
||||||
res.status(200).json({ user, token })
|
res.status(200).json({ user, token })
|
||||||
}
|
}
|
||||||
@@ -25,9 +25,11 @@ export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
|
|||||||
const { success, error, authUser, successRedirectUrl } = authResult
|
const { success, error, authUser, successRedirectUrl } = authResult
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
const { jwt_secret } = req.scope.resolve("configModule").projectConfig
|
const { http } = req.scope.resolve("configModule").projectConfig
|
||||||
|
|
||||||
const token = jwt.sign(authUser, jwt_secret)
|
const { jwtSecret, jwtExpiresIn } = http
|
||||||
|
|
||||||
|
const token = jwt.sign(authUser, jwtSecret, { expiresIn: jwtExpiresIn })
|
||||||
|
|
||||||
if (successRedirectUrl) {
|
if (successRedirectUrl) {
|
||||||
const url = new URL(successRedirectUrl!)
|
const url = new URL(successRedirectUrl!)
|
||||||
|
|||||||
@@ -30,8 +30,11 @@ export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
const { jwt_secret } = req.scope.resolve("configModule").projectConfig
|
const { http } = req.scope.resolve("configModule").projectConfig
|
||||||
const token = jwt.sign(authUser, jwt_secret)
|
|
||||||
|
const token = jwt.sign(authUser, http.jwtSecret, {
|
||||||
|
expiresIn: http.jwtExpiresIn,
|
||||||
|
})
|
||||||
|
|
||||||
return res.status(200).json({ token })
|
return res.status(200).json({ token })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,11 @@ export const authRoutesMiddlewares: MiddlewareRoute[] = [
|
|||||||
matcher: "/auth/session",
|
matcher: "/auth/session",
|
||||||
middlewares: [authenticate(/.*/, "bearer")],
|
middlewares: [authenticate(/.*/, "bearer")],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
method: ["DELETE"],
|
||||||
|
matcher: "/auth/session",
|
||||||
|
middlewares: [authenticate(/.*/, ["session"])],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
method: ["POST"],
|
method: ["POST"],
|
||||||
matcher: "/auth/:scope/:auth_provider/callback",
|
matcher: "/auth/:scope/:auth_provider/callback",
|
||||||
|
|||||||
@@ -11,3 +11,11 @@ export const POST = async (
|
|||||||
|
|
||||||
res.status(200).json({ user: req.auth })
|
res.status(200).json({ user: req.auth })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const DELETE = async (
|
||||||
|
req: AuthenticatedMedusaRequest,
|
||||||
|
res: MedusaResponse
|
||||||
|
) => {
|
||||||
|
req.session.destroy()
|
||||||
|
res.json({ success: true })
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
|
import { ConfigModule } from "@medusajs/types"
|
||||||
import { getConfigFile, isDefined } from "medusa-core-utils"
|
import { getConfigFile, isDefined } from "medusa-core-utils"
|
||||||
import logger from "./logger"
|
import logger from "./logger"
|
||||||
import { ConfigModule } from "@medusajs/types"
|
|
||||||
|
|
||||||
const isProduction = ["production", "prod"].includes(process.env.NODE_ENV || "")
|
const isProduction = ["production", "prod"].includes(process.env.NODE_ENV || "")
|
||||||
|
|
||||||
@@ -18,47 +18,55 @@ export const handleConfigError = (error: Error): void => {
|
|||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default (rootDirectory: string): ConfigModule => {
|
const buildHttpConfig = (projectConfig: ConfigModule["projectConfig"]) => {
|
||||||
const { configModule, error } = getConfigFile<ConfigModule>(
|
const http = projectConfig.http ?? {}
|
||||||
rootDirectory,
|
|
||||||
`medusa-config`
|
|
||||||
)
|
|
||||||
|
|
||||||
if (error) {
|
http.jwtExpiresIn = http?.jwtExpiresIn ?? "1d"
|
||||||
handleConfigError(error)
|
http.authCors = http.authCors ?? ""
|
||||||
|
http.storeCors = http.storeCors ?? ""
|
||||||
|
http.adminCors = http.adminCors ?? ""
|
||||||
|
|
||||||
|
http.jwtSecret = http?.jwtSecret ?? process.env.JWT_SECRET
|
||||||
|
|
||||||
|
if (!http.jwtSecret) {
|
||||||
|
errorHandler(
|
||||||
|
`[medusa-config] ⚠️ http.jwtSecret not found.${
|
||||||
|
isProduction ? "" : "Using default 'supersecret'."
|
||||||
|
}`
|
||||||
|
)
|
||||||
|
|
||||||
|
http.jwtSecret = "supersecret"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!configModule?.projectConfig?.redis_url) {
|
http.cookieSecret =
|
||||||
|
projectConfig.http?.cookieSecret ?? process.env.COOKIE_SECRET
|
||||||
|
|
||||||
|
if (!http.cookieSecret) {
|
||||||
|
errorHandler(
|
||||||
|
`[medusa-config] ⚠️ http.cookieSecret not found.${
|
||||||
|
isProduction ? "" : " Using default 'supersecret'."
|
||||||
|
}`
|
||||||
|
)
|
||||||
|
|
||||||
|
http.cookieSecret = "supersecret"
|
||||||
|
}
|
||||||
|
|
||||||
|
return http
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizeProjectConfig = (
|
||||||
|
projectConfig: ConfigModule["projectConfig"]
|
||||||
|
) => {
|
||||||
|
if (!projectConfig?.redis_url) {
|
||||||
console.log(
|
console.log(
|
||||||
`[medusa-config] ⚠️ redis_url not found. A fake redis instance will be used.`
|
`[medusa-config] ⚠️ redis_url not found. A fake redis instance will be used.`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const jwt_secret =
|
projectConfig.http = buildHttpConfig(projectConfig)
|
||||||
configModule?.projectConfig?.jwt_secret ?? process.env.JWT_SECRET
|
|
||||||
if (!jwt_secret) {
|
|
||||||
errorHandler(
|
|
||||||
`[medusa-config] ⚠️ jwt_secret not found.${
|
|
||||||
isProduction
|
|
||||||
? ""
|
|
||||||
: " fallback to either cookie_secret or default 'supersecret'."
|
|
||||||
}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const cookie_secret =
|
let worker_mode = projectConfig?.worker_mode
|
||||||
configModule?.projectConfig?.cookie_secret ?? process.env.COOKIE_SECRET
|
|
||||||
if (!cookie_secret) {
|
|
||||||
errorHandler(
|
|
||||||
`[medusa-config] ⚠️ cookie_secret not found.${
|
|
||||||
isProduction
|
|
||||||
? ""
|
|
||||||
: " fallback to either cookie_secret or default 'supersecret'."
|
|
||||||
}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let worker_mode = configModule?.projectConfig?.worker_mode
|
|
||||||
if (!isDefined(worker_mode)) {
|
if (!isDefined(worker_mode)) {
|
||||||
const env = process.env.MEDUSA_WORKER_MODE
|
const env = process.env.MEDUSA_WORKER_MODE
|
||||||
if (isDefined(env)) {
|
if (isDefined(env)) {
|
||||||
@@ -71,12 +79,25 @@ export default (rootDirectory: string): ConfigModule => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
projectConfig: {
|
...projectConfig,
|
||||||
jwt_secret: jwt_secret ?? "supersecret",
|
worker_mode,
|
||||||
cookie_secret: cookie_secret ?? "supersecret",
|
}
|
||||||
...configModule?.projectConfig,
|
}
|
||||||
worker_mode,
|
|
||||||
},
|
export default (rootDirectory: string): ConfigModule => {
|
||||||
|
const { configModule, error } = getConfigFile<ConfigModule>(
|
||||||
|
rootDirectory,
|
||||||
|
`medusa-config`
|
||||||
|
)
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
handleConfigError(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
const projectConfig = normalizeProjectConfig(configModule.projectConfig)
|
||||||
|
|
||||||
|
return {
|
||||||
|
projectConfig,
|
||||||
admin: configModule?.admin ?? {},
|
admin: configModule?.admin ?? {},
|
||||||
modules: configModule.modules ?? {},
|
modules: configModule.modules ?? {},
|
||||||
featureFlags: configModule?.featureFlags ?? {},
|
featureFlags: configModule?.featureFlags ?? {},
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
|
import { ConfigModule } from "@medusajs/types"
|
||||||
import createStore from "connect-redis"
|
import createStore from "connect-redis"
|
||||||
import cookieParser from "cookie-parser"
|
import cookieParser from "cookie-parser"
|
||||||
import { Express } from "express"
|
import { Express } from "express"
|
||||||
import session from "express-session"
|
import session from "express-session"
|
||||||
import morgan from "morgan"
|
|
||||||
import Redis from "ioredis"
|
import Redis from "ioredis"
|
||||||
import { ConfigModule } from "@medusajs/types"
|
import morgan from "morgan"
|
||||||
|
|
||||||
type Options = {
|
type Options = {
|
||||||
app: Express
|
app: Express
|
||||||
@@ -28,14 +28,14 @@ export default async ({
|
|||||||
sameSite = "none"
|
sameSite = "none"
|
||||||
}
|
}
|
||||||
|
|
||||||
const { cookie_secret, session_options } = configModule.projectConfig
|
const { http, session_options } = configModule.projectConfig
|
||||||
const sessionOpts = {
|
const sessionOpts = {
|
||||||
name: session_options?.name ?? "connect.sid",
|
name: session_options?.name ?? "connect.sid",
|
||||||
resave: session_options?.resave ?? true,
|
resave: session_options?.resave ?? true,
|
||||||
rolling: session_options?.rolling ?? false,
|
rolling: session_options?.rolling ?? false,
|
||||||
saveUninitialized: session_options?.saveUninitialized ?? true,
|
saveUninitialized: session_options?.saveUninitialized ?? true,
|
||||||
proxy: true,
|
proxy: true,
|
||||||
secret: session_options?.secret ?? cookie_secret,
|
secret: session_options?.secret ?? http?.cookieSecret,
|
||||||
cookie: {
|
cookie: {
|
||||||
sameSite,
|
sameSite,
|
||||||
secure,
|
secure,
|
||||||
|
|||||||
@@ -4,11 +4,13 @@ export const storeGlobalMiddlewareMock = jest.fn()
|
|||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
projectConfig: {
|
projectConfig: {
|
||||||
store_cors: "http://localhost:8000",
|
|
||||||
admin_cors: "http://localhost:7001",
|
|
||||||
database_logging: false,
|
database_logging: false,
|
||||||
jwt_secret: "supersecret",
|
http: {
|
||||||
cookie_secret: "superSecret",
|
storeCors: "http://localhost:8000",
|
||||||
|
adminCors: "http://localhost:7001",
|
||||||
|
jwtSecret: "supersecret",
|
||||||
|
cookieSecret: "superSecret",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
featureFlags: {},
|
featureFlags: {},
|
||||||
plugins: [],
|
plugins: [],
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ export const createServer = async (rootDir) => {
|
|||||||
user_id: opts.adminSession.userId || opts.adminSession.jwt?.userId,
|
user_id: opts.adminSession.userId || opts.adminSession.jwt?.userId,
|
||||||
domain: "admin",
|
domain: "admin",
|
||||||
},
|
},
|
||||||
config.projectConfig.jwt_secret
|
config.projectConfig.http.jwtSecret
|
||||||
)
|
)
|
||||||
|
|
||||||
headers.Authorization = `Bearer ${token}`
|
headers.Authorization = `Bearer ${token}`
|
||||||
@@ -137,7 +137,7 @@ export const createServer = async (rootDir) => {
|
|||||||
opts.clientSession.jwt?.customer_id,
|
opts.clientSession.jwt?.customer_id,
|
||||||
domain: "store",
|
domain: "store",
|
||||||
},
|
},
|
||||||
config.projectConfig.jwt_secret
|
config.projectConfig.http.jwtSecret
|
||||||
)
|
)
|
||||||
|
|
||||||
headers.Authorization = `Bearer ${token}`
|
headers.Authorization = `Bearer ${token}`
|
||||||
|
|||||||
@@ -1,30 +1,30 @@
|
|||||||
|
import { ConfigModule } from "@medusajs/types"
|
||||||
import { promiseAll, wrapHandler } from "@medusajs/utils"
|
import { promiseAll, wrapHandler } from "@medusajs/utils"
|
||||||
import cors from "cors"
|
import cors from "cors"
|
||||||
import { type Express, json, Router, text, urlencoded } from "express"
|
import { Router, json, text, urlencoded, type Express } from "express"
|
||||||
import { readdir } from "fs/promises"
|
import { readdir } from "fs/promises"
|
||||||
import { parseCorsOrigins } from "medusa-core-utils"
|
import { parseCorsOrigins } from "medusa-core-utils"
|
||||||
import { extname, join, sep } from "path"
|
import { extname, join, sep } from "path"
|
||||||
|
import { MedusaRequest, MedusaResponse } from "../../../types/routing"
|
||||||
import {
|
import {
|
||||||
authenticateCustomer,
|
authenticateCustomer,
|
||||||
authenticateLegacy,
|
authenticateLegacy,
|
||||||
errorHandler,
|
errorHandler,
|
||||||
requireCustomerAuthentication,
|
requireCustomerAuthentication,
|
||||||
} from "../../../utils/middlewares"
|
} from "../../../utils/middlewares"
|
||||||
import { MedusaRequest, MedusaResponse } from "../../../types/routing"
|
|
||||||
import logger from "../../logger"
|
import logger from "../../logger"
|
||||||
import {
|
import {
|
||||||
AsyncRouteHandler,
|
AsyncRouteHandler,
|
||||||
GlobalMiddlewareDescriptor,
|
GlobalMiddlewareDescriptor,
|
||||||
HTTP_METHODS,
|
HTTP_METHODS,
|
||||||
MiddlewareRoute,
|
MiddlewareRoute,
|
||||||
MiddlewaresConfig,
|
|
||||||
MiddlewareVerb,
|
MiddlewareVerb,
|
||||||
|
MiddlewaresConfig,
|
||||||
ParserConfigArgs,
|
ParserConfigArgs,
|
||||||
RouteConfig,
|
RouteConfig,
|
||||||
RouteDescriptor,
|
RouteDescriptor,
|
||||||
RouteVerb,
|
RouteVerb,
|
||||||
} from "./types"
|
} from "./types"
|
||||||
import { ConfigModule } from "@medusajs/types"
|
|
||||||
|
|
||||||
const log = ({
|
const log = ({
|
||||||
activityId,
|
activityId,
|
||||||
@@ -610,7 +610,7 @@ export class RoutesLoader {
|
|||||||
descriptor.route,
|
descriptor.route,
|
||||||
cors({
|
cors({
|
||||||
origin: parseCorsOrigins(
|
origin: parseCorsOrigins(
|
||||||
this.configModule.projectConfig.admin_cors || ""
|
this.configModule.projectConfig.http.adminCors
|
||||||
),
|
),
|
||||||
credentials: true,
|
credentials: true,
|
||||||
})
|
})
|
||||||
@@ -625,7 +625,7 @@ export class RoutesLoader {
|
|||||||
descriptor.route,
|
descriptor.route,
|
||||||
cors({
|
cors({
|
||||||
origin: parseCorsOrigins(
|
origin: parseCorsOrigins(
|
||||||
this.configModule.projectConfig.auth_cors || ""
|
this.configModule.projectConfig.http.authCors
|
||||||
),
|
),
|
||||||
credentials: true,
|
credentials: true,
|
||||||
})
|
})
|
||||||
@@ -640,7 +640,7 @@ export class RoutesLoader {
|
|||||||
descriptor.route,
|
descriptor.route,
|
||||||
cors({
|
cors({
|
||||||
origin: parseCorsOrigins(
|
origin: parseCorsOrigins(
|
||||||
this.configModule.projectConfig.store_cors || ""
|
this.configModule.projectConfig.http.storeCors
|
||||||
),
|
),
|
||||||
credentials: true,
|
credentials: true,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -3,9 +3,6 @@ import {
|
|||||||
MedusaAppMigrateDown,
|
MedusaAppMigrateDown,
|
||||||
MedusaAppMigrateUp,
|
MedusaAppMigrateUp,
|
||||||
MedusaAppOutput,
|
MedusaAppOutput,
|
||||||
MedusaModule,
|
|
||||||
MODULE_PACKAGE_NAMES,
|
|
||||||
Modules,
|
|
||||||
ModulesDefinition,
|
ModulesDefinition,
|
||||||
} from "@medusajs/modules-sdk"
|
} from "@medusajs/modules-sdk"
|
||||||
import {
|
import {
|
||||||
@@ -188,29 +185,6 @@ export const loadMedusaApp = async (
|
|||||||
injectedDependencies,
|
injectedDependencies,
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO: Remove this and make it more dynamic on ensuring all modules are loaded.
|
|
||||||
const requiredModuleKeys = [Modules.PRODUCT, Modules.PRICING]
|
|
||||||
|
|
||||||
const missingPackages: string[] = []
|
|
||||||
|
|
||||||
for (const requiredModuleKey of requiredModuleKeys) {
|
|
||||||
const isModuleInstalled = MedusaModule.isInstalled(requiredModuleKey)
|
|
||||||
|
|
||||||
if (!isModuleInstalled) {
|
|
||||||
missingPackages.push(
|
|
||||||
MODULE_PACKAGE_NAMES[requiredModuleKey] || requiredModuleKey
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (missingPackages.length) {
|
|
||||||
throw new Error(
|
|
||||||
`Medusa requires the following packages/module registration: (${missingPackages.join(
|
|
||||||
", "
|
|
||||||
)})`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!config.registerInContainer) {
|
if (!config.registerInContainer) {
|
||||||
return medusaApp
|
return medusaApp
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export default async ({
|
|||||||
|
|
||||||
// After a user has authenticated a JWT will be placed on a cookie, all
|
// After a user has authenticated a JWT will be placed on a cookie, all
|
||||||
// calls will be authenticated based on the JWT
|
// calls will be authenticated based on the JWT
|
||||||
const { jwt_secret } = configModule.projectConfig
|
const { http } = configModule.projectConfig
|
||||||
passport.use(
|
passport.use(
|
||||||
"admin-session",
|
"admin-session",
|
||||||
new CustomStrategy(async (req, done) => {
|
new CustomStrategy(async (req, done) => {
|
||||||
@@ -97,7 +97,7 @@ export default async ({
|
|||||||
new JWTStrategy(
|
new JWTStrategy(
|
||||||
{
|
{
|
||||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||||
secretOrKey: jwt_secret,
|
secretOrKey: http.jwtSecret,
|
||||||
},
|
},
|
||||||
(token, done) => {
|
(token, done) => {
|
||||||
if (token.domain !== "admin") {
|
if (token.domain !== "admin") {
|
||||||
@@ -121,7 +121,7 @@ export default async ({
|
|||||||
new JWTStrategy(
|
new JWTStrategy(
|
||||||
{
|
{
|
||||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||||
secretOrKey: jwt_secret,
|
secretOrKey: http.jwtSecret,
|
||||||
},
|
},
|
||||||
(token, done) => {
|
(token, done) => {
|
||||||
if (token.domain !== "store") {
|
if (token.domain !== "store") {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { PaymentWebhookEvents } from "@medusajs/utils"
|
|
||||||
import { IPaymentModuleService, ProviderWebhookPayload } from "@medusajs/types"
|
|
||||||
import { SubscriberArgs, SubscriberConfig } from "../types/subscribers"
|
|
||||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||||
|
import { IPaymentModuleService, ProviderWebhookPayload } from "@medusajs/types"
|
||||||
|
import { PaymentWebhookEvents } from "@medusajs/utils"
|
||||||
|
import { SubscriberArgs, SubscriberConfig } from "../types/subscribers"
|
||||||
|
|
||||||
type SerializedBuffer = {
|
type SerializedBuffer = {
|
||||||
data: ArrayBuffer
|
data: ArrayBuffer
|
||||||
|
|||||||
@@ -1,13 +1,8 @@
|
|||||||
import { Request, Response, NextFunction } from "express"
|
import { HttpCompressionOptions, ProjectConfigOptions } from "@medusajs/types"
|
||||||
import compression from "compression"
|
import compression from "compression"
|
||||||
import { Logger } from "@medusajs/types"
|
import { Request, Response } from "express"
|
||||||
import {
|
|
||||||
ProjectConfigOptions,
|
|
||||||
HttpCompressionOptions,
|
|
||||||
} from "@medusajs/types"
|
|
||||||
|
|
||||||
export function shouldCompressResponse(req: Request, res: Response) {
|
export function shouldCompressResponse(req: Request, res: Response) {
|
||||||
const logger: Logger = req.scope.resolve("logger")
|
|
||||||
const { projectConfig } = req.scope.resolve("configModule")
|
const { projectConfig } = req.scope.resolve("configModule")
|
||||||
const { enabled } = compressionOptions(projectConfig)
|
const { enabled } = compressionOptions(projectConfig)
|
||||||
|
|
||||||
@@ -27,9 +22,10 @@ export function shouldCompressResponse(req: Request, res: Response) {
|
|||||||
export function compressionOptions(
|
export function compressionOptions(
|
||||||
config: ProjectConfigOptions
|
config: ProjectConfigOptions
|
||||||
): HttpCompressionOptions {
|
): HttpCompressionOptions {
|
||||||
const responseCompressionOptions = config.http_compression ?? {}
|
const responseCompressionOptions = config.http.compression ?? {}
|
||||||
|
|
||||||
responseCompressionOptions.enabled = responseCompressionOptions.enabled ?? false
|
responseCompressionOptions.enabled =
|
||||||
|
responseCompressionOptions.enabled ?? false
|
||||||
responseCompressionOptions.level = responseCompressionOptions.level ?? 6
|
responseCompressionOptions.level = responseCompressionOptions.level ?? 6
|
||||||
responseCompressionOptions.memLevel = responseCompressionOptions.memLevel ?? 8
|
responseCompressionOptions.memLevel = responseCompressionOptions.memLevel ?? 8
|
||||||
responseCompressionOptions.threshold =
|
responseCompressionOptions.threshold =
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||||
import { ApiKeyDTO, AuthUserDTO, IApiKeyModuleService } from "@medusajs/types"
|
import {
|
||||||
|
ApiKeyDTO,
|
||||||
|
AuthUserDTO,
|
||||||
|
ConfigModule,
|
||||||
|
IApiKeyModuleService,
|
||||||
|
} from "@medusajs/types"
|
||||||
import { stringEqualsOrRegexMatch } from "@medusajs/utils"
|
import { stringEqualsOrRegexMatch } from "@medusajs/utils"
|
||||||
import { NextFunction, RequestHandler } from "express"
|
import { NextFunction, RequestHandler } from "express"
|
||||||
import jwt, { JwtPayload } from "jsonwebtoken"
|
import jwt, { JwtPayload } from "jsonwebtoken"
|
||||||
@@ -55,10 +60,11 @@ export const authenticate = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (!authUser) {
|
if (!authUser) {
|
||||||
const { jwt_secret } = req.scope.resolve("configModule").projectConfig
|
const { http } =
|
||||||
|
req.scope.resolve<ConfigModule>("configModule").projectConfig
|
||||||
authUser = getAuthUserFromJwtToken(
|
authUser = getAuthUserFromJwtToken(
|
||||||
req.headers.authorization,
|
req.headers.authorization,
|
||||||
jwt_secret,
|
http.jwtSecret!,
|
||||||
authTypes,
|
authTypes,
|
||||||
authScope
|
authScope
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import {
|
|||||||
import { AuthUserService } from "@services"
|
import { AuthUserService } from "@services"
|
||||||
import Scrypt from "scrypt-kdf"
|
import Scrypt from "scrypt-kdf"
|
||||||
|
|
||||||
|
const EXPIRATION = "1d"
|
||||||
|
|
||||||
class EmailPasswordProvider extends AbstractAuthModuleProvider {
|
class EmailPasswordProvider extends AbstractAuthModuleProvider {
|
||||||
public static PROVIDER = "emailpass"
|
public static PROVIDER = "emailpass"
|
||||||
public static DISPLAY_NAME = "Email/Password Authentication"
|
public static DISPLAY_NAME = "Email/Password Authentication"
|
||||||
|
|||||||
Reference in new issue
Block a user