chore: Move token from params to headers (#11281)

* chore: Move token from params to body

* chore: Add type

* wip

* chore: clean up

* clean ip
This commit is contained in:
Oli Juhl authored and GitHub committed 2025-02-26 17:41:16 +01:00
1 parent 4ec5219a72
commit 54a6ef91ac
7 files changed
+96 -75

No files matched your search

@@ -150,6 +150,9 @@ medusaIntegrationTestRunner({
.post("/auth/user/emailpass/reset-password", {}) .post("/auth/user/emailpass/reset-password", {})
.catch((e) => e) .catch((e) => e)
expect(errResponse.response.data.message).toEqual(
"Invalid request: Field 'identifier' is required"
)
expect(errResponse.response.status).toEqual(400) expect(errResponse.response.status).toEqual(400)
}) })
@@ -170,15 +173,6 @@ medusaIntegrationTestRunner({
expect(response.status).toEqual(201) 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 () => { it("should successfully reset password", async () => {
// Register user // Register user
await api.post("/auth/user/emailpass/register", { await api.post("/auth/user/emailpass/register", {
@@ -199,10 +193,14 @@ medusaIntegrationTestRunner({
}) })
const response = await api.post( const response = await api.post(
`/auth/user/emailpass/update?token=${result}`, `/auth/user/emailpass/update`,
{ {
email: "test@medusa-commerce.com",
password: "new_password", password: "new_password",
},
{
headers: {
authorization: `Bearer ${result}`,
},
} }
) )
@@ -250,10 +248,15 @@ medusaIntegrationTestRunner({
}) })
const response = await api.post( const response = await api.post(
`/auth/user/emailpass/update?token=${result}`, `/auth/user/emailpass/update`,
{ {
email: "test+new@medusa-commerce.com", email: "test+new@medusa-commerce.com",
password: "new_password", password: "new_password",
},
{
headers: {
authorization: `Bearer ${result}`,
},
} }
) )
@@ -306,10 +309,17 @@ medusaIntegrationTestRunner({
jest.advanceTimersByTime(15 * 60 * 1000) jest.advanceTimersByTime(15 * 60 * 1000)
const response = await api const response = await api
.post(`/auth/user/emailpass/update?token=${result}`, { .post(
email: "test@medusa-commerce.com", `/auth/user/emailpass/update`,
password: "new_password", {
}) password: "new_password",
},
{
headers: {
authorization: `Bearer ${result}`,
},
}
)
.catch((e) => e) .catch((e) => e)
expect(response.response.status).toEqual(401) expect(response.response.status).toEqual(401)
@@ -363,10 +373,17 @@ medusaIntegrationTestRunner({
jest.advanceTimersByTime(15 * 60 * 1000) jest.advanceTimersByTime(15 * 60 * 1000)
const response = await api const response = await api
.post(`/auth/customer/emailpass/update?token=${result}`, { .post(
email: "test@medusa-commerce.com", `/auth/customer/emailpass/update`,
password: "new_password", {
}) password: "new_password",
},
{
headers: {
authorization: `Bearer ${result}`,
},
}
)
.catch((e) => e) .catch((e) => e)
expect(response.response.status).toEqual(401) expect(response.response.status).toEqual(401)
@@ -398,10 +415,17 @@ medusaIntegrationTestRunner({
jest.advanceTimersByTime(15 * 60 * 1000) jest.advanceTimersByTime(15 * 60 * 1000)
const response = await api const response = await api
.post(`/auth/user/emailpass/update?token=${result}`, { .post(
email: "test@medusa-commerce.com", `/auth/user/emailpass/update`,
password: "new_password", {
}) password: "new_password",
},
{
headers: {
authorization: `Bearer ${result}`,
},
}
)
.catch((e) => e) .catch((e) => e)
expect(response.response.status).toEqual(401) expect(response.response.status).toEqual(401)
@@ -62,7 +62,7 @@ export const useLogout = (options?: UseMutationOptions<void, FetchError>) => {
export const useUpdateProviderForEmailPass = ( export const useUpdateProviderForEmailPass = (
token: string, token: string,
options?: UseMutationOptions<void, FetchError, { password: string }> options?: UseMutationOptions<void, FetchError, HttpTypes.AdminUpdateProvider>
) => { ) => {
return useMutation({ return useMutation({
mutationFn: (payload) => mutationFn: (payload) =>
@@ -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) * [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) * and [Generate Reset Password Token for Customer](https://docs.medusajs.com/api/store#auth_postactor_typeauth_providerresetpassword)
* API Routes. * API Routes.
* *
* The workflow emits the `auth.password_reset` event, which you can listen to in * 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 * 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 * [this guide](https://docs.medusajs.com/resources/commerce-modules/auth/reset-password) to learn
* how to handle this event. * how to handle this event.
* *
* You can use this workflow within your customizations or your own custom workflows, allowing you to * You can use this workflow within your customizations or your own custom workflows, allowing you to
* generate reset password tokens within your custom flows. * generate reset password tokens within your custom flows.
* *
* @example * @example
* const { result } = await generateResetPasswordTokenWorkflow(container) * const { result } = await generateResetPasswordTokenWorkflow(container)
* .run({ * .run({
@@ -34,9 +34,9 @@ import { emitEventStep, useRemoteQueryStep } from "../../common"
* secret: "jwt_123" // jwt secret * secret: "jwt_123" // jwt secret
* } * }
* }) * })
* *
* @summary * @summary
* *
* Generate a reset password token for a user or customer. * Generate a reset password token for a user or customer.
*/ */
export const generateResetPasswordTokenWorkflow = createWorkflow( export const generateResetPasswordTokenWorkflow = createWorkflow(
@@ -90,11 +90,6 @@ export const generateResetPasswordTokenWorkflow = createWorkflow(
eventName: AuthWorkflowEvents.PASSWORD_RESET, eventName: AuthWorkflowEvents.PASSWORD_RESET,
data: { data: {
entity_id: input.entityId, entity_id: input.entityId,
/**
* Use `actor_type` instead. Will be removed in a future version.
* @deprecated
*/
actorType: input.actorType,
actor_type: input.actorType, actor_type: input.actorType,
token, token,
}, },
@@ -170,7 +170,7 @@ const getAuthContextFromSession = (
return null return null
} }
const getAuthContextFromJwtToken = ( export const getAuthContextFromJwtToken = (
authHeader: string | undefined, authHeader: string | undefined,
jwtSecret: string, jwtSecret: string,
authTypes: AuthType[], authTypes: AuthType[],
+14 -16
View File
@@ -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, * @param payload - The data to pass in the request's body for authentication. When using the `emailpass` provider,
* you pass the email and password. * you pass the email and password.
* @returns The JWT token used for registration later. * @returns The JWT token used for registration later.
* *
* @tags auth * @tags auth
* *
* @example * @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, * @param payload - The data to pass in the request's body for authentication. When using the `emailpass` provider,
* you pass the email and password. * you pass the email and password.
* @returns The authentication JWT token * @returns The authentication JWT token
* *
* @tags auth * @tags auth
* *
* @example * @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 * @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`. * `code` and `state`.
* @returns The authentication JWT token * @returns The authentication JWT token
* *
* @tags auth * @tags auth
* *
* @example * @example
@@ -125,7 +125,7 @@ export class Auth {
* "google", * "google",
* { * {
* code: "123", * code: "123",
* state: "456" * state: "456"
* } * }
* ).then((token) => { * ).then((token) => {
* console.log(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). * 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. * @returns The refreshed JWT authentication token.
* *
* @tags auth * @tags auth
* *
* @example * @example
@@ -184,7 +184,7 @@ export class Auth {
/** /**
* This method deletes the authentication session of the currently logged-in user to log them out. * 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). * It sends a request to the [Delete Authentication Session API route](https://docs.medusajs.com/api/admin#auth_deletesession).
* *
* @tags auth * @tags auth
* *
* @example * @example
@@ -214,7 +214,7 @@ export class Auth {
* @param actor - The actor type. For example, `user` for admin user, or `customer` for customer. * @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 provider - The authentication provider to use. For example, `emailpass`.
* @param body - The data required to identify the user. * @param body - The data required to identify the user.
* *
* @tags auth * @tags auth
* *
* @example * @example
@@ -261,7 +261,7 @@ export class Auth {
* @param provider - The authentication provider to use. For example, `emailpass`. * @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, * @param body - The data necessary to update the user's authentication data. When resetting the user's password,
* send the `password` property. * send the `password` property.
* *
* @tags auth * @tags auth
* *
* @example * @example
@@ -280,16 +280,14 @@ export class Auth {
updateProvider = async ( updateProvider = async (
actor: string, actor: string,
provider: string, provider: string,
body: Record<string, unknown>, body: HttpTypes.AdminUpdateProvider,
token: string token: string
) => { ) => {
await this.client.fetch( await this.client.fetch(`/auth/${actor}/${provider}/update`, {
`/auth/${actor}/${provider}/update?token=${token}`, method: "POST",
{ body,
method: "POST", headers: { Authorization: `Bearer ${token}` },
body, })
}
)
} }
/** /**
@@ -5,3 +5,7 @@ export interface AdminSignUpWithEmailPassword {
export interface AdminSignInWithEmailPassword export interface AdminSignInWithEmailPassword
extends AdminSignUpWithEmailPassword {} extends AdminSignUpWithEmailPassword {}
export interface AdminUpdateProvider {
[key: string]: unknown // Allow for any additional fields, this will vary depending on the provider
}
@@ -1,5 +1,6 @@
import { import {
AuthenticatedMedusaRequest, AuthenticatedMedusaRequest,
getAuthContextFromJwtToken,
MedusaNextFunction, MedusaNextFunction,
MedusaRequest, MedusaRequest,
MedusaResponse, MedusaResponse,
@@ -10,20 +11,37 @@ import {
MedusaError, MedusaError,
Modules, Modules,
} from "@medusajs/framework/utils" } 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 // Middleware to validate that a token is valid
export const validateToken = () => { export const validateToken = () => {
return async ( return async (
req: MedusaRequest, req: MedusaRequest<HttpTypes.AdminUpdateProvider>,
res: MedusaResponse, res: MedusaResponse,
next: MedusaNextFunction next: MedusaNextFunction
) => { ) => {
const { actor_type, auth_provider } = req.params const { actor_type, auth_provider } = req.params
const { token } = req.query
const req_ = req as AuthenticatedMedusaRequest const req_ = req as AuthenticatedMedusaRequest
// @ts-ignore
const { http } = req_.scope.resolve<ConfigModule>(
ContainerRegistrationKeys.CONFIG_MODULE
).projectConfig
const token = getAuthContextFromJwtToken(
req.headers.authorization,
http.jwtSecret as string,
["bearer"],
[actor_type]
) as UpdateProviderJwtPayload | null
const errorObject = new MedusaError( const errorObject = new MedusaError(
MedusaError.Types.UNAUTHORIZED, MedusaError.Types.UNAUTHORIZED,
`Invalid token` `Invalid token`
@@ -33,27 +51,15 @@ export const validateToken = () => {
return next(errorObject) return next(errorObject)
} }
// @ts-ignore
const { http } = req_.scope.resolve<ConfigModule>(
ContainerRegistrationKeys.CONFIG_MODULE
).projectConfig
const authModule = req.scope.resolve<IAuthModuleService>(Modules.AUTH) const authModule = req.scope.resolve<IAuthModuleService>(Modules.AUTH)
const decoded = decode(token as string) as JwtPayload if (!token?.entity_id) {
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) {
return next(errorObject) return next(errorObject)
} }
const [providerIdentity] = await authModule.listProviderIdentities( const [providerIdentity] = await authModule.listProviderIdentities(
{ {
entity_id: decoded.entity_id, entity_id: token.entity_id,
provider: auth_provider, provider: auth_provider,
}, },
{ {
@@ -65,12 +71,6 @@ export const validateToken = () => {
return next(errorObject) return next(errorObject)
} }
try {
verify(token as string, http.jwtSecret as string) as JwtPayload
} catch (error) {
return next(errorObject)
}
req_.auth_context = { req_.auth_context = {
actor_type, actor_type,
auth_identity_id: providerIdentity.auth_identity_id!, auth_identity_id: providerIdentity.auth_identity_id!,