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
2024-09-06 12:28:29 +02:00
committed by GitHub
parent 2c5e72d141
commit e27056b3c3
29 changed files with 633 additions and 194 deletions
@@ -215,7 +215,7 @@ moduleIntegrationTestRunner<IAuthModuleService>({
let error
try {
await service.updateAuthIdentites([
await service.updateAuthIdentities([
{
id: "does-not-exist",
},
@@ -230,7 +230,7 @@ moduleIntegrationTestRunner<IAuthModuleService>({
})
it("should update authIdentity", async () => {
await service.updateAuthIdentites([
await service.updateAuthIdentities([
{
id,
app_metadata: { email: "test@email.com" },
@@ -364,7 +364,7 @@ moduleIntegrationTestRunner<IAuthModuleService>({
let error
try {
await service.updateProviderIdentites([
await service.updateProviderIdentities([
{
id: "does-not-exist",
},
@@ -382,18 +382,18 @@ moduleIntegrationTestRunner<IAuthModuleService>({
let [providerIdentity] = await service.listProviderIdentities({
entity_id,
})
await service.updateProviderIdentites([
await service.updateProviderIdentities([
{
id: providerIdentity.id,
provider_metadata: { email: "test@email.com" },
},
])
const providerIdentites = await service.listProviderIdentities({
const providerIdentities = await service.listProviderIdentities({
id: [providerIdentity.id],
})
expect(providerIdentites[0]).toEqual(
expect(providerIdentities[0]).toEqual(
expect.objectContaining({
provider_metadata: expect.objectContaining({
email: "test@email.com",
@@ -1,8 +1,8 @@
import { IAuthModuleService } from "@medusajs/types"
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
import { resolve } from "path"
import { Module, Modules } from "@medusajs/utils"
import { AuthModuleService } from "@services"
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
import { resolve } from "path"
let moduleOptions = {
providers: [
@@ -42,7 +42,10 @@ moduleIntegrationTestRunner({
service: AuthModuleService,
}).linkable
expect(Object.keys(linkable)).toEqual(["authIdentity"])
expect(Object.keys(linkable)).toEqual([
"authIdentity",
"providerIdentity",
])
linkable.authIdentity.toJSON = undefined
@@ -54,6 +57,17 @@ moduleIntegrationTestRunner({
field: "authIdentity",
},
})
linkable.providerIdentity.toJSON = undefined
expect(linkable.providerIdentity).toEqual({
id: {
linkable: "provider_identity_id",
primaryKey: "id",
serviceName: "auth",
field: "providerIdentity",
},
})
})
it("it fails if the provider does not exist", async () => {
+2 -2
View File
@@ -1,6 +1,6 @@
import { AuthIdentity } from "@models"
import { defineJoinerConfig, Modules } from "@medusajs/utils"
import { AuthIdentity, ProviderIdentity } from "@models"
export const joinerConfig = defineJoinerConfig(Modules.AUTH, {
models: [AuthIdentity],
models: [AuthIdentity, ProviderIdentity],
})
@@ -9,17 +9,14 @@ import {
ModuleJoinerConfig,
ModulesSdkTypes,
} from "@medusajs/types"
import { AuthIdentity, ProviderIdentity } from "@models"
import { joinerConfig } from "../joiner-config"
import {
InjectManager,
MedusaContext,
MedusaError,
MedusaService,
} from "@medusajs/utils"
import { AuthIdentity, ProviderIdentity } from "@models"
import { joinerConfig } from "../joiner-config"
import AuthProviderService from "./auth-provider"
type InjectedDependencies = {
@@ -92,18 +89,19 @@ export default class AuthModuleService
}
// TODO: Update to follow convention
updateAuthIdentites(
// @ts-expect-error
updateAuthIdentities(
data: AuthTypes.UpdateAuthIdentityDTO[],
sharedContext?: Context
): Promise<AuthTypes.AuthIdentityDTO[]>
updateAuthIdentites(
updateAuthIdentities(
data: AuthTypes.UpdateAuthIdentityDTO,
sharedContext?: Context
): Promise<AuthTypes.AuthIdentityDTO>
@InjectManager("baseRepository_")
async updateAuthIdentites(
async updateAuthIdentities(
data: AuthTypes.UpdateAuthIdentityDTO | AuthTypes.UpdateAuthIdentityDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<AuthTypes.AuthIdentityDTO | AuthTypes.AuthIdentityDTO[]> {
@@ -135,6 +133,7 @@ export default class AuthModuleService
return { success: false, error: error.message }
}
}
// @ts-expect-error
createProviderIdentities(
data: AuthTypes.CreateProviderIdentityDTO[],
@@ -163,18 +162,19 @@ export default class AuthModuleService
>(providerIdentities)
}
updateProviderIdentites(
// @ts-expect-error
updateProviderIdentities(
data: AuthTypes.UpdateProviderIdentityDTO[],
sharedContext?: Context
): Promise<AuthTypes.ProviderIdentityDTO[]>
updateProviderIdentites(
updateProviderIdentities(
data: AuthTypes.UpdateProviderIdentityDTO,
sharedContext?: Context
): Promise<AuthTypes.ProviderIdentityDTO>
@InjectManager("baseRepository_")
async updateProviderIdentites(
async updateProviderIdentities(
data:
| AuthTypes.UpdateProviderIdentityDTO
| AuthTypes.UpdateProviderIdentityDTO[],
@@ -192,6 +192,21 @@ export default class AuthModuleService
return Array.isArray(data) ? serializedProviders : serializedProviders[0]
}
async updateProvider(
provider: string,
data: Record<string, unknown>
): Promise<AuthenticationResponse> {
try {
return await this.authProviderService_.update(
provider,
data,
this.getAuthIdentityProviderService(provider)
)
} catch (error) {
return { success: false, error: error.message }
}
}
async authenticate(
provider: string,
authenticationData: AuthenticationInput
@@ -2,7 +2,7 @@ import {
AuthIdentityProviderService,
AuthTypes,
AuthenticationInput,
AuthenticationResponse,
AuthenticationResponse
} from "@medusajs/types"
import { MedusaError } from "@medusajs/utils"
import { AuthProviderRegistrationPrefix } from "@types"
@@ -51,6 +51,15 @@ export default class AuthProviderService {
return await providerHandler.register(auth, authIdentityProviderService)
}
async update(
provider: string,
data: Record<string, unknown>,
authIdentityProviderService: AuthIdentityProviderService
): Promise<AuthenticationResponse> {
const providerHandler = this.retrieveProviderRegistration(provider)
return await providerHandler.update(data, authIdentityProviderService)
}
async validateCallback(
provider: string,
auth: AuthenticationInput,
@@ -35,14 +35,56 @@ export class EmailPassAuthService extends AbstractAuthModuleProvider {
this.logger_ = logger
}
protected async createAuthIdentity({ email, password, authIdentityService }) {
protected async hashPassword(password: string) {
const hashConfig = this.config_.hashConfig ?? { logN: 15, r: 8, p: 1 }
const passwordHash = await Scrypt.kdf(password, hashConfig)
return passwordHash.toString("base64")
}
async update(
data: { email: string; password: string },
authIdentityService: AuthIdentityProviderService
) {
const { email, password } = data ?? {}
if (!email || !isString(email)) {
return {
success: false,
error: `Cannot update ${this.provider} provider identity without email`,
}
}
if (!password || !isString(password)) {
return { success: true }
}
let authIdentity
try {
const passwordHash = await this.hashPassword(password)
authIdentity = await authIdentityService.update(email, {
provider_metadata: {
password: passwordHash,
},
})
} catch (error) {
return { success: false, error: error.message }
}
return {
success: true,
authIdentity,
}
}
protected async createAuthIdentity({ email, password, authIdentityService }) {
const passwordHash = await this.hashPassword(password)
const createdAuthIdentity = await authIdentityService.create({
entity_id: email,
provider_metadata: {
password: passwordHash.toString("base64"),
password: passwordHash,
},
})