Revamp the authentication setup (#7419)
* feat: Add email pass authentication provider package * feat: Revamp auth module and remove concept of scope * feat: Revamp the auth module to be more standardized in how providers are loaded * feat: Switch from scope to actor type for authentication * feat: Add support for per-actor auth methods * feat: Add emailpass auth provider by default * fix: Add back app_metadata in auth module
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
import { ModuleProviderExports } from "@medusajs/types"
|
||||
import { EmailPassAuthService } from "./services/emailpass"
|
||||
|
||||
const services = [EmailPassAuthService]
|
||||
|
||||
const providerExport: ModuleProviderExports = {
|
||||
services,
|
||||
}
|
||||
|
||||
export default providerExport
|
||||
@@ -0,0 +1,110 @@
|
||||
import {
|
||||
Logger,
|
||||
EmailPassAuthProviderOptions,
|
||||
AuthenticationResponse,
|
||||
AuthenticationInput,
|
||||
AuthIdentityProviderService,
|
||||
} from "@medusajs/types"
|
||||
import {
|
||||
AbstractAuthModuleProvider,
|
||||
MedusaError,
|
||||
isString,
|
||||
} from "@medusajs/utils"
|
||||
import Scrypt from "scrypt-kdf"
|
||||
|
||||
type InjectedDependencies = {
|
||||
logger: Logger
|
||||
}
|
||||
|
||||
interface LocalServiceConfig extends EmailPassAuthProviderOptions {}
|
||||
|
||||
export class EmailPassAuthService extends AbstractAuthModuleProvider {
|
||||
protected config_: LocalServiceConfig
|
||||
protected logger_: Logger
|
||||
|
||||
constructor(
|
||||
{ logger }: InjectedDependencies,
|
||||
options: EmailPassAuthProviderOptions
|
||||
) {
|
||||
super(
|
||||
{},
|
||||
{ provider: "emailpass", displayName: "Email/Password Authentication" }
|
||||
)
|
||||
this.config_ = options
|
||||
this.logger_ = logger
|
||||
}
|
||||
|
||||
async authenticate(
|
||||
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",
|
||||
}
|
||||
}
|
||||
let authIdentity
|
||||
|
||||
try {
|
||||
authIdentity = await authIdentityService.retrieve({
|
||||
entity_id: email,
|
||||
provider: this.provider,
|
||||
})
|
||||
} 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: this.provider,
|
||||
provider_metadata: {
|
||||
password: passwordHash.toString("base64"),
|
||||
},
|
||||
})
|
||||
|
||||
const copy = JSON.parse(JSON.stringify(createdAuthIdentity))
|
||||
delete copy.provider_metadata?.password
|
||||
|
||||
return {
|
||||
success: true,
|
||||
authIdentity: copy,
|
||||
}
|
||||
}
|
||||
|
||||
return { success: false, error: error.message }
|
||||
}
|
||||
|
||||
const passwordHash = authIdentity.provider_metadata?.password
|
||||
|
||||
if (isString(passwordHash)) {
|
||||
const buf = Buffer.from(passwordHash as string, "base64")
|
||||
const success = await Scrypt.verify(buf, password)
|
||||
|
||||
if (success) {
|
||||
const copy = JSON.parse(JSON.stringify(authIdentity))
|
||||
delete copy.provider_metadata!.password
|
||||
|
||||
return {
|
||||
success,
|
||||
authIdentity: copy,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: "Invalid email or password",
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user