diff --git a/integration-tests/http/__tests__/auth/admin/auth.spec.ts b/integration-tests/http/__tests__/auth/admin/auth.spec.ts index bb44c6bc3f..890f09fbd8 100644 --- a/integration-tests/http/__tests__/auth/admin/auth.spec.ts +++ b/integration-tests/http/__tests__/auth/admin/auth.spec.ts @@ -150,6 +150,9 @@ medusaIntegrationTestRunner({ .post("/auth/user/emailpass/reset-password", {}) .catch((e) => e) + expect(errResponse.response.data.message).toEqual( + "Invalid request: Field 'identifier' is required" + ) expect(errResponse.response.status).toEqual(400) }) @@ -170,15 +173,6 @@ medusaIntegrationTestRunner({ expect(response.status).toEqual(201) }) - it("should fail to generate token for existing user but no provider, but still respond with 201", async () => { - const response = await api.post( - "/auth/user/non-existing-provider/reset-password", - { identifier: "admin@medusa.js" } - ) - - expect(response.status).toEqual(201) - }) - it("should successfully reset password", async () => { // Register user await api.post("/auth/user/emailpass/register", { @@ -199,10 +193,14 @@ medusaIntegrationTestRunner({ }) const response = await api.post( - `/auth/user/emailpass/update?token=${result}`, + `/auth/user/emailpass/update`, { - email: "test@medusa-commerce.com", password: "new_password", + }, + { + headers: { + authorization: `Bearer ${result}`, + }, } ) @@ -250,10 +248,15 @@ medusaIntegrationTestRunner({ }) const response = await api.post( - `/auth/user/emailpass/update?token=${result}`, + `/auth/user/emailpass/update`, { email: "test+new@medusa-commerce.com", password: "new_password", + }, + { + headers: { + authorization: `Bearer ${result}`, + }, } ) @@ -306,10 +309,17 @@ medusaIntegrationTestRunner({ jest.advanceTimersByTime(15 * 60 * 1000) const response = await api - .post(`/auth/user/emailpass/update?token=${result}`, { - email: "test@medusa-commerce.com", - password: "new_password", - }) + .post( + `/auth/user/emailpass/update`, + { + password: "new_password", + }, + { + headers: { + authorization: `Bearer ${result}`, + }, + } + ) .catch((e) => e) expect(response.response.status).toEqual(401) @@ -363,10 +373,17 @@ medusaIntegrationTestRunner({ jest.advanceTimersByTime(15 * 60 * 1000) const response = await api - .post(`/auth/customer/emailpass/update?token=${result}`, { - email: "test@medusa-commerce.com", - password: "new_password", - }) + .post( + `/auth/customer/emailpass/update`, + { + password: "new_password", + }, + { + headers: { + authorization: `Bearer ${result}`, + }, + } + ) .catch((e) => e) expect(response.response.status).toEqual(401) @@ -398,10 +415,17 @@ medusaIntegrationTestRunner({ jest.advanceTimersByTime(15 * 60 * 1000) const response = await api - .post(`/auth/user/emailpass/update?token=${result}`, { - email: "test@medusa-commerce.com", - password: "new_password", - }) + .post( + `/auth/user/emailpass/update`, + { + password: "new_password", + }, + { + headers: { + authorization: `Bearer ${result}`, + }, + } + ) .catch((e) => e) expect(response.response.status).toEqual(401) diff --git a/packages/admin/dashboard/src/hooks/api/auth.tsx b/packages/admin/dashboard/src/hooks/api/auth.tsx index 170bfc5b70..e28d9d6aeb 100644 --- a/packages/admin/dashboard/src/hooks/api/auth.tsx +++ b/packages/admin/dashboard/src/hooks/api/auth.tsx @@ -62,7 +62,7 @@ export const useLogout = (options?: UseMutationOptions) => { export const useUpdateProviderForEmailPass = ( token: string, - options?: UseMutationOptions + options?: UseMutationOptions ) => { return useMutation({ mutationFn: (payload) => diff --git a/packages/core/core-flows/src/auth/workflows/generate-reset-password-token.ts b/packages/core/core-flows/src/auth/workflows/generate-reset-password-token.ts index da3461b2e6..8c7bd193c2 100644 --- a/packages/core/core-flows/src/auth/workflows/generate-reset-password-token.ts +++ b/packages/core/core-flows/src/auth/workflows/generate-reset-password-token.ts @@ -15,15 +15,15 @@ import { emitEventStep, useRemoteQueryStep } from "../../common" * [Generate Reset Password Token for Admin](https://docs.medusajs.com/api/admin#auth_postactor_typeauth_providerresetpassword) * and [Generate Reset Password Token for Customer](https://docs.medusajs.com/api/store#auth_postactor_typeauth_providerresetpassword) * API Routes. - * + * * The workflow emits the `auth.password_reset` event, which you can listen to in * a [subscriber](https://docs.medusajs.com/learn/fundamentals/events-and-subscribers). Follow * [this guide](https://docs.medusajs.com/resources/commerce-modules/auth/reset-password) to learn * how to handle this event. - * + * * You can use this workflow within your customizations or your own custom workflows, allowing you to * generate reset password tokens within your custom flows. - * + * * @example * const { result } = await generateResetPasswordTokenWorkflow(container) * .run({ @@ -34,9 +34,9 @@ import { emitEventStep, useRemoteQueryStep } from "../../common" * secret: "jwt_123" // jwt secret * } * }) - * + * * @summary - * + * * Generate a reset password token for a user or customer. */ export const generateResetPasswordTokenWorkflow = createWorkflow( @@ -90,11 +90,6 @@ export const generateResetPasswordTokenWorkflow = createWorkflow( eventName: AuthWorkflowEvents.PASSWORD_RESET, data: { entity_id: input.entityId, - /** - * Use `actor_type` instead. Will be removed in a future version. - * @deprecated - */ - actorType: input.actorType, actor_type: input.actorType, token, }, diff --git a/packages/core/framework/src/http/middlewares/authenticate-middleware.ts b/packages/core/framework/src/http/middlewares/authenticate-middleware.ts index af715abc42..2a27024a02 100644 --- a/packages/core/framework/src/http/middlewares/authenticate-middleware.ts +++ b/packages/core/framework/src/http/middlewares/authenticate-middleware.ts @@ -170,7 +170,7 @@ const getAuthContextFromSession = ( return null } -const getAuthContextFromJwtToken = ( +export const getAuthContextFromJwtToken = ( authHeader: string | undefined, jwtSecret: string, authTypes: AuthType[], diff --git a/packages/core/js-sdk/src/auth/index.ts b/packages/core/js-sdk/src/auth/index.ts index 227d2a4602..720ed3ed2b 100644 --- a/packages/core/js-sdk/src/auth/index.ts +++ b/packages/core/js-sdk/src/auth/index.ts @@ -20,7 +20,7 @@ export class Auth { * @param payload - The data to pass in the request's body for authentication. When using the `emailpass` provider, * you pass the email and password. * @returns The JWT token used for registration later. - * + * * @tags auth * * @example @@ -68,7 +68,7 @@ export class Auth { * @param payload - The data to pass in the request's body for authentication. When using the `emailpass` provider, * you pass the email and password. * @returns The authentication JWT token - * + * * @tags auth * * @example @@ -116,7 +116,7 @@ export class Auth { * @param query - The query parameters from the Oauth callback, which should be passed to the API route. This includes query parameters like * `code` and `state`. * @returns The authentication JWT token - * + * * @tags auth * * @example @@ -125,7 +125,7 @@ export class Auth { * "google", * { * code: "123", - * state: "456" + * state: "456" * } * ).then((token) => { * console.log(token) @@ -158,7 +158,7 @@ export class Auth { * with {@link callback}. It sends a request to the [Refresh Authentication Token API route](https://docs.medusajs.com/api/admin#auth_postadminauthtokenrefresh). * * @returns The refreshed JWT authentication token. - * + * * @tags auth * * @example @@ -184,7 +184,7 @@ export class Auth { /** * This method deletes the authentication session of the currently logged-in user to log them out. * It sends a request to the [Delete Authentication Session API route](https://docs.medusajs.com/api/admin#auth_deletesession). - * + * * @tags auth * * @example @@ -214,7 +214,7 @@ export class Auth { * @param actor - The actor type. For example, `user` for admin user, or `customer` for customer. * @param provider - The authentication provider to use. For example, `emailpass`. * @param body - The data required to identify the user. - * + * * @tags auth * * @example @@ -261,7 +261,7 @@ export class Auth { * @param provider - The authentication provider to use. For example, `emailpass`. * @param body - The data necessary to update the user's authentication data. When resetting the user's password, * send the `password` property. - * + * * @tags auth * * @example @@ -280,16 +280,14 @@ export class Auth { updateProvider = async ( actor: string, provider: string, - body: Record, + body: HttpTypes.AdminUpdateProvider, token: string ) => { - await this.client.fetch( - `/auth/${actor}/${provider}/update?token=${token}`, - { - method: "POST", - body, - } - ) + await this.client.fetch(`/auth/${actor}/${provider}/update`, { + method: "POST", + body, + headers: { Authorization: `Bearer ${token}` }, + }) } /** diff --git a/packages/core/types/src/http/auth/payloads.ts b/packages/core/types/src/http/auth/payloads.ts index 6976b0504b..5a357f54b9 100644 --- a/packages/core/types/src/http/auth/payloads.ts +++ b/packages/core/types/src/http/auth/payloads.ts @@ -5,3 +5,7 @@ export interface AdminSignUpWithEmailPassword { export interface AdminSignInWithEmailPassword extends AdminSignUpWithEmailPassword {} + +export interface AdminUpdateProvider { + [key: string]: unknown // Allow for any additional fields, this will vary depending on the provider +} diff --git a/packages/medusa/src/api/auth/utils/validate-token.ts b/packages/medusa/src/api/auth/utils/validate-token.ts index 84762fd29d..78fbddfee4 100644 --- a/packages/medusa/src/api/auth/utils/validate-token.ts +++ b/packages/medusa/src/api/auth/utils/validate-token.ts @@ -1,5 +1,6 @@ import { AuthenticatedMedusaRequest, + getAuthContextFromJwtToken, MedusaNextFunction, MedusaRequest, MedusaResponse, @@ -10,20 +11,37 @@ import { MedusaError, Modules, } from "@medusajs/framework/utils" -import { decode, JwtPayload, verify } from "jsonwebtoken" +import { HttpTypes } from "@medusajs/types" + +export interface UpdateProviderJwtPayload { + entity_id: string + actor_type: string + provider: string +} // Middleware to validate that a token is valid export const validateToken = () => { return async ( - req: MedusaRequest, + req: MedusaRequest, res: MedusaResponse, next: MedusaNextFunction ) => { const { actor_type, auth_provider } = req.params - const { token } = req.query const req_ = req as AuthenticatedMedusaRequest + // @ts-ignore + const { http } = req_.scope.resolve( + ContainerRegistrationKeys.CONFIG_MODULE + ).projectConfig + + const token = getAuthContextFromJwtToken( + req.headers.authorization, + http.jwtSecret as string, + ["bearer"], + [actor_type] + ) as UpdateProviderJwtPayload | null + const errorObject = new MedusaError( MedusaError.Types.UNAUTHORIZED, `Invalid token` @@ -33,27 +51,15 @@ export const validateToken = () => { return next(errorObject) } - // @ts-ignore - const { http } = req_.scope.resolve( - ContainerRegistrationKeys.CONFIG_MODULE - ).projectConfig - const authModule = req.scope.resolve(Modules.AUTH) - const decoded = decode(token as string) as JwtPayload - - if (!decoded?.entity_id) { - return next(errorObject) - } - - // E.g. token was requested for a customer, but attempted used for a user - if (decoded?.actor_type !== actor_type) { + if (!token?.entity_id) { return next(errorObject) } const [providerIdentity] = await authModule.listProviderIdentities( { - entity_id: decoded.entity_id, + entity_id: token.entity_id, provider: auth_provider, }, { @@ -65,12 +71,6 @@ export const validateToken = () => { return next(errorObject) } - try { - verify(token as string, http.jwtSecret as string) as JwtPayload - } catch (error) { - return next(errorObject) - } - req_.auth_context = { actor_type, auth_identity_id: providerIdentity.auth_identity_id!,