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:
6
.changeset/new-zebras-turn.md
Normal file
6
.changeset/new-zebras-turn.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@medusajs/medusa": patch
|
||||
"medusa-core-utils": patch
|
||||
---
|
||||
|
||||
jwt fix
|
||||
@@ -8,6 +8,7 @@ export const MedusaErrorTypes = {
|
||||
DUPLICATE_ERROR: "duplicate_error",
|
||||
INVALID_ARGUMENT: "invalid_argument",
|
||||
INVALID_DATA: "invalid_data",
|
||||
UNAUTHORIZED: "unauthorized",
|
||||
NOT_FOUND: "not_found",
|
||||
NOT_ALLOWED: "not_allowed",
|
||||
UNEXPECTED_STATE: "unexpected_state",
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import { NextFunction, Request, RequestHandler, Response } from "express"
|
||||
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 => {
|
||||
return (req: Request, res: Response, next: NextFunction): void => {
|
||||
passport.authenticate(
|
||||
["jwt", "bearer"],
|
||||
["store-jwt", "bearer"],
|
||||
{ session: false },
|
||||
(err, user) => {
|
||||
if (err) {
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import { NextFunction, Request, RequestHandler, Response } from "express"
|
||||
import passport from "passport"
|
||||
import { Request, Response, NextFunction, RequestHandler } from "express"
|
||||
|
||||
export default (): RequestHandler => {
|
||||
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 =
|
||||
"The request conflicted with another request. You may retry the request with the provided Idempotency-Key."
|
||||
break
|
||||
case MedusaError.Types.UNAUTHORIZED:
|
||||
statusCode = 401
|
||||
break
|
||||
case MedusaError.Types.DUPLICATE_ERROR:
|
||||
statusCode = 422
|
||||
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 normalizeQuery } from "./normalized-query"
|
||||
import { default as authenticateCustomer } from "./authenticate-customer"
|
||||
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 { getRequestedBatchJob } from "./batch-job/get-requested-batch-job"
|
||||
export { doesConditionBelongToDiscount } from "./discount/does-condition-belong-to-discount"
|
||||
export { transformQuery } from "./transform-query"
|
||||
export { transformBody } from "./transform-body"
|
||||
export { transformQuery } from "./transform-query"
|
||||
|
||||
export default {
|
||||
authenticate,
|
||||
authenticateCustomer,
|
||||
requireCustomerAuthentication,
|
||||
normalizeQuery,
|
||||
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 AuthService from "../../../../services/auth"
|
||||
import { EntityManager } from "typeorm"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import _ from "lodash"
|
||||
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"
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { IsEmail, IsNotEmpty } from "class-validator"
|
||||
import jwt from "jsonwebtoken"
|
||||
import { EntityManager } from "typeorm"
|
||||
import AuthService from "../../../../services/auth"
|
||||
import CustomerService from "../../../../services/customer"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
import { EntityManager } from "typeorm"
|
||||
|
||||
/**
|
||||
* @oas [post] /auth
|
||||
@@ -79,7 +79,7 @@ export default async (req, res) => {
|
||||
const {
|
||||
projectConfig: { jwt_secret },
|
||||
} = req.scope.resolve("configModule")
|
||||
req.session.jwt = jwt.sign(
|
||||
req.session.jwt_store = jwt.sign(
|
||||
{ customer_id: result.customer?.id },
|
||||
jwt_secret!,
|
||||
{
|
||||
|
||||
@@ -31,6 +31,6 @@
|
||||
* $ref: "#/components/responses/500_error"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
req.session.jwt = {}
|
||||
req.session.jwt_store = {}
|
||||
res.json({})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Router } from "express"
|
||||
import { Customer } from "./../../../.."
|
||||
import middlewares from "../../../middlewares"
|
||||
import { Customer } from "./../../../.."
|
||||
|
||||
const route = Router()
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Type } from "class-transformer"
|
||||
import { ValidateNested } from "class-validator"
|
||||
import { EntityManager } from "typeorm"
|
||||
import { defaultStoreCustomersFields, defaultStoreCustomersRelations } from "."
|
||||
import CustomerService from "../../../../services/customer"
|
||||
import { AddressCreatePayload } from "../../../../types/common"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
import { EntityManager } from "typeorm"
|
||||
|
||||
/**
|
||||
* @oas [post] /customers/me/addresses
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { IsEmail, IsOptional, IsString } from "class-validator"
|
||||
import { defaultStoreCustomersFields, defaultStoreCustomersRelations } from "."
|
||||
|
||||
import jwt from "jsonwebtoken"
|
||||
import { EntityManager } from "typeorm"
|
||||
import { Customer } from "../../../.."
|
||||
import CustomerService from "../../../../services/customer"
|
||||
import jwt from "jsonwebtoken"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
import { EntityManager } from "typeorm"
|
||||
|
||||
/**
|
||||
* @oas [post] /customers
|
||||
@@ -121,7 +121,7 @@ export default async (req, res) => {
|
||||
const {
|
||||
projectConfig: { jwt_secret },
|
||||
} = 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",
|
||||
})
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { defaultStoreCustomersFields, defaultStoreCustomersRelations } from "."
|
||||
|
||||
import CustomerService from "../../../../services/customer"
|
||||
import { EntityManager } from "typeorm"
|
||||
import CustomerService from "../../../../services/customer"
|
||||
|
||||
/**
|
||||
* @oas [delete] /customers/me/addresses/{address_id}
|
||||
|
||||
@@ -2,11 +2,11 @@ import { Router } from "express"
|
||||
import { Customer, Order } from "../../../.."
|
||||
import { PaginatedResponse } from "../../../../types/common"
|
||||
import middlewares, { transformQuery } from "../../../middlewares"
|
||||
import { StoreGetCustomersCustomerOrdersParams } from "./list-orders"
|
||||
import {
|
||||
defaultStoreOrdersRelations,
|
||||
defaultStoreOrdersFields,
|
||||
defaultStoreOrdersRelations,
|
||||
} from "../orders"
|
||||
import { StoreGetCustomersCustomerOrdersParams } from "./list-orders"
|
||||
|
||||
const route = Router()
|
||||
|
||||
@@ -34,7 +34,7 @@ export default (app, container) => {
|
||||
)
|
||||
|
||||
// Authenticated endpoints
|
||||
route.use(middlewares.authenticate())
|
||||
route.use(middlewares.requireCustomerAuthentication())
|
||||
|
||||
route.get("/me", middlewares.wrap(require("./get-customer").default))
|
||||
route.post("/me", middlewares.wrap(require("./update-customer").default))
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
import {
|
||||
FulfillmentStatus,
|
||||
OrderStatus,
|
||||
PaymentStatus,
|
||||
} from "../../../../models/order"
|
||||
import {
|
||||
IsEnum,
|
||||
IsNumber,
|
||||
@@ -11,11 +6,15 @@ import {
|
||||
ValidateNested,
|
||||
} from "class-validator"
|
||||
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 OrderService from "../../../../services/order"
|
||||
import { DateComparisonOperator } from "../../../../types/common"
|
||||
|
||||
/**
|
||||
* @oas [get] /customers/me/orders
|
||||
@@ -194,13 +193,6 @@ import { Type } from "class-transformer"
|
||||
export default async (req: Request, res: Response) => {
|
||||
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")
|
||||
|
||||
req.filterableFields = {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { defaultStoreCustomersFields, defaultStoreCustomersRelations } from "."
|
||||
|
||||
import { AddressPayload } from "../../../../types/common"
|
||||
import CustomerService from "../../../../services/customer"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
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}
|
||||
@@ -69,6 +69,7 @@ import { EntityManager } from "typeorm"
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const id = req.user.customer_id
|
||||
|
||||
const { address_id } = req.params
|
||||
|
||||
const validated = await validator(
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { IsEmail, IsObject, IsOptional, IsString } from "class-validator"
|
||||
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 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
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import cors from "cors"
|
||||
import { Router } from "express"
|
||||
import middlewares from "../../middlewares"
|
||||
import productTypesRoutes from "../admin/product-types"
|
||||
import authRoutes from "./auth"
|
||||
import cartRoutes from "./carts"
|
||||
import collectionRoutes from "./collections"
|
||||
import customerRoutes from "./customers"
|
||||
import giftCardRoutes from "./gift-cards"
|
||||
import orderRoutes from "./orders"
|
||||
import orderEditRoutes from "./order-edits"
|
||||
import orderRoutes from "./orders"
|
||||
import productRoutes from "./products"
|
||||
import productTypesRoutes from "../admin/product-types"
|
||||
import regionRoutes from "./regions"
|
||||
import returnReasonRoutes from "./return-reasons"
|
||||
import returnRoutes from "./returns"
|
||||
|
||||
@@ -99,7 +99,7 @@ export async function request(method, url, opts = {}) {
|
||||
}
|
||||
if (opts.clientSession) {
|
||||
if (opts.clientSession.jwt) {
|
||||
opts.clientSession.jwt = jwt.sign(
|
||||
opts.clientSession.jwt_store = jwt.sign(
|
||||
opts.clientSession.jwt,
|
||||
config.projectConfig.jwt_secret,
|
||||
{
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import passport from "passport"
|
||||
import { AuthService } from "../services"
|
||||
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 JWTStrategy } from "passport-jwt"
|
||||
import { Strategy as LocalStrategy } from "passport-local"
|
||||
import { AuthService } from "../services"
|
||||
import { ConfigModule, MedusaContainer } from "../types/global"
|
||||
|
||||
export default async ({
|
||||
app,
|
||||
@@ -46,6 +46,7 @@ export default async ({
|
||||
// calls will be authenticated based on the JWT
|
||||
const { jwt_secret } = configModule.projectConfig
|
||||
passport.use(
|
||||
"admin-jwt",
|
||||
new JWTStrategy(
|
||||
{
|
||||
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
|
||||
passport.use(
|
||||
new BearerStrategy(async (token, done) => {
|
||||
|
||||
Reference in New Issue
Block a user