Revamp the authentication setup (#7419)

* feat: Add email pass authentication provider package

* feat: Revamp auth module and remove concept of scope

* feat: Revamp the auth module to be more standardized in how providers are loaded

* feat: Switch from scope to actor type for authentication

* feat: Add support for per-actor auth methods

* feat: Add emailpass auth provider by default

* fix: Add back app_metadata in auth module
This commit is contained in:
Stevche Radevski authored and GitHub committed 2024-05-23 20:56:40 +02:00
1 parent 7b0cfe3b77
commit 8a070d5d85
100 files changed
+991 -1005

No files matched your search

@@ -16,7 +16,7 @@ import { createLinkBody } from "../../utils/validators"
export const adminApiKeyRoutesMiddlewares: MiddlewareRoute[] = [
{
matcher: "/admin/api-keys*",
middlewares: [authenticate("admin", ["bearer", "session"])],
middlewares: [authenticate("user", ["bearer", "session"])],
},
{
method: ["GET"],
@@ -14,7 +14,7 @@ import {
export const adminCampaignRoutesMiddlewares: MiddlewareRoute[] = [
{
matcher: "/admin/campaigns*",
middlewares: [authenticate("admin", ["bearer", "session", "api-key"])],
middlewares: [authenticate("user", ["bearer", "session", "api-key"])],
},
{
method: ["GET"],
@@ -15,7 +15,7 @@ export const adminCollectionRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["ALL"],
matcher: "/admin/collections*",
middlewares: [authenticate("admin", ["bearer", "session", "api-key"])],
middlewares: [authenticate("user", ["bearer", "session", "api-key"])],
},
{
@@ -8,7 +8,7 @@ export const adminCurrencyRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["ALL"],
matcher: "/admin/currencies*",
middlewares: [authenticate("admin", ["bearer", "session", "api-key"])],
middlewares: [authenticate("user", ["bearer", "session", "api-key"])],
},
{
method: ["GET"],
@@ -15,7 +15,7 @@ export const adminCustomerGroupRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["ALL"],
matcher: "/admin/customer-groups*",
middlewares: [authenticate("admin", ["bearer", "session", "api-key"])],
middlewares: [authenticate("user", ["bearer", "session", "api-key"])],
},
{
method: ["GET"],
@@ -19,7 +19,7 @@ export const adminCustomerRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["ALL"],
matcher: "/admin/customers*",
middlewares: [authenticate("admin", ["bearer", "session", "api-key"])],
middlewares: [authenticate("user", ["bearer", "session", "api-key"])],
},
{
method: ["GET"],
@@ -13,7 +13,7 @@ export const adminDraftOrderRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["ALL"],
matcher: "/admin/draft-orders*",
middlewares: [authenticate("admin", ["bearer", "session", "api-key"])],
middlewares: [authenticate("user", ["bearer", "session", "api-key"])],
},
{
method: ["GET"],
@@ -8,7 +8,7 @@ export const adminFulfillmentProvidersRoutesMiddlewares: MiddlewareRoute[] = [
{
method: "ALL",
matcher: "/admin/fulfillment-providers*",
middlewares: [authenticate("admin", ["session", "bearer", "api-key"])],
middlewares: [authenticate("user", ["session", "bearer", "api-key"])],
},
{
method: ["GET"],
@@ -14,7 +14,7 @@ export const adminFulfillmentSetsRoutesMiddlewares: MiddlewareRoute[] = [
{
method: "ALL",
matcher: "/admin/fulfillment-sets*",
middlewares: [authenticate("admin", ["session", "bearer", "api-key"])],
middlewares: [authenticate("user", ["session", "bearer", "api-key"])],
},
{
method: ["POST"],
@@ -14,7 +14,7 @@ export const adminFulfillmentsRoutesMiddlewares: MiddlewareRoute[] = [
{
method: "ALL",
matcher: "/admin/fulfillments*",
middlewares: [authenticate("admin", ["session", "bearer", "api-key"])],
middlewares: [authenticate("user", ["session", "bearer", "api-key"])],
},
{
method: ["POST"],
@@ -20,7 +20,7 @@ export const adminInventoryRoutesMiddlewares: MiddlewareRoute[] = [
{
method: "ALL",
matcher: "/admin/inventory-items*",
middlewares: [authenticate("admin", ["session", "bearer", "api-key"])],
middlewares: [authenticate("user", ["session", "bearer", "api-key"])],
},
{
method: ["GET"],
@@ -18,7 +18,7 @@ export const adminInviteRoutesMiddlewares: MiddlewareRoute[] = [
method: ["GET"],
matcher: "/admin/invites",
middlewares: [
authenticate("admin", ["session", "bearer", "api-key"]),
authenticate("user", ["session", "bearer", "api-key"]),
validateAndTransformQuery(
AdminGetInvitesParams,
QueryConfig.listTransformQueryConfig
@@ -29,7 +29,7 @@ export const adminInviteRoutesMiddlewares: MiddlewareRoute[] = [
method: ["POST"],
matcher: "/admin/invites",
middlewares: [
authenticate("admin", ["session", "bearer", "api-key"]),
authenticate("user", ["session", "bearer", "api-key"]),
validateAndTransformBody(AdminCreateInvite),
validateAndTransformQuery(
AdminGetInviteParams,
@@ -41,7 +41,7 @@ export const adminInviteRoutesMiddlewares: MiddlewareRoute[] = [
method: "POST",
matcher: "/admin/invites/accept",
middlewares: [
authenticate("admin", ["session", "bearer"], {
authenticate("user", ["session", "bearer"], {
allowUnregistered: true,
}),
validateAndTransformBody(AdminInviteAccept),
@@ -55,7 +55,7 @@ export const adminInviteRoutesMiddlewares: MiddlewareRoute[] = [
method: ["GET"],
matcher: "/admin/invites/:id",
middlewares: [
authenticate("admin", ["session", "bearer", "api-key"]),
authenticate("user", ["session", "bearer", "api-key"]),
validateAndTransformQuery(
AdminGetInviteParams,
QueryConfig.retrieveTransformQueryConfig
@@ -65,13 +65,13 @@ export const adminInviteRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["DELETE"],
matcher: "/admin/invites/:id",
middlewares: [authenticate("admin", ["session", "bearer", "api-key"])],
middlewares: [authenticate("user", ["session", "bearer", "api-key"])],
},
{
method: "POST",
matcher: "/admin/invites/:id/resend",
middlewares: [
authenticate("admin", ["session", "bearer", "api-key"]),
authenticate("user", ["session", "bearer", "api-key"]),
validateAndTransformQuery(
AdminGetInviteParams,
QueryConfig.retrieveTransformQueryConfig
@@ -14,7 +14,7 @@ export const adminOrderRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["ALL"],
matcher: "/admin/orders*",
middlewares: [authenticate("admin", ["bearer", "session", "api-key"])],
middlewares: [authenticate("user", ["bearer", "session", "api-key"])],
},
{
method: ["GET"],
@@ -16,7 +16,7 @@ export const adminPaymentRoutesMiddlewares: MiddlewareRoute[] = [
{
method: "ALL",
matcher: "/admin/payments",
middlewares: [authenticate("admin", ["session", "bearer", "api-key"])],
middlewares: [authenticate("user", ["session", "bearer", "api-key"])],
},
{
method: ["GET"],
@@ -18,7 +18,7 @@ export const adminPriceListsRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["ALL"],
matcher: "/admin/price-lists*",
middlewares: [authenticate("admin", ["bearer", "session", "api-key"])],
middlewares: [authenticate("user", ["bearer", "session", "api-key"])],
},
{
method: ["GET"],
@@ -13,7 +13,7 @@ import {
export const adminPricingRoutesMiddlewares: MiddlewareRoute[] = [
{
matcher: "/admin/pricing*",
middlewares: [authenticate("admin", ["bearer", "session", "api-key"])],
middlewares: [authenticate("user", ["bearer", "session", "api-key"])],
},
{
method: ["GET"],
@@ -15,7 +15,7 @@ export const adminProductCategoryRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["ALL"],
matcher: "/admin/product-categories*",
middlewares: [authenticate("admin", ["bearer", "session", "api-key"])],
middlewares: [authenticate("user", ["bearer", "session", "api-key"])],
},
{
method: ["GET"],
@@ -14,7 +14,7 @@ export const adminProductTypeRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["ALL"],
matcher: "/admin/product-types/*",
middlewares: [authenticate("admin", ["bearer", "session", "api-key"])],
middlewares: [authenticate("user", ["bearer", "session", "api-key"])],
},
{
@@ -28,7 +28,7 @@ export const adminProductRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["ALL"],
matcher: "/admin/products*",
middlewares: [authenticate("admin", ["bearer", "session", "api-key"])],
middlewares: [authenticate("user", ["bearer", "session", "api-key"])],
},
{
method: ["GET"],
@@ -19,7 +19,7 @@ import {
export const adminPromotionRoutesMiddlewares: MiddlewareRoute[] = [
{
matcher: "/admin/promotions*",
middlewares: [authenticate("admin", ["bearer", "session", "api-key"])],
middlewares: [authenticate("user", ["bearer", "session", "api-key"])],
},
{
method: ["GET"],
@@ -14,7 +14,7 @@ export const adminRegionRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["ALL"],
matcher: "/admin/regions*",
middlewares: [authenticate("admin", ["bearer", "session", "api-key"])],
middlewares: [authenticate("user", ["bearer", "session", "api-key"])],
},
{
method: ["GET"],
@@ -15,7 +15,7 @@ export const adminReservationRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["ALL"],
matcher: "/admin/reservations*",
middlewares: [authenticate("admin", ["bearer", "session", "api-key"])],
middlewares: [authenticate("user", ["bearer", "session", "api-key"])],
},
{
method: ["GET"],
@@ -13,7 +13,7 @@ export const adminReturnReasonRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["ALL"],
matcher: "/admin/return-reasons*",
middlewares: [authenticate("admin", ["bearer", "session", "api-key"])],
middlewares: [authenticate("user", ["bearer", "session", "api-key"])],
},
{
method: ["GET"],
@@ -13,7 +13,7 @@ export const adminOrderRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["ALL"],
matcher: "/admin/returns*",
middlewares: [authenticate("admin", ["bearer", "session", "api-key"])],
middlewares: [authenticate("user", ["bearer", "session", "api-key"])],
},
{
method: ["GET"],
@@ -16,7 +16,7 @@ export const adminSalesChannelRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["ALL"],
matcher: "/admin/sales-channels*",
middlewares: [authenticate("admin", ["bearer", "session", "api-key"])],
middlewares: [authenticate("user", ["bearer", "session", "api-key"])],
},
{
method: ["GET"],
@@ -21,7 +21,7 @@ import { createBatchBody } from "../../utils/validators"
export const adminShippingOptionRoutesMiddlewares: MiddlewareRoute[] = [
{
matcher: "/admin/shipping-options*",
middlewares: [authenticate("admin", ["bearer", "session"])],
middlewares: [authenticate("user", ["bearer", "session"])],
},
{
method: ["GET"],
@@ -15,7 +15,7 @@ import {
export const adminShippingProfilesMiddlewares: MiddlewareRoute[] = [
{
matcher: "/admin/shipping-profiles*",
middlewares: [authenticate("admin", ["bearer", "session", "api-key"])],
middlewares: [authenticate("user", ["bearer", "session", "api-key"])],
},
{
method: ["POST"],
@@ -17,7 +17,7 @@ export const adminStockLocationRoutesMiddlewares: MiddlewareRoute[] = [
{
method: "ALL",
matcher: "/admin/stock-locations*",
middlewares: [authenticate("admin", ["session", "bearer", "api-key"])],
middlewares: [authenticate("user", ["session", "bearer", "api-key"])],
},
{
method: ["POST"],
@@ -13,7 +13,7 @@ export const adminStoreRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["ALL"],
matcher: "/admin/stores*",
middlewares: [authenticate("admin", ["bearer", "session", "api-key"])],
middlewares: [authenticate("user", ["bearer", "session", "api-key"])],
},
{
method: ["GET"],
@@ -17,7 +17,7 @@ export const adminTaxRateRoutesMiddlewares: MiddlewareRoute[] = [
{
method: "ALL",
matcher: "/admin/tax-rates*",
middlewares: [authenticate("admin", ["bearer", "session", "api-key"])],
middlewares: [authenticate("user", ["bearer", "session", "api-key"])],
},
{
method: "POST",
@@ -15,7 +15,7 @@ export const adminTaxRegionRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["ALL"],
matcher: "/admin/tax-regions*",
middlewares: [authenticate("admin", ["bearer", "session", "api-key"])],
middlewares: [authenticate("user", ["bearer", "session", "api-key"])],
},
{
method: "POST",
@@ -14,7 +14,7 @@ export const adminUploadRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["ALL"],
matcher: "/admin/uploads*",
middlewares: [authenticate("admin", ["bearer", "session", "api-key"])],
middlewares: [authenticate("user", ["bearer", "session", "api-key"])],
},
// TODO: There is a `/protected` route in v1 that might need a bit more thought when implementing
{
@@ -17,7 +17,7 @@ export const adminUserRoutesMiddlewares: MiddlewareRoute[] = [
method: ["GET"],
matcher: "/admin/users",
middlewares: [
authenticate("admin", ["bearer", "session"]),
authenticate("user", ["bearer", "session"]),
validateAndTransformQuery(
AdminGetUsersParams,
QueryConfig.listTransformQueryConfig
@@ -28,7 +28,7 @@ export const adminUserRoutesMiddlewares: MiddlewareRoute[] = [
method: ["POST"],
matcher: "/admin/users",
middlewares: [
authenticate("admin", ["bearer", "session"], { allowUnregistered: true }),
authenticate("user", ["bearer", "session"], { allowUnregistered: true }),
validateAndTransformBody(AdminCreateUser),
validateAndTransformQuery(
AdminGetUserParams,
@@ -40,7 +40,7 @@ export const adminUserRoutesMiddlewares: MiddlewareRoute[] = [
method: ["GET"],
matcher: "/admin/users/:id",
middlewares: [
authenticate("admin", ["bearer", "session"]),
authenticate("user", ["bearer", "session"]),
validateAndTransformQuery(
AdminGetUserParams,
QueryConfig.retrieveTransformQueryConfig
@@ -51,7 +51,7 @@ export const adminUserRoutesMiddlewares: MiddlewareRoute[] = [
method: ["GET"],
matcher: "/admin/users/me",
middlewares: [
authenticate("admin", ["bearer", "session"]),
authenticate("user", ["bearer", "session"]),
validateAndTransformQuery(
AdminGetUserParams,
QueryConfig.retrieveTransformQueryConfig
@@ -62,7 +62,7 @@ export const adminUserRoutesMiddlewares: MiddlewareRoute[] = [
method: ["POST"],
matcher: "/admin/users/:id",
middlewares: [
authenticate("admin", ["bearer", "session"]),
authenticate("user", ["bearer", "session"]),
validateAndTransformBody(AdminUpdateUser),
validateAndTransformQuery(
AdminGetUserParams,
@@ -73,6 +73,6 @@ export const adminUserRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["DELETE"],
matcher: "/admin/users/:id",
middlewares: [authenticate("admin", ["bearer", "session"])],
middlewares: [authenticate("user", ["bearer", "session"])],
},
]
@@ -73,8 +73,6 @@ export const POST = async (
actor_id: result.id,
actor_type: "user",
auth_identity_id: req.auth_context.auth_identity_id,
app_metadata: {},
scope: "admin",
},
{
secret: jwtSecret,
@@ -16,7 +16,7 @@ export const adminWorkflowsExecutionsMiddlewares: MiddlewareRoute[] = [
{
method: ["ALL"],
matcher: "/admin/workflows-executions*",
middlewares: [authenticate("admin", ["bearer", "session", "api-key"])],
middlewares: [authenticate("user", ["bearer", "session", "api-key"])],
},
{
method: ["GET"],
@@ -1,5 +1,9 @@
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { AuthenticationInput, IAuthModuleService } from "@medusajs/types"
import {
AuthenticationInput,
IAuthModuleService,
ConfigModule,
} from "@medusajs/types"
import {
ContainerRegistrationKeys,
MedusaError,
@@ -9,10 +13,23 @@ import { MedusaRequest, MedusaResponse } from "../../../../../types/routing"
import { generateJwtToken } from "../../../../utils/auth/token"
export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
const { scope, auth_provider } = req.params
const actorType = scope === "admin" ? "user" : "customer"
const { actor_type, auth_provider } = req.params
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const config: ConfigModule = req.scope.resolve(
ContainerRegistrationKeys.CONFIG_MODULE
)
const authMethods = (config.projectConfig?.http as any)?.authMethods ?? {}
// Not having the config defined would allow for all auth providers for the particular actor.
if (authMethods[actor_type]) {
if (!authMethods[actor_type].includes(auth_provider)) {
throw new MedusaError(
MedusaError.Types.NOT_ALLOWED,
`The actor type ${actor_type} is not allowed to use the auth provider ${auth_provider}`
)
}
}
const service: IAuthModuleService = req.scope.resolve(
ModuleRegistrationName.AUTH
)
@@ -22,7 +39,6 @@ export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
headers: req.headers,
query: req.query,
body: req.body,
authScope: scope,
protocol: req.protocol,
} as AuthenticationInput
@@ -31,11 +47,11 @@ export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
const queryObject = remoteQueryObjectFromString({
entryPoint: "auth_identity",
fields: [`${actorType}.id`],
fields: [`${actor_type}.id`],
variables: { id: authIdentity.id },
})
const [actorData] = await remoteQuery(queryObject)
const entityId = actorData?.[actorType]?.id
const entityId = actorData?.[actor_type]?.id
if (success) {
const { http } = req.scope.resolve(
@@ -43,14 +59,11 @@ export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
).projectConfig
const { jwtSecret, jwtExpiresIn } = http
// TODO: Clean up mapping between scope and actor type
const token = generateJwtToken(
{
actor_id: entityId,
actor_type: actorType,
actor_type,
auth_identity_id: authIdentity.id,
app_metadata: {},
scope,
},
{
secret: jwtSecret,
@@ -1,5 +1,9 @@
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { AuthenticationInput, IAuthModuleService } from "@medusajs/types"
import {
AuthenticationInput,
IAuthModuleService,
ConfigModule,
} from "@medusajs/types"
import {
ContainerRegistrationKeys,
MedusaError,
@@ -9,9 +13,22 @@ import { MedusaRequest, MedusaResponse } from "../../../../types/routing"
import { generateJwtToken } from "../../../utils/auth/token"
export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
const { scope, auth_provider } = req.params
const actorType = scope === "admin" ? "user" : "customer"
const { actor_type, auth_provider } = req.params
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const config: ConfigModule = req.scope.resolve(
ContainerRegistrationKeys.CONFIG_MODULE
)
const authMethods = (config.projectConfig?.http as any)?.authMethods ?? {}
// Not having the config defined would allow for all auth providers for the particular actor.
if (authMethods[actor_type]) {
if (!authMethods[actor_type].includes(auth_provider)) {
throw new MedusaError(
MedusaError.Types.NOT_ALLOWED,
`The actor type ${actor_type} is not allowed to use the auth provider ${auth_provider}`
)
}
}
const service: IAuthModuleService = req.scope.resolve(
ModuleRegistrationName.AUTH
@@ -22,7 +39,6 @@ export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
headers: req.headers,
query: req.query,
body: req.body,
authScope: scope,
protocol: req.protocol,
} as AuthenticationInput
@@ -43,21 +59,18 @@ export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
const queryObject = remoteQueryObjectFromString({
entryPoint: "auth_identity",
fields: [`${actorType}.id`],
fields: [`${actor_type}.id`],
variables: { id: authIdentity.id },
})
const [actorData] = await remoteQuery(queryObject)
const entityId = actorData?.[actorType]?.id
const entityId = actorData?.[actor_type]?.id
const { jwtSecret, jwtExpiresIn } = http
// TODO: Clean up mapping between scope and actor type
const token = generateJwtToken(
{
actor_id: entityId,
actor_type: actorType,
actor_type,
auth_identity_id: authIdentity.id,
app_metadata: {},
scope,
},
{
secret: jwtSecret,
+2 -2
View File
@@ -14,12 +14,12 @@ export const authRoutesMiddlewares: MiddlewareRoute[] = [
},
{
method: ["POST"],
matcher: "/auth/:scope/:auth_provider/callback",
matcher: "/auth/:actor_type/:auth_provider/callback",
middlewares: [],
},
{
method: ["POST"],
matcher: "/auth/:scope/:auth_provider",
matcher: "/auth/:actor_type/:auth_provider",
middlewares: [],
},
]
@@ -25,7 +25,7 @@ export const storeCartRoutesMiddlewares: MiddlewareRoute[] = [
method: "ALL",
matcher: "/store/carts*",
middlewares: [
authenticate("store", ["session", "bearer"], {
authenticate("customer", ["session", "bearer"], {
allowUnauthenticated: true,
}),
],
@@ -20,7 +20,9 @@ export const storeCustomerRoutesMiddlewares: MiddlewareRoute[] = [
method: ["POST"],
matcher: "/store/customers",
middlewares: [
authenticate("store", ["session", "bearer"], { allowUnregistered: true }),
authenticate("customer", ["session", "bearer"], {
allowUnregistered: true,
}),
validateAndTransformBody(StoreCreateCustomer),
validateAndTransformQuery(
StoreGetCustomerParams,
@@ -31,7 +33,7 @@ export const storeCustomerRoutesMiddlewares: MiddlewareRoute[] = [
{
method: "ALL",
matcher: "/store/customers/me*",
middlewares: [authenticate("store", ["session", "bearer"])],
middlewares: [authenticate("customer", ["session", "bearer"])],
},
{
method: ["GET"],
@@ -41,8 +41,6 @@ export const POST = async (
actor_id: result.id,
actor_type: "customer",
auth_identity_id: req.auth_context.auth_identity_id,
app_metadata: {},
scope: "store",
},
{
secret: jwtSecret,
@@ -9,7 +9,7 @@ export const storeOrderRoutesMiddlewares: MiddlewareRoute[] = [
method: ["GET"],
matcher: "/store/orders",
middlewares: [
authenticate("store", ["session", "bearer"]),
authenticate("customer", ["session", "bearer"]),
validateAndTransformQuery(
StoreGetOrdersParams,
QueryConfig.listTransformQueryConfig
@@ -20,7 +20,7 @@ export const storeOrderRoutesMiddlewares: MiddlewareRoute[] = [
method: ["GET"],
matcher: "/store/orders/:id",
middlewares: [
authenticate("store", ["session", "bearer"], {
authenticate("customer", ["session", "bearer"], {
allowUnauthenticated: true,
}),
validateAndTransformQuery(
@@ -14,7 +14,7 @@ export const storePaymentCollectionsMiddlewares: MiddlewareRoute[] = [
method: "ALL",
matcher: "/store/payment-collections*",
middlewares: [
authenticate("store", ["session", "bearer"], {
authenticate("customer", ["session", "bearer"], {
allowUnauthenticated: true,
}),
],
@@ -19,7 +19,7 @@ export const storeProductRoutesMiddlewares: MiddlewareRoute[] = [
method: "ALL",
matcher: "/store/products*",
middlewares: [
authenticate("store", ["session", "bearer"], {
authenticate("customer", ["session", "bearer"], {
allowUnauthenticated: true,
}),
],
+6 -2
View File
@@ -46,14 +46,18 @@ export default async function ({
} else {
const user = await userService.create({ email })
const { authIdentity } = await authService.authenticate(provider, {
const { authIdentity, error } = await authService.authenticate(provider, {
body: {
email,
password,
},
authScope: "admin",
})
if (error) {
Logger.error(error)
throw new Error(error)
}
await remoteLink.create([
{
[Modules.USER]: {
+1 -4
View File
@@ -61,11 +61,8 @@ export interface MedusaRequest<Body = unknown>
export interface AuthContext {
actor_id: string
// TODO: We possibly want to make this more open-ended so it's easy to extend.
actor_type: "api-key" | "user" | "customer" | "unknown"
actor_type: string
auth_identity_id: string
scope: string
app_metadata: Record<string, any>
}
export interface AuthenticatedMedusaRequest<Body = never>
@@ -8,25 +8,23 @@ import {
MedusaRequest,
MedusaResponse,
} from "../../types/routing"
import { ContainerRegistrationKeys } from "@medusajs/utils"
const SESSION_AUTH = "session"
const BEARER_AUTH = "bearer"
const API_KEY_AUTH = "api-key"
// This is the only hard-coded actor type, as API keys have special handling for now. We could also generalize API keys to carry the actor type with them.
const ADMIN_ACTOR_TYPE = "user"
type AuthType = typeof SESSION_AUTH | typeof BEARER_AUTH | typeof API_KEY_AUTH
const ADMIN_SCOPE = "admin"
const STORE_SCOPE = "store"
const ALL_SCOPE = "*"
type Scope = typeof ADMIN_SCOPE | typeof STORE_SCOPE | typeof ALL_SCOPE
type MedusaSession = {
auth_context: AuthContext
}
export const authenticate = (
authScope: Scope | Scope[],
actorType: string,
authType: AuthType | AuthType[],
options: { allowUnauthenticated?: boolean; allowUnregistered?: boolean } = {}
): RequestHandler => {
@@ -36,21 +34,20 @@ export const authenticate = (
next: NextFunction
): Promise<void> => {
const authTypes = Array.isArray(authType) ? authType : [authType]
const scopes = Array.isArray(authScope) ? authScope : [authScope]
const actorTypes = Array.isArray(actorType) ? actorType : [actorType]
const req_ = req as AuthenticatedMedusaRequest
// We only allow authenticating using a secret API key on the admin
const isExclusivelyAdmin =
scopes.length === 1 && scopes.includes(ADMIN_SCOPE)
if (authTypes.includes(API_KEY_AUTH) && isExclusivelyAdmin) {
const isExclusivelyUser =
actorTypes.length === 1 && actorTypes[0] === ADMIN_ACTOR_TYPE
if (authTypes.includes(API_KEY_AUTH) && isExclusivelyUser) {
const apiKey = await getApiKeyInfo(req)
if (apiKey) {
req_.auth_context = {
actor_id: apiKey.id,
actor_type: "api-key",
auth_identity_id: "",
app_metadata: {},
scope: ADMIN_SCOPE,
}
return next()
@@ -61,27 +58,29 @@ export const authenticate = (
let authContext: AuthContext | null = getAuthContextFromSession(
req.session,
authTypes,
scopes
actorTypes
)
if (!authContext) {
const { http } =
req.scope.resolve<ConfigModule>("configModule").projectConfig
const { http } = req.scope.resolve<ConfigModule>(
ContainerRegistrationKeys.CONFIG_MODULE
).projectConfig
authContext = getAuthContextFromJwtToken(
req.headers.authorization,
http.jwtSecret!,
authTypes,
scopes
actorTypes
)
}
// If the entity is authenticated, and it is a registered user/customer we can continue
if (!!authContext?.actor_id && authContext.actor_type !== "unknown") {
// If the entity is authenticated, and it is a registered actor we can continue
if (authContext?.actor_id) {
req_.auth_context = authContext
return next()
}
// If the entity is authenticated, but there is no user/customer yet, we can continue (eg. in the case of a user invite) if allow unregistered is set
// If the entity is authenticated, but there is no registered actor yet, we can continue (eg. in the case of a user invite) if allow unregistered is set
if (authContext?.auth_identity_id && options.allowUnregistered) {
req_.auth_context = authContext
return next()
@@ -142,7 +141,7 @@ const getApiKeyInfo = async (req: MedusaRequest): Promise<ApiKeyDTO | null> => {
const getAuthContextFromSession = (
session: Partial<MedusaSession> = {},
authTypes: AuthType[],
scopes: Scope[]
actorTypes: string[]
): AuthContext | null => {
if (!authTypes.includes(SESSION_AUTH)) {
return null
@@ -150,8 +149,8 @@ const getAuthContextFromSession = (
if (
session.auth_context &&
(scopes.includes("*") ||
scopes.includes(session.auth_context.scope as Scope))
(actorTypes.includes("*") ||
actorTypes.includes(session.auth_context.actor_type))
) {
return session.auth_context
}
@@ -163,7 +162,7 @@ const getAuthContextFromJwtToken = (
authHeader: string | undefined,
jwtSecret: string,
authTypes: AuthType[],
scopes: Scope[]
actorTypes: string[]
): AuthContext | null => {
if (!authTypes.includes(BEARER_AUTH)) {
return null
@@ -185,7 +184,10 @@ const getAuthContextFromJwtToken = (
// verify token and set authUser
try {
const verified = jwt.verify(token, jwtSecret) as JwtPayload
if (scopes.includes("*") || scopes.includes(verified.scope)) {
if (
actorTypes.includes("*") ||
actorTypes.includes(verified.actor_type)
) {
return verified as AuthContext
}
} catch (err) {