feat: Separate registration from authentication in auth domain (#8683)
* wip * feat: Introduce register * fix: user command * fix: Invite HTTP tests * fix: Auth tests * fix: Invite modules tests
This commit is contained in:
@@ -136,7 +136,7 @@ describe("Email password auth provider", () => {
|
||||
}),
|
||||
}
|
||||
|
||||
const resp = await emailpassService.authenticate(
|
||||
const resp = await emailpassService.register(
|
||||
{ body: { email: "test@admin.com", password: "test" } },
|
||||
authServiceSpies
|
||||
)
|
||||
@@ -151,4 +151,52 @@ describe("Email password auth provider", () => {
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("throw if auth identity with email already exists", async () => {
|
||||
const authServiceSpies = {
|
||||
retrieve: jest.fn().mockImplementation(() => {
|
||||
return { success: true }
|
||||
}),
|
||||
create: jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
provider_identities: [
|
||||
{
|
||||
entity_id: "test@admin.com",
|
||||
provider: "emailpass",
|
||||
provider_metadata: {
|
||||
password: "somehash",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
}),
|
||||
}
|
||||
|
||||
const resp = await emailpassService.register(
|
||||
{ body: { email: "test@admin.com", password: "test" } },
|
||||
authServiceSpies
|
||||
)
|
||||
|
||||
expect(authServiceSpies.retrieve).toHaveBeenCalled()
|
||||
|
||||
expect(resp.error).toEqual("Identity with email already exists")
|
||||
})
|
||||
|
||||
it("throws if auth identity with email doesn't exist", async () => {
|
||||
const authServiceSpies = {
|
||||
retrieve: jest.fn().mockImplementation(() => {
|
||||
throw new MedusaError(MedusaError.Types.NOT_FOUND, "Not found")
|
||||
}),
|
||||
create: jest.fn().mockImplementation(() => {}),
|
||||
}
|
||||
|
||||
const resp = await emailpassService.authenticate(
|
||||
{ body: { email: "test@admin.com", password: "test" } },
|
||||
authServiceSpies
|
||||
)
|
||||
|
||||
expect(authServiceSpies.retrieve).toHaveBeenCalled()
|
||||
|
||||
expect(resp.error).toEqual("Invalid email or password")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import {
|
||||
Logger,
|
||||
EmailPassAuthProviderOptions,
|
||||
AuthenticationResponse,
|
||||
AuthenticationInput,
|
||||
AuthIdentityProviderService,
|
||||
AuthenticationResponse,
|
||||
AuthIdentityDTO,
|
||||
AuthIdentityProviderService,
|
||||
EmailPassAuthProviderOptions,
|
||||
Logger,
|
||||
} from "@medusajs/types"
|
||||
import {
|
||||
AbstractAuthModuleProvider,
|
||||
MedusaError,
|
||||
isString,
|
||||
MedusaError,
|
||||
} from "@medusajs/utils"
|
||||
import Scrypt from "scrypt-kdf"
|
||||
|
||||
@@ -35,6 +35,26 @@ export class EmailPassAuthService extends AbstractAuthModuleProvider {
|
||||
this.logger_ = logger
|
||||
}
|
||||
|
||||
protected async createAuthIdentity({ email, password, authIdentityService }) {
|
||||
const hashConfig = this.config_.hashConfig ?? { logN: 15, r: 8, p: 1 }
|
||||
const passwordHash = await Scrypt.kdf(password, hashConfig)
|
||||
|
||||
const createdAuthIdentity = await authIdentityService.create({
|
||||
entity_id: email,
|
||||
provider_metadata: {
|
||||
password: passwordHash.toString("base64"),
|
||||
},
|
||||
})
|
||||
|
||||
const copy = JSON.parse(JSON.stringify(createdAuthIdentity))
|
||||
const providerIdentity = copy.provider_identities?.find(
|
||||
(pi) => pi.provider === this.provider
|
||||
)!
|
||||
delete providerIdentity.provider_metadata?.password
|
||||
|
||||
return copy
|
||||
}
|
||||
|
||||
async authenticate(
|
||||
userData: AuthenticationInput,
|
||||
authIdentityService: AuthIdentityProviderService
|
||||
@@ -54,6 +74,7 @@ export class EmailPassAuthService extends AbstractAuthModuleProvider {
|
||||
error: "Email should be a string",
|
||||
}
|
||||
}
|
||||
|
||||
let authIdentity: AuthIdentityDTO | undefined
|
||||
|
||||
try {
|
||||
@@ -62,25 +83,9 @@ export class EmailPassAuthService extends AbstractAuthModuleProvider {
|
||||
})
|
||||
} catch (error) {
|
||||
if (error.type === MedusaError.Types.NOT_FOUND) {
|
||||
const config = this.config_.hashConfig ?? { logN: 15, r: 8, p: 1 }
|
||||
const passwordHash = await Scrypt.kdf(password, config)
|
||||
|
||||
const createdAuthIdentity = await authIdentityService.create({
|
||||
entity_id: email,
|
||||
provider_metadata: {
|
||||
password: passwordHash.toString("base64"),
|
||||
},
|
||||
})
|
||||
|
||||
const copy = JSON.parse(JSON.stringify(createdAuthIdentity))
|
||||
const providerIdentity = copy.provider_identities?.find(
|
||||
(pi) => pi.provider === this.provider
|
||||
)!
|
||||
delete providerIdentity.provider_metadata?.password
|
||||
|
||||
return {
|
||||
success: true,
|
||||
authIdentity: copy,
|
||||
success: false,
|
||||
error: "Invalid email or password",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,4 +120,51 @@ export class EmailPassAuthService extends AbstractAuthModuleProvider {
|
||||
error: "Invalid email or password",
|
||||
}
|
||||
}
|
||||
|
||||
async register(
|
||||
userData: AuthenticationInput,
|
||||
authIdentityService: AuthIdentityProviderService
|
||||
): Promise<AuthenticationResponse> {
|
||||
const { email, password } = userData.body ?? {}
|
||||
|
||||
if (!password || !isString(password)) {
|
||||
return {
|
||||
success: false,
|
||||
error: "Password should be a string",
|
||||
}
|
||||
}
|
||||
|
||||
if (!email || !isString(email)) {
|
||||
return {
|
||||
success: false,
|
||||
error: "Email should be a string",
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await authIdentityService.retrieve({
|
||||
entity_id: email,
|
||||
})
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: "Identity with email already exists",
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.type === MedusaError.Types.NOT_FOUND) {
|
||||
const createdAuthIdentity = await this.createAuthIdentity({
|
||||
email,
|
||||
password,
|
||||
authIdentityService,
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
authIdentity: createdAuthIdentity,
|
||||
}
|
||||
}
|
||||
|
||||
return { success: false, error: error.message }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import {
|
||||
Logger,
|
||||
GoogleAuthProviderOptions,
|
||||
AuthenticationResponse,
|
||||
AuthenticationInput,
|
||||
AuthenticationResponse,
|
||||
AuthIdentityProviderService,
|
||||
GoogleAuthProviderOptions,
|
||||
Logger,
|
||||
} from "@medusajs/types"
|
||||
import { AbstractAuthModuleProvider, MedusaError } from "@medusajs/utils"
|
||||
import jwt, { JwtPayload } from "jsonwebtoken"
|
||||
@@ -29,6 +29,13 @@ export class GoogleAuthService extends AbstractAuthModuleProvider {
|
||||
this.logger_ = logger
|
||||
}
|
||||
|
||||
async register(_): Promise<AuthenticationResponse> {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_ALLOWED,
|
||||
"Google does not support registration. Use method `authenticate` instead."
|
||||
)
|
||||
}
|
||||
|
||||
async authenticate(
|
||||
req: AuthenticationInput
|
||||
): Promise<AuthenticationResponse> {
|
||||
|
||||
Reference in New Issue
Block a user