Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Move token from params to body #11281

Merged
merged 7 commits into from
Feb 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 48 additions & 24 deletions integration-tests/http/__tests__/auth/admin/auth.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
})

Expand All @@ -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: "[email protected]" }
)

expect(response.status).toEqual(201)
})

it("should successfully reset password", async () => {
// Register user
await api.post("/auth/user/emailpass/register", {
Expand All @@ -199,10 +193,14 @@ medusaIntegrationTestRunner({
})

const response = await api.post(
`/auth/user/emailpass/update?token=${result}`,
`/auth/user/emailpass/update`,
{
email: "[email protected]",
password: "new_password",
},
{
headers: {
authorization: `Bearer ${result}`,
},
}
)

Expand Down Expand Up @@ -250,10 +248,15 @@ medusaIntegrationTestRunner({
})

const response = await api.post(
`/auth/user/emailpass/update?token=${result}`,
`/auth/user/emailpass/update`,
{
email: "[email protected]",
password: "new_password",
},
{
headers: {
authorization: `Bearer ${result}`,
},
}
)

Expand Down Expand Up @@ -306,10 +309,17 @@ medusaIntegrationTestRunner({
jest.advanceTimersByTime(15 * 60 * 1000)

const response = await api
.post(`/auth/user/emailpass/update?token=${result}`, {
email: "[email protected]",
password: "new_password",
})
.post(
`/auth/user/emailpass/update`,
{
password: "new_password",
},
{
headers: {
authorization: `Bearer ${result}`,
},
}
)
.catch((e) => e)

expect(response.response.status).toEqual(401)
Expand Down Expand Up @@ -363,10 +373,17 @@ medusaIntegrationTestRunner({
jest.advanceTimersByTime(15 * 60 * 1000)

const response = await api
.post(`/auth/customer/emailpass/update?token=${result}`, {
email: "[email protected]",
password: "new_password",
})
.post(
`/auth/customer/emailpass/update`,
{
password: "new_password",
},
{
headers: {
authorization: `Bearer ${result}`,
},
}
)
.catch((e) => e)

expect(response.response.status).toEqual(401)
Expand Down Expand Up @@ -398,10 +415,17 @@ medusaIntegrationTestRunner({
jest.advanceTimersByTime(15 * 60 * 1000)

const response = await api
.post(`/auth/user/emailpass/update?token=${result}`, {
email: "[email protected]",
password: "new_password",
})
.post(
`/auth/user/emailpass/update`,
{
password: "new_password",
},
{
headers: {
authorization: `Bearer ${result}`,
},
}
)
.catch((e) => e)

expect(response.response.status).toEqual(401)
Expand Down
2 changes: 1 addition & 1 deletion packages/admin/dashboard/src/hooks/api/auth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export const useLogout = (options?: UseMutationOptions<void, FetchError>) => {

export const useUpdateProviderForEmailPass = (
token: string,
options?: UseMutationOptions<void, FetchError, { password: string }>
options?: UseMutationOptions<void, FetchError, HttpTypes.AdminUpdateProvider>
) => {
return useMutation({
mutationFn: (payload) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -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(
Expand Down Expand Up @@ -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,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ const getAuthContextFromSession = (
return null
}

const getAuthContextFromJwtToken = (
export const getAuthContextFromJwtToken = (
authHeader: string | undefined,
jwtSecret: string,
authTypes: AuthType[],
Expand Down
30 changes: 14 additions & 16 deletions packages/core/js-sdk/src/auth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -125,7 +125,7 @@ export class Auth {
* "google",
* {
* code: "123",
* state: "456"
* state: "456"
* }
* ).then((token) => {
* console.log(token)
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -280,16 +280,14 @@ export class Auth {
updateProvider = async (
actor: string,
provider: string,
body: Record<string, unknown>,
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}` },
})
}

/**
Expand Down
4 changes: 4 additions & 0 deletions packages/core/types/src/http/auth/payloads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
46 changes: 23 additions & 23 deletions packages/medusa/src/api/auth/utils/validate-token.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
AuthenticatedMedusaRequest,
getAuthContextFromJwtToken,
MedusaNextFunction,
MedusaRequest,
MedusaResponse,
Expand All @@ -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<HttpTypes.AdminUpdateProvider>,
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<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(
MedusaError.Types.UNAUTHORIZED,
`Invalid token`
Expand All @@ -33,27 +51,15 @@ export const validateToken = () => {
return next(errorObject)
}

// @ts-ignore
const { http } = req_.scope.resolve<ConfigModule>(
ContainerRegistrationKeys.CONFIG_MODULE
).projectConfig

const authModule = req.scope.resolve<IAuthModuleService>(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,
},
{
Expand All @@ -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!,
Expand Down
Loading