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:
Oli Juhl authored and GitHub committed 2024-09-06 12:28:29 +02:00
1 parent 2c5e72d141
commit e27056b3c3
29 files changed
+633 -194

No files matched your search

@@ -1 +1,3 @@
export * from "./steps"
export * from "./workflows"
@@ -30,7 +30,7 @@ export const setAuthAppMetadataStep = createStep(
appMetadata[key] = data.value
await service.updateAuthIdentites({
await service.updateAuthIdentities({
id: authIdentity.id,
app_metadata: appMetadata,
})
@@ -58,7 +58,7 @@ export const setAuthAppMetadataStep = createStep(
delete appMetadata[key]
}
await service.updateAuthIdentites({
await service.updateAuthIdentities({
id: authIdentity.id,
app_metadata: appMetadata,
})
@@ -0,0 +1,61 @@
import {
AuthWorkflowEvents,
generateJwtToken,
MedusaError,
} from "@medusajs/utils";
import {
createWorkflow,
transform,
WorkflowResponse,
} from "@medusajs/workflows-sdk";
import { emitEventStep, useRemoteQueryStep } from "../../common";
export const generateResetPasswordTokenWorkflow = createWorkflow(
"generate-reset-password-token",
(input: { entityId: string; provider: string; secret: string }) => {
const providerIdentities = useRemoteQueryStep({
entry_point: "provider_identity",
fields: ["auth_identity_id", "provider_metadata"],
variables: {
filters: {
entity_id: input.entityId,
provider: input.provider,
},
},
})
const token = transform(
{ input, providerIdentities },
({ input, providerIdentities }) => {
const providerIdentity = providerIdentities?.[0]
if (!providerIdentity) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
`Provider identity with entity_id ${input.entityId} and provider ${input.provider} not found`
)
}
const token = generateJwtToken(
{
entity_id: input.entityId,
provider: input.provider,
},
{
secret: input.secret,
expiresIn: "15m",
}
)
return token
}
)
emitEventStep({
eventName: AuthWorkflowEvents.PASSWORD_RESET,
data: { entity_id: input.entityId, token },
})
return new WorkflowResponse(token)
}
)
@@ -0,0 +1 @@
export * from "./generate-reset-password-token"
@@ -83,6 +83,11 @@ export type ProviderIdentityDTO = {
*/
entity_id: string
/**
* The ID of the auth identity linked to the provider identity.
*/
auth_identity_id?: string
/**
* The auth identity linked to the provider identity.
*/
@@ -205,6 +210,11 @@ export interface FilterableProviderIdentityProps
*/
entity_id?: string
/**
* Filter the provider identities by the ID of the auth identity they are linked to.
*/
auth_identity_id?: string
/**
* The provider handle to filter the provider identity by.
*/
+4
View File
@@ -41,4 +41,8 @@ export interface IAuthProvider {
data: AuthenticationInput,
authIdentityProviderService: AuthIdentityProviderService
): Promise<AuthenticationResponse>
update: (
data: Record<string, unknown>,
authIdentityProviderService: AuthIdentityProviderService
) => Promise<AuthenticationResponse>
}
+12 -7
View File
@@ -54,6 +54,11 @@ export interface IAuthModuleService extends IModuleService {
providerData: AuthenticationInput
): Promise<AuthenticationResponse>
updateProvider(
provider: string,
providerData: Record<string, unknown>
): Promise<AuthenticationResponse>
/**
* When authenticating users with a third-party provider, such as Google, the user performs an
* action to finish the authentication, such as enter their credentials in Google's sign-in
@@ -244,13 +249,13 @@ export interface IAuthModuleService extends IModuleService {
* @returns {Promise<AuthIdentityDTO[]>} The updated auths.
*
* @example
* const authIdentities = await authModuleService.updateAuthIdentites([
* const authIdentities = await authModuleService.updateAuthIdentities([
* {
* id: "authusr_123",
* },
* ])
*/
updateAuthIdentites(
updateAuthIdentities(
data: UpdateAuthIdentityDTO[],
sharedContext?: Context
): Promise<AuthIdentityDTO[]>
@@ -263,11 +268,11 @@ export interface IAuthModuleService extends IModuleService {
* @returns {Promise<AuthIdentityDTO>} The updated auth.
*
* @example
* const authIdentity = await authModuleService.updateAuthIdentites({
* const authIdentity = await authModuleService.updateAuthIdentities({
* id: "authusr_123",
* })
*/
updateAuthIdentites(
updateAuthIdentities(
data: UpdateAuthIdentityDTO,
sharedContext?: Context
): Promise<AuthIdentityDTO>
@@ -399,7 +404,7 @@ export interface IAuthModuleService extends IModuleService {
* },
* ])
*/
updateProviderIdentites(
updateProviderIdentities(
data: UpdateProviderIdentityDTO[],
sharedContext?: Context
): Promise<ProviderIdentityDTO[]>
@@ -412,11 +417,11 @@ export interface IAuthModuleService extends IModuleService {
* @returns {Promise<ProviderIdentityDTO>} The updated provider identity.
*
* @example
* const providerIdentity = await authModuleService.updateProviderIdentites({
* const providerIdentity = await authModuleService.updateProviderIdentities({
* id: "provider_123",
* })
*/
updateProviderIdentites(
updateProviderIdentities(
data: UpdateProviderIdentityDTO,
sharedContext?: Context
): Promise<ProviderIdentityDTO>
@@ -2,7 +2,7 @@ import {
AuthenticationInput,
AuthenticationResponse,
AuthIdentityProviderService,
IAuthProvider,
IAuthProvider
} from "@medusajs/types"
/**
@@ -238,6 +238,15 @@ export abstract class AbstractAuthModuleProvider implements IAuthProvider {
)
}
update(
data: Record<string, unknown>,
authIdentityProviderService: AuthIdentityProviderService
): Promise<AuthenticationResponse> {
throw new Error(
`Method 'update' not implemented for provider ${this.provider}`
)
}
/**
* This method validates the callback of an authentication request.
*
@@ -10,6 +10,16 @@ export const OrderWorkflowEvents = {
COMPLETED: "order.completed",
}
export const UserWorkflowEvents = {
CREATED: "user.created",
UPDATED: "user.updated",
DELETED: "user.deleted",
}
export const AuthWorkflowEvents = {
PASSWORD_RESET: "auth.password_reset",
}
export const SalesChannelWorkflowEvents = {
CREATED: "sales-channel.created",
UPDATED: "sales-channel.updated",
-6
View File
@@ -7,9 +7,3 @@ export const UserEvents = {
...buildEventNamesFromEntityName(eventBaseNames, Modules.USER),
INVITE_TOKEN_GENERATED: `${Modules.USER}.user.invite.token_generated`,
}
export const UserWorkflowEvents = {
CREATED: "user.created",
UPDATED: "user.updated",
DELETED: "user.deleted",
}