fix(medusa): Separate JWT auth strategies per domain (#2646)
**What** Separate JWT auth strategies per domain Co-authored-by: Oliver Windall Juhl <59018053+olivermrbl@users.noreply.github.com> Co-authored-by: Adrien de Peretti <25098370+adrien2p@users.noreply.github.com>
This commit is contained in:
21 files changed
+90
-52
No files matched your search
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/medusa": patch
|
||||||
|
"medusa-core-utils": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
jwt fix
|
||||||
@@ -8,6 +8,7 @@ export const MedusaErrorTypes = {
|
|||||||
DUPLICATE_ERROR: "duplicate_error",
|
DUPLICATE_ERROR: "duplicate_error",
|
||||||
INVALID_ARGUMENT: "invalid_argument",
|
INVALID_ARGUMENT: "invalid_argument",
|
||||||
INVALID_DATA: "invalid_data",
|
INVALID_DATA: "invalid_data",
|
||||||
|
UNAUTHORIZED: "unauthorized",
|
||||||
NOT_FOUND: "not_found",
|
NOT_FOUND: "not_found",
|
||||||
NOT_ALLOWED: "not_allowed",
|
NOT_ALLOWED: "not_allowed",
|
||||||
UNEXPECTED_STATE: "unexpected_state",
|
UNEXPECTED_STATE: "unexpected_state",
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
|
import { NextFunction, Request, RequestHandler, Response } from "express"
|
||||||
import passport from "passport"
|
import passport from "passport"
|
||||||
import { Request, Response, NextFunction, RequestHandler } from "express"
|
|
||||||
|
|
||||||
|
// Optional customer authentication
|
||||||
|
// If authenticated, middleware attaches customer to request (as user) otherwise we pass through
|
||||||
|
// If you want to require authentication, use `requireCustomerAuthentication` in `packages/medusa/src/api/middlewares/require-customer-authentication.ts`
|
||||||
export default (): RequestHandler => {
|
export default (): RequestHandler => {
|
||||||
return (req: Request, res: Response, next: NextFunction): void => {
|
return (req: Request, res: Response, next: NextFunction): void => {
|
||||||
passport.authenticate(
|
passport.authenticate(
|
||||||
["jwt", "bearer"],
|
["store-jwt", "bearer"],
|
||||||
{ session: false },
|
{ session: false },
|
||||||
(err, user) => {
|
(err, user) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
|
import { NextFunction, Request, RequestHandler, Response } from "express"
|
||||||
import passport from "passport"
|
import passport from "passport"
|
||||||
import { Request, Response, NextFunction, RequestHandler } from "express"
|
|
||||||
|
|
||||||
export default (): RequestHandler => {
|
export default (): RequestHandler => {
|
||||||
return (req: Request, res: Response, next: NextFunction): void => {
|
return (req: Request, res: Response, next: NextFunction): void => {
|
||||||
passport.authenticate(["jwt", "bearer"], { session: false })(req, res, next)
|
passport.authenticate(["admin-jwt", "bearer"], { session: false })(
|
||||||
|
req,
|
||||||
|
res,
|
||||||
|
next
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -43,6 +43,9 @@ export default () => {
|
|||||||
errObj.message =
|
errObj.message =
|
||||||
"The request conflicted with another request. You may retry the request with the provided Idempotency-Key."
|
"The request conflicted with another request. You may retry the request with the provided Idempotency-Key."
|
||||||
break
|
break
|
||||||
|
case MedusaError.Types.UNAUTHORIZED:
|
||||||
|
statusCode = 401
|
||||||
|
break
|
||||||
case MedusaError.Types.DUPLICATE_ERROR:
|
case MedusaError.Types.DUPLICATE_ERROR:
|
||||||
statusCode = 422
|
statusCode = 422
|
||||||
errObj.code = INVALID_REQUEST_ERROR
|
errObj.code = INVALID_REQUEST_ERROR
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
import { default as authenticateCustomer } from "./authenticate-customer"
|
|
||||||
import { default as authenticate } from "./authenticate"
|
import { default as authenticate } from "./authenticate"
|
||||||
import { default as normalizeQuery } from "./normalized-query"
|
import { default as authenticateCustomer } from "./authenticate-customer"
|
||||||
import { default as wrap } from "./await-middleware"
|
import { default as wrap } from "./await-middleware"
|
||||||
|
import { default as normalizeQuery } from "./normalized-query"
|
||||||
|
import { default as requireCustomerAuthentication } from "./require-customer-authentication"
|
||||||
|
|
||||||
export { getRequestedBatchJob } from "./batch-job/get-requested-batch-job"
|
|
||||||
export { canAccessBatchJob } from "./batch-job/can-access-batch-job"
|
export { canAccessBatchJob } from "./batch-job/can-access-batch-job"
|
||||||
|
export { getRequestedBatchJob } from "./batch-job/get-requested-batch-job"
|
||||||
export { doesConditionBelongToDiscount } from "./discount/does-condition-belong-to-discount"
|
export { doesConditionBelongToDiscount } from "./discount/does-condition-belong-to-discount"
|
||||||
export { transformQuery } from "./transform-query"
|
|
||||||
export { transformBody } from "./transform-body"
|
export { transformBody } from "./transform-body"
|
||||||
|
export { transformQuery } from "./transform-query"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
authenticate,
|
authenticate,
|
||||||
authenticateCustomer,
|
authenticateCustomer,
|
||||||
|
requireCustomerAuthentication,
|
||||||
normalizeQuery,
|
normalizeQuery,
|
||||||
wrap,
|
wrap,
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { NextFunction, Request, RequestHandler, Response } from "express"
|
||||||
|
import passport from "passport"
|
||||||
|
|
||||||
|
export default (): RequestHandler => {
|
||||||
|
return (req: Request, res: Response, next: NextFunction): void => {
|
||||||
|
passport.authenticate(["store-jwt", "bearer"], { session: false })(
|
||||||
|
req,
|
||||||
|
res,
|
||||||
|
next
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import { IsEmail, IsNotEmpty, IsString } from "class-validator"
|
import { IsEmail, IsNotEmpty, IsString } from "class-validator"
|
||||||
|
|
||||||
import AuthService from "../../../../services/auth"
|
|
||||||
import { EntityManager } from "typeorm"
|
|
||||||
import { MedusaError } from "medusa-core-utils"
|
|
||||||
import _ from "lodash"
|
|
||||||
import jwt from "jsonwebtoken"
|
import jwt from "jsonwebtoken"
|
||||||
|
import _ from "lodash"
|
||||||
|
import { MedusaError } from "medusa-core-utils"
|
||||||
|
import { EntityManager } from "typeorm"
|
||||||
|
import AuthService from "../../../../services/auth"
|
||||||
import { validator } from "../../../../utils/validator"
|
import { validator } from "../../../../utils/validator"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { IsEmail, IsNotEmpty } from "class-validator"
|
import { IsEmail, IsNotEmpty } from "class-validator"
|
||||||
import jwt from "jsonwebtoken"
|
import jwt from "jsonwebtoken"
|
||||||
|
import { EntityManager } from "typeorm"
|
||||||
import AuthService from "../../../../services/auth"
|
import AuthService from "../../../../services/auth"
|
||||||
import CustomerService from "../../../../services/customer"
|
import CustomerService from "../../../../services/customer"
|
||||||
import { validator } from "../../../../utils/validator"
|
import { validator } from "../../../../utils/validator"
|
||||||
import { EntityManager } from "typeorm"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [post] /auth
|
* @oas [post] /auth
|
||||||
@@ -79,7 +79,7 @@ export default async (req, res) => {
|
|||||||
const {
|
const {
|
||||||
projectConfig: { jwt_secret },
|
projectConfig: { jwt_secret },
|
||||||
} = req.scope.resolve("configModule")
|
} = req.scope.resolve("configModule")
|
||||||
req.session.jwt = jwt.sign(
|
req.session.jwt_store = jwt.sign(
|
||||||
{ customer_id: result.customer?.id },
|
{ customer_id: result.customer?.id },
|
||||||
jwt_secret!,
|
jwt_secret!,
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -31,6 +31,6 @@
|
|||||||
* $ref: "#/components/responses/500_error"
|
* $ref: "#/components/responses/500_error"
|
||||||
*/
|
*/
|
||||||
export default async (req, res) => {
|
export default async (req, res) => {
|
||||||
req.session.jwt = {}
|
req.session.jwt_store = {}
|
||||||
res.json({})
|
res.json({})
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Router } from "express"
|
import { Router } from "express"
|
||||||
import { Customer } from "./../../../.."
|
|
||||||
import middlewares from "../../../middlewares"
|
import middlewares from "../../../middlewares"
|
||||||
|
import { Customer } from "./../../../.."
|
||||||
|
|
||||||
const route = Router()
|
const route = Router()
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { Type } from "class-transformer"
|
import { Type } from "class-transformer"
|
||||||
import { ValidateNested } from "class-validator"
|
import { ValidateNested } from "class-validator"
|
||||||
|
import { EntityManager } from "typeorm"
|
||||||
import { defaultStoreCustomersFields, defaultStoreCustomersRelations } from "."
|
import { defaultStoreCustomersFields, defaultStoreCustomersRelations } from "."
|
||||||
import CustomerService from "../../../../services/customer"
|
import CustomerService from "../../../../services/customer"
|
||||||
import { AddressCreatePayload } from "../../../../types/common"
|
import { AddressCreatePayload } from "../../../../types/common"
|
||||||
import { validator } from "../../../../utils/validator"
|
import { validator } from "../../../../utils/validator"
|
||||||
import { EntityManager } from "typeorm"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [post] /customers/me/addresses
|
* @oas [post] /customers/me/addresses
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { IsEmail, IsOptional, IsString } from "class-validator"
|
import { IsEmail, IsOptional, IsString } from "class-validator"
|
||||||
import { defaultStoreCustomersFields, defaultStoreCustomersRelations } from "."
|
import { defaultStoreCustomersFields, defaultStoreCustomersRelations } from "."
|
||||||
|
|
||||||
|
import jwt from "jsonwebtoken"
|
||||||
|
import { EntityManager } from "typeorm"
|
||||||
import { Customer } from "../../../.."
|
import { Customer } from "../../../.."
|
||||||
import CustomerService from "../../../../services/customer"
|
import CustomerService from "../../../../services/customer"
|
||||||
import jwt from "jsonwebtoken"
|
|
||||||
import { validator } from "../../../../utils/validator"
|
import { validator } from "../../../../utils/validator"
|
||||||
import { EntityManager } from "typeorm"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [post] /customers
|
* @oas [post] /customers
|
||||||
@@ -121,7 +121,7 @@ export default async (req, res) => {
|
|||||||
const {
|
const {
|
||||||
projectConfig: { jwt_secret },
|
projectConfig: { jwt_secret },
|
||||||
} = req.scope.resolve("configModule")
|
} = req.scope.resolve("configModule")
|
||||||
req.session.jwt = jwt.sign({ customer_id: customer.id }, jwt_secret!, {
|
req.session.jwt_store = jwt.sign({ customer_id: customer.id }, jwt_secret!, {
|
||||||
expiresIn: "30d",
|
expiresIn: "30d",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { defaultStoreCustomersFields, defaultStoreCustomersRelations } from "."
|
import { defaultStoreCustomersFields, defaultStoreCustomersRelations } from "."
|
||||||
|
|
||||||
import CustomerService from "../../../../services/customer"
|
|
||||||
import { EntityManager } from "typeorm"
|
import { EntityManager } from "typeorm"
|
||||||
|
import CustomerService from "../../../../services/customer"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [delete] /customers/me/addresses/{address_id}
|
* @oas [delete] /customers/me/addresses/{address_id}
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ import { Router } from "express"
|
|||||||
import { Customer, Order } from "../../../.."
|
import { Customer, Order } from "../../../.."
|
||||||
import { PaginatedResponse } from "../../../../types/common"
|
import { PaginatedResponse } from "../../../../types/common"
|
||||||
import middlewares, { transformQuery } from "../../../middlewares"
|
import middlewares, { transformQuery } from "../../../middlewares"
|
||||||
import { StoreGetCustomersCustomerOrdersParams } from "./list-orders"
|
|
||||||
import {
|
import {
|
||||||
defaultStoreOrdersRelations,
|
|
||||||
defaultStoreOrdersFields,
|
defaultStoreOrdersFields,
|
||||||
|
defaultStoreOrdersRelations,
|
||||||
} from "../orders"
|
} from "../orders"
|
||||||
|
import { StoreGetCustomersCustomerOrdersParams } from "./list-orders"
|
||||||
|
|
||||||
const route = Router()
|
const route = Router()
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ export default (app, container) => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Authenticated endpoints
|
// Authenticated endpoints
|
||||||
route.use(middlewares.authenticate())
|
route.use(middlewares.requireCustomerAuthentication())
|
||||||
|
|
||||||
route.get("/me", middlewares.wrap(require("./get-customer").default))
|
route.get("/me", middlewares.wrap(require("./get-customer").default))
|
||||||
route.post("/me", middlewares.wrap(require("./update-customer").default))
|
route.post("/me", middlewares.wrap(require("./update-customer").default))
|
||||||
|
|||||||
@@ -1,8 +1,3 @@
|
|||||||
import {
|
|
||||||
FulfillmentStatus,
|
|
||||||
OrderStatus,
|
|
||||||
PaymentStatus,
|
|
||||||
} from "../../../../models/order"
|
|
||||||
import {
|
import {
|
||||||
IsEnum,
|
IsEnum,
|
||||||
IsNumber,
|
IsNumber,
|
||||||
@@ -11,11 +6,15 @@ import {
|
|||||||
ValidateNested,
|
ValidateNested,
|
||||||
} from "class-validator"
|
} from "class-validator"
|
||||||
import { Request, Response } from "express"
|
import { Request, Response } from "express"
|
||||||
|
import {
|
||||||
|
FulfillmentStatus,
|
||||||
|
OrderStatus,
|
||||||
|
PaymentStatus,
|
||||||
|
} from "../../../../models/order"
|
||||||
|
|
||||||
import { DateComparisonOperator } from "../../../../types/common"
|
|
||||||
import { MedusaError } from "medusa-core-utils"
|
|
||||||
import OrderService from "../../../../services/order"
|
|
||||||
import { Type } from "class-transformer"
|
import { Type } from "class-transformer"
|
||||||
|
import OrderService from "../../../../services/order"
|
||||||
|
import { DateComparisonOperator } from "../../../../types/common"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [get] /customers/me/orders
|
* @oas [get] /customers/me/orders
|
||||||
@@ -194,13 +193,6 @@ import { Type } from "class-transformer"
|
|||||||
export default async (req: Request, res: Response) => {
|
export default async (req: Request, res: Response) => {
|
||||||
const id: string | undefined = req.user?.customer_id
|
const id: string | undefined = req.user?.customer_id
|
||||||
|
|
||||||
if (!id) {
|
|
||||||
throw new MedusaError(
|
|
||||||
MedusaError.Types.UNEXPECTED_STATE,
|
|
||||||
"Not authorized to list orders"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const orderService: OrderService = req.scope.resolve("orderService")
|
const orderService: OrderService = req.scope.resolve("orderService")
|
||||||
|
|
||||||
req.filterableFields = {
|
req.filterableFields = {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { defaultStoreCustomersFields, defaultStoreCustomersRelations } from "."
|
import { defaultStoreCustomersFields, defaultStoreCustomersRelations } from "."
|
||||||
|
|
||||||
import { AddressPayload } from "../../../../types/common"
|
|
||||||
import CustomerService from "../../../../services/customer"
|
|
||||||
import { validator } from "../../../../utils/validator"
|
|
||||||
import { EntityManager } from "typeorm"
|
import { EntityManager } from "typeorm"
|
||||||
|
import CustomerService from "../../../../services/customer"
|
||||||
|
import { AddressPayload } from "../../../../types/common"
|
||||||
|
import { validator } from "../../../../utils/validator"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [post] /customers/me/addresses/{address_id}
|
* @oas [post] /customers/me/addresses/{address_id}
|
||||||
@@ -69,6 +69,7 @@ import { EntityManager } from "typeorm"
|
|||||||
*/
|
*/
|
||||||
export default async (req, res) => {
|
export default async (req, res) => {
|
||||||
const id = req.user.customer_id
|
const id = req.user.customer_id
|
||||||
|
|
||||||
const { address_id } = req.params
|
const { address_id } = req.params
|
||||||
|
|
||||||
const validated = await validator(
|
const validated = await validator(
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { IsEmail, IsObject, IsOptional, IsString } from "class-validator"
|
import { IsEmail, IsObject, IsOptional, IsString } from "class-validator"
|
||||||
import { defaultStoreCustomersFields, defaultStoreCustomersRelations } from "."
|
import { defaultStoreCustomersFields, defaultStoreCustomersRelations } from "."
|
||||||
|
|
||||||
import { AddressPayload } from "../../../../types/common"
|
|
||||||
import CustomerService from "../../../../services/customer"
|
|
||||||
import { IsType } from "../../../../utils/validators/is-type"
|
|
||||||
import { validator } from "../../../../utils/validator"
|
|
||||||
import { EntityManager } from "typeorm"
|
import { EntityManager } from "typeorm"
|
||||||
|
import CustomerService from "../../../../services/customer"
|
||||||
|
import { AddressPayload } from "../../../../types/common"
|
||||||
|
import { validator } from "../../../../utils/validator"
|
||||||
|
import { IsType } from "../../../../utils/validators/is-type"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @oas [post] /customers/me
|
* @oas [post] /customers/me
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import cors from "cors"
|
import cors from "cors"
|
||||||
import { Router } from "express"
|
import { Router } from "express"
|
||||||
import middlewares from "../../middlewares"
|
import middlewares from "../../middlewares"
|
||||||
|
import productTypesRoutes from "../admin/product-types"
|
||||||
import authRoutes from "./auth"
|
import authRoutes from "./auth"
|
||||||
import cartRoutes from "./carts"
|
import cartRoutes from "./carts"
|
||||||
import collectionRoutes from "./collections"
|
import collectionRoutes from "./collections"
|
||||||
import customerRoutes from "./customers"
|
import customerRoutes from "./customers"
|
||||||
import giftCardRoutes from "./gift-cards"
|
import giftCardRoutes from "./gift-cards"
|
||||||
import orderRoutes from "./orders"
|
|
||||||
import orderEditRoutes from "./order-edits"
|
import orderEditRoutes from "./order-edits"
|
||||||
|
import orderRoutes from "./orders"
|
||||||
import productRoutes from "./products"
|
import productRoutes from "./products"
|
||||||
import productTypesRoutes from "../admin/product-types"
|
|
||||||
import regionRoutes from "./regions"
|
import regionRoutes from "./regions"
|
||||||
import returnReasonRoutes from "./return-reasons"
|
import returnReasonRoutes from "./return-reasons"
|
||||||
import returnRoutes from "./returns"
|
import returnRoutes from "./returns"
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ export async function request(method, url, opts = {}) {
|
|||||||
}
|
}
|
||||||
if (opts.clientSession) {
|
if (opts.clientSession) {
|
||||||
if (opts.clientSession.jwt) {
|
if (opts.clientSession.jwt) {
|
||||||
opts.clientSession.jwt = jwt.sign(
|
opts.clientSession.jwt_store = jwt.sign(
|
||||||
opts.clientSession.jwt,
|
opts.clientSession.jwt,
|
||||||
config.projectConfig.jwt_secret,
|
config.projectConfig.jwt_secret,
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import passport from "passport"
|
|
||||||
import { AuthService } from "../services"
|
|
||||||
import { Express } from "express"
|
import { Express } from "express"
|
||||||
import { ConfigModule, MedusaContainer } from "../types/global"
|
import passport from "passport"
|
||||||
import { Strategy as BearerStrategy } from "passport-http-bearer"
|
import { Strategy as BearerStrategy } from "passport-http-bearer"
|
||||||
import { Strategy as JWTStrategy } from "passport-jwt"
|
import { Strategy as JWTStrategy } from "passport-jwt"
|
||||||
import { Strategy as LocalStrategy } from "passport-local"
|
import { Strategy as LocalStrategy } from "passport-local"
|
||||||
|
import { AuthService } from "../services"
|
||||||
|
import { ConfigModule, MedusaContainer } from "../types/global"
|
||||||
|
|
||||||
export default async ({
|
export default async ({
|
||||||
app,
|
app,
|
||||||
@@ -46,6 +46,7 @@ export default async ({
|
|||||||
// calls will be authenticated based on the JWT
|
// calls will be authenticated based on the JWT
|
||||||
const { jwt_secret } = configModule.projectConfig
|
const { jwt_secret } = configModule.projectConfig
|
||||||
passport.use(
|
passport.use(
|
||||||
|
"admin-jwt",
|
||||||
new JWTStrategy(
|
new JWTStrategy(
|
||||||
{
|
{
|
||||||
jwtFromRequest: (req) => req.session.jwt,
|
jwtFromRequest: (req) => req.session.jwt,
|
||||||
@@ -57,6 +58,19 @@ export default async ({
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
passport.use(
|
||||||
|
"store-jwt",
|
||||||
|
new JWTStrategy(
|
||||||
|
{
|
||||||
|
jwtFromRequest: (req) => req.session.jwt_store,
|
||||||
|
secretOrKey: jwt_secret,
|
||||||
|
},
|
||||||
|
async (jwtPayload, done) => {
|
||||||
|
return done(null, jwtPayload)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
// Alternatively use bearer token to authenticate to the admin api
|
// Alternatively use bearer token to authenticate to the admin api
|
||||||
passport.use(
|
passport.use(
|
||||||
new BearerStrategy(async (token, done) => {
|
new BearerStrategy(async (token, done) => {
|
||||||
|
|||||||
Reference in new issue
Block a user