feat: Reset password (#8962)
* wip * more work * wip * more work * wrap up first iteration * work on new approach * more work * move middleware func to route * cleanup * more work * wrap up * more work * fix workflow * minor tweaks * finalize * Use JWT secret instead
This commit is contained in:
@@ -1,8 +1,4 @@
|
||||
import {
|
||||
AuthenticationInput,
|
||||
ConfigModule,
|
||||
IAuthModuleService,
|
||||
} from "@medusajs/types"
|
||||
import { AuthenticationInput, IAuthModuleService } from "@medusajs/types"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
MedusaError,
|
||||
@@ -13,21 +9,6 @@ import { MedusaRequest, MedusaResponse } from "../../../../../types/routing"
|
||||
|
||||
export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
|
||||
const { actor_type, auth_provider } = req.params
|
||||
const config: ConfigModule = req.scope.resolve(
|
||||
ContainerRegistrationKeys.CONFIG_MODULE
|
||||
)
|
||||
|
||||
const authMethodsPerActor =
|
||||
config.projectConfig?.http?.authMethodsPerActor ?? {}
|
||||
// Not having the config defined would allow for all auth providers for the particular actor.
|
||||
if (authMethodsPerActor[actor_type]) {
|
||||
if (!authMethodsPerActor[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
|
||||
|
||||
@@ -17,18 +17,6 @@ export const POST = async (req: MedusaRequest, res: MedusaResponse) => {
|
||||
ContainerRegistrationKeys.CONFIG_MODULE
|
||||
)
|
||||
|
||||
const authMethodsPerActor =
|
||||
config.projectConfig?.http?.authMethodsPerActor ?? {}
|
||||
// Not having the config defined would allow for all auth providers for the particular actor.
|
||||
if (authMethodsPerActor[actor_type]) {
|
||||
if (!authMethodsPerActor[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
|
||||
)
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { generateResetPasswordTokenWorkflow } from "@medusajs/core-flows"
|
||||
import { ContainerRegistrationKeys } from "@medusajs/utils"
|
||||
import {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "../../../../../types/routing"
|
||||
|
||||
export const POST = async (
|
||||
req: AuthenticatedMedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const { auth_provider } = req.params
|
||||
const { identifier } = req.body
|
||||
|
||||
const { http } = req.scope.resolve(
|
||||
ContainerRegistrationKeys.CONFIG_MODULE
|
||||
).projectConfig
|
||||
|
||||
await generateResetPasswordTokenWorkflow(req.scope).run({
|
||||
input: {
|
||||
entityId: identifier,
|
||||
provider: auth_provider,
|
||||
secret: http.jwtSecret as string,
|
||||
},
|
||||
throwOnError: false, // we don't want to throw on error to avoid leaking information about non-existing identities
|
||||
})
|
||||
|
||||
res.sendStatus(201)
|
||||
}
|
||||
|
||||
export const AUTHENTICATE = false
|
||||
@@ -17,18 +17,6 @@ export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
|
||||
ContainerRegistrationKeys.CONFIG_MODULE
|
||||
)
|
||||
|
||||
const authMethodsPerActor =
|
||||
config.projectConfig?.http?.authMethodsPerActor ?? {}
|
||||
// Not having the config defined would allow for all auth providers for the particular actor.
|
||||
if (authMethodsPerActor[actor_type]) {
|
||||
if (!authMethodsPerActor[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
|
||||
)
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { AuthenticatedMedusaRequest } from "@medusajs/framework"
|
||||
import { IAuthModuleService } from "@medusajs/types"
|
||||
import { MedusaError, ModuleRegistrationName } from "@medusajs/utils"
|
||||
import { MedusaResponse } from "../../../../../types/routing"
|
||||
|
||||
export const POST = async (
|
||||
req: AuthenticatedMedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const { auth_provider } = req.params
|
||||
|
||||
const authService = req.scope.resolve<IAuthModuleService>(
|
||||
ModuleRegistrationName.AUTH
|
||||
)
|
||||
|
||||
const { authIdentity, success, error } = await authService.updateProvider(
|
||||
auth_provider,
|
||||
req.body as Record<string, unknown>
|
||||
)
|
||||
|
||||
if (success && authIdentity) {
|
||||
return res.status(200).json({ success: true })
|
||||
}
|
||||
|
||||
throw new MedusaError(MedusaError.Types.UNAUTHORIZED, error || "Unauthorized")
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
import { MiddlewareRoute } from "@medusajs/framework"
|
||||
import { authenticate } from "../../utils/middlewares/authenticate-middleware"
|
||||
import { validateScopeProviderAssociation } from "./utils/validate-scope-provider-association"
|
||||
import { validateToken } from "./utils/validate-token"
|
||||
|
||||
export const authRoutesMiddlewares: MiddlewareRoute[] = [
|
||||
{
|
||||
@@ -15,21 +17,31 @@ export const authRoutesMiddlewares: MiddlewareRoute[] = [
|
||||
{
|
||||
method: ["POST"],
|
||||
matcher: "/auth/:actor_type/:auth_provider/callback",
|
||||
middlewares: [],
|
||||
middlewares: [validateScopeProviderAssociation()],
|
||||
},
|
||||
{
|
||||
method: ["POST"],
|
||||
matcher: "/auth/:actor_type/:auth_provider/register",
|
||||
middlewares: [],
|
||||
middlewares: [validateScopeProviderAssociation()],
|
||||
},
|
||||
{
|
||||
method: ["POST"],
|
||||
matcher: "/auth/:actor_type/:auth_provider",
|
||||
middlewares: [],
|
||||
middlewares: [validateScopeProviderAssociation()],
|
||||
},
|
||||
{
|
||||
method: ["GET"],
|
||||
matcher: "/auth/:actor_type/:auth_provider",
|
||||
middlewares: [],
|
||||
middlewares: [validateScopeProviderAssociation()],
|
||||
},
|
||||
{
|
||||
method: ["POST"],
|
||||
matcher: "/auth/:actor_type/:auth_provider/reset-password",
|
||||
middlewares: [validateScopeProviderAssociation()],
|
||||
},
|
||||
{
|
||||
method: ["POST"],
|
||||
matcher: "/auth/:actor_type/:auth_provider/update",
|
||||
middlewares: [validateScopeProviderAssociation(), validateToken()],
|
||||
},
|
||||
]
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import {
|
||||
MedusaNextFunction,
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/framework"
|
||||
import { ConfigModule } from "@medusajs/types"
|
||||
import { ContainerRegistrationKeys, MedusaError } from "@medusajs/utils"
|
||||
|
||||
// Middleware to validate that a scope is associated with a provider
|
||||
export const validateScopeProviderAssociation = () => {
|
||||
return async (
|
||||
req: MedusaRequest,
|
||||
_: MedusaResponse,
|
||||
next: MedusaNextFunction
|
||||
) => {
|
||||
const { actor_type, auth_provider } = req.params
|
||||
const config: ConfigModule = req.scope.resolve(
|
||||
ContainerRegistrationKeys.CONFIG_MODULE
|
||||
)
|
||||
|
||||
const authMethodsPerActor =
|
||||
config.projectConfig?.http?.authMethodsPerActor ?? {}
|
||||
// Not having the config defined would allow for all auth providers for the particular actor.
|
||||
if (authMethodsPerActor[actor_type]) {
|
||||
if (!authMethodsPerActor[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}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
next()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaNextFunction,
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/framework"
|
||||
import { ConfigModule, IAuthModuleService } from "@medusajs/types"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
ModuleRegistrationName,
|
||||
} from "@medusajs/utils"
|
||||
import { decode, JwtPayload, verify } from "jsonwebtoken"
|
||||
|
||||
// Middleware to validate that a token is valid
|
||||
export const validateToken = () => {
|
||||
return async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse,
|
||||
next: MedusaNextFunction
|
||||
) => {
|
||||
const { actor_type, auth_provider } = req.params
|
||||
const { token } = req.query
|
||||
|
||||
const req_ = req as AuthenticatedMedusaRequest
|
||||
|
||||
if (!token) {
|
||||
return next()
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const { http } = req_.scope.resolve<ConfigModule>(
|
||||
ContainerRegistrationKeys.CONFIG_MODULE
|
||||
).projectConfig
|
||||
|
||||
const authModule = req.scope.resolve<IAuthModuleService>(
|
||||
ModuleRegistrationName.AUTH
|
||||
)
|
||||
|
||||
let decoded = decode(token as string) as JwtPayload
|
||||
|
||||
const [providerIdentity] = await authModule.listProviderIdentities(
|
||||
{
|
||||
entity_id: decoded.entity_id,
|
||||
provider: auth_provider,
|
||||
},
|
||||
{
|
||||
select: ["provider_metadata", "auth_identity_id", "entity_id"],
|
||||
}
|
||||
)
|
||||
|
||||
if (!providerIdentity) {
|
||||
return res.status(401).json({ message: "Invalid token" })
|
||||
}
|
||||
|
||||
let verified: JwtPayload | null = null
|
||||
|
||||
try {
|
||||
verified = verify(token as string, http.jwtSecret as string) as JwtPayload
|
||||
} catch (error) {
|
||||
return res.status(401).json({ message: "Invalid token" })
|
||||
}
|
||||
|
||||
req_.auth_context = {
|
||||
actor_type,
|
||||
auth_identity_id: verified.auth_identity_id!,
|
||||
actor_id: providerIdentity.entity_id,
|
||||
app_metadata: {},
|
||||
}
|
||||
|
||||
return next()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user