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:
Stevche Radevski
2024-05-23 20:56:40 +02:00
committed by GitHub
parent 7b0cfe3b77
commit 8a070d5d85
100 changed files with 991 additions and 1005 deletions
@@ -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",
}
}
}