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

View File

@@ -0,0 +1,236 @@
import { IAuthModuleService } from "@medusajs/types"
import { Modules } from "@medusajs/modules-sdk"
import { createAuthIdentities } from "../../__fixtures__/auth-identity"
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
jest.setTimeout(30000)
moduleIntegrationTestRunner({
moduleName: Modules.AUTH,
testSuite: ({
MikroOrmWrapper,
service,
}: SuiteOptions<IAuthModuleService>) => {
describe("AuthModuleService - AuthIdentity", () => {
beforeEach(async () => {
await createAuthIdentities(MikroOrmWrapper.forkManager())
})
describe("listAuthIdentities", () => {
it("should list authIdentities", async () => {
const authIdentities = await service.list()
expect(authIdentities).toEqual([
expect.objectContaining({
provider: "store",
}),
expect.objectContaining({
provider: "manual",
}),
expect.objectContaining({
provider: "manual",
}),
])
})
it("should list authIdentities by id", async () => {
const authIdentities = await service.list({
id: ["test-id"],
})
expect(authIdentities).toEqual([
expect.objectContaining({
id: "test-id",
}),
])
})
it("should list authIdentities by provider", async () => {
const authIdentities = await service.list({
provider: "manual",
})
expect(authIdentities).toEqual([
expect.objectContaining({
id: "test-id",
}),
expect.objectContaining({
id: "test-id-1",
}),
])
})
})
describe("listAndCountAuthIdentities", () => {
it("should list and count authIdentities", async () => {
const [authIdentities, count] = await service.listAndCount()
expect(count).toEqual(3)
expect(authIdentities).toEqual([
expect.objectContaining({
provider: "store",
}),
expect.objectContaining({
provider: "manual",
}),
expect.objectContaining({
provider: "manual",
}),
])
})
it("should listAndCount authIdentities by provider_id", async () => {
const [authIdentities, count] = await service.listAndCount({
provider: "manual",
})
expect(count).toEqual(2)
expect(authIdentities).toEqual([
expect.objectContaining({
id: "test-id",
}),
expect.objectContaining({
id: "test-id-1",
}),
])
})
})
describe("retrieveAuthIdentity", () => {
const id = "test-id"
it("should return an authIdentity for the given id", async () => {
const authIdentity = await service.retrieve(id)
expect(authIdentity).toEqual(
expect.objectContaining({
id,
})
)
})
it("should throw an error when an authIdentity with the given id does not exist", async () => {
let error
try {
await service.retrieve("does-not-exist")
} catch (e) {
error = e
}
expect(error.message).toEqual(
"AuthIdentity with id: does-not-exist was not found"
)
})
it("should not return an authIdentity with password hash", async () => {
const authIdentity = await service.retrieve("test-id-1")
expect(authIdentity).toEqual(
expect.objectContaining({
id: "test-id-1",
})
)
expect(authIdentity["password_hash"]).toEqual(undefined)
})
it("should throw an error when a authIdentityId is not provided", async () => {
let error
try {
await service.retrieve(undefined as unknown as string)
} catch (e) {
error = e
}
expect(error.message).toEqual("authIdentity - id must be defined")
})
it("should return authIdentity based on config select param", async () => {
const authIdentity = await service.retrieve(id, {
select: ["id"],
})
expect(authIdentity).toEqual({
id,
})
})
})
describe("deleteAuthIdentity", () => {
const id = "test-id"
it("should delete the authIdentities given an id successfully", async () => {
await service.delete([id])
const authIdentities = await service.list({
id: [id],
})
expect(authIdentities).toHaveLength(0)
})
})
describe("updateAuthIdentity", () => {
const id = "test-id"
it("should throw an error when a id does not exist", async () => {
let error
try {
await service.update([
{
id: "does-not-exist",
},
])
} catch (e) {
error = e
}
expect(error.message).toEqual(
'AuthIdentity with id "does-not-exist" not found'
)
})
it("should update authIdentity", async () => {
await service.update([
{
id,
provider_metadata: { email: "test@email.com" },
},
])
const [authIdentity] = await service.list({ id: [id] })
expect(authIdentity).toEqual(
expect.objectContaining({
provider_metadata: { email: "test@email.com" },
})
)
})
})
describe("createAuthIdentity", () => {
it("should create a authIdentity successfully", async () => {
await service.create([
{
id: "test",
provider: "manual",
entity_id: "test",
},
])
const [authIdentity, count] = await service.listAndCount({
id: ["test"],
})
expect(count).toEqual(1)
expect(authIdentity[0]).toEqual(
expect.objectContaining({
id: "test",
})
)
})
})
})
},
})

View File

@@ -0,0 +1,110 @@
import { Modules } from "@medusajs/modules-sdk"
import { IAuthModuleService } from "@medusajs/types"
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
import { resolve } from "path"
let moduleOptions = {
providers: [
{
resolve: resolve(
process.cwd() +
"/integration-tests/__fixtures__/providers/default-provider"
),
options: {
config: {
plaintextpass: {},
},
},
},
],
}
jest.setTimeout(30000)
moduleIntegrationTestRunner({
moduleName: Modules.AUTH,
moduleOptions,
testSuite: ({ service }: SuiteOptions<IAuthModuleService>) =>
describe("Auth Module Service", () => {
beforeEach(async () => {
await service.create({
entity_id: "test@admin.com",
provider: "plaintextpass",
provider_metadata: {
password: "plaintext",
},
})
})
it("it fails if the provider does not exist", async () => {
const err = await service
.authenticate("facebook", {
body: {
email: "test@admin.com",
password: "password",
},
})
.catch((e) => e)
expect(err).toEqual({
success: false,
error: "Could not find a auth provider with id: facebook",
})
})
it("successfully calls the provider for authentication if correct password", async () => {
const result = await service.authenticate("plaintextpass", {
body: {
email: "test@admin.com",
password: "plaintext",
},
})
expect(result).toEqual(
expect.objectContaining({
success: true,
authIdentity: expect.objectContaining({
id: expect.any(String),
entity_id: "test@admin.com",
}),
})
)
})
it("should fail if the password is incorrect", async () => {
const result = await service
.authenticate("plaintextpass", {
body: {
email: "test@admin.com",
password: "incorrect",
},
})
.catch((e) => e)
expect(result).toEqual(
expect.objectContaining({
success: false,
error: "Invalid email or password",
})
)
})
it("successfully create a new entity if nonexistent", async () => {
const result = await service.authenticate("plaintextpass", {
body: {
email: "new@admin.com",
password: "newpass",
},
})
const dbAuthIdentity = await service.retrieve(result.authIdentity.id)
expect(dbAuthIdentity).toEqual(
expect.objectContaining({
id: expect.any(String),
entity_id: "new@admin.com",
})
)
})
}),
})