Feat(auth): Rename authentication to auth (#6229)

**What**
- rename `authenticationModule` -> `authModule`
This commit is contained in:
Philip Korsholm authored and GitHub committed 2024-01-29 10:19:30 +00:00
1 parent a41aad4bea
commit 512b041929
70 files changed
+245 -147

No files matched your search

+6
View File
@@ -0,0 +1,6 @@
/dist
node_modules
.DS_store
.env*
.env
*.sql
+14
View File
@@ -0,0 +1,14 @@
# @medusajs/auth
## 0.0.2
### Patch Changes
- [#6062](https://github.com/medusajs/medusa/pull/6062) [`72bc52231c`](https://github.com/medusajs/medusa/commit/72bc52231ca3a72fa6d197a248fe07a938ed0d85) Thanks [@adrien2p](https://github.com/adrien2p)! - chore(utils): Update base repository to infer primary keys and support composite
- [#6035](https://github.com/medusajs/medusa/pull/6035) [`b6ac768698`](https://github.com/medusajs/medusa/commit/b6ac768698a3b49d0162cb49e628386f3352d034) Thanks [@adrien2p](https://github.com/adrien2p)! - chore: Attempt to abstract the modules repository
- Updated dependencies [[`68ddd866a5`](https://github.com/medusajs/medusa/commit/68ddd866a5ff9414e2db5b80d75acc5e81948540), [`72bc52231c`](https://github.com/medusajs/medusa/commit/72bc52231ca3a72fa6d197a248fe07a938ed0d85), [`99045848f`](https://github.com/medusajs/medusa/commit/99045848fd3e863359c7878d9bc05271ed083a0e), [`af7af7374`](https://github.com/medusajs/medusa/commit/af7af737455daa0f330840a9678e6339e519dfe6), [`fc6b1772a7`](https://github.com/medusajs/medusa/commit/fc6b1772a71582bb48602c5cac7b2297e9d267a9), [`a9b4214503`](https://github.com/medusajs/medusa/commit/a9b42145032ee88aa922a11fe03e777b140c68f4), [`d85fee42e`](https://github.com/medusajs/medusa/commit/d85fee42ee7f661310584dfee5741d6c53b989bb), [`5e655dd59`](https://github.com/medusajs/medusa/commit/5e655dd59bda4ffface28db38021ba71cae6de10), [`b132ff7669`](https://github.com/medusajs/medusa/commit/b132ff76693148b3a06373c168e8dd5e02970757), [`e28fa7fbdf`](https://github.com/medusajs/medusa/commit/e28fa7fbdf45c5b1fa19848db731132a0bf1757d), [`a12c28b7d5`](https://github.com/medusajs/medusa/commit/a12c28b7d5faed733bebbb4963dff50b9c8a33bc), [`b782d3bcb7`](https://github.com/medusajs/medusa/commit/b782d3bcb7e8088a962584b9a55200dd29c2161c), [`2b9f98895e`](https://github.com/medusajs/medusa/commit/2b9f98895eaca255e01278674b11cd7cb69b388f), [`7f7cb2a263`](https://github.com/medusajs/medusa/commit/7f7cb2a263c26baf540b05a40ab3732ffeb0c73c), [`302323916`](https://github.com/medusajs/medusa/commit/302323916b6d8eaf571cd59b5fc92a913af207de), [`da5cc4cf7`](https://github.com/medusajs/medusa/commit/da5cc4cf7f7f0ef40d409704a95b025ce95477f4), [`daecd82a7`](https://github.com/medusajs/medusa/commit/daecd82a7cdf7315599f464999690414c20d6748), [`ce81cade88`](https://github.com/medusajs/medusa/commit/ce81cade887659cefe9638e3c1c2807378191c62), [`fd78f5e24`](https://github.com/medusajs/medusa/commit/fd78f5e24263f5e158c3b7d11fbf0a4436e9c17a), [`192bc336cc`](https://github.com/medusajs/medusa/commit/192bc336cc2b6ec3820d94524c046dcd3c4ac7d9), [`06b33a9b4`](https://github.com/medusajs/medusa/commit/06b33a9b4525b77b1b14b35b973209700945654e), [`b6ac768698`](https://github.com/medusajs/medusa/commit/b6ac768698a3b49d0162cb49e628386f3352d034), [`130c641e5c`](https://github.com/medusajs/medusa/commit/130c641e5c91cf831de64fb87aebbfdc4d23530d), [`fade8ea7bf`](https://github.com/medusajs/medusa/commit/fade8ea7bf560343ecbde116d226ac44053cdb8e), [`8472460f53`](https://github.com/medusajs/medusa/commit/8472460f533322cc4535199aa768ac163021bc79), [`68d8daccd`](https://github.com/medusajs/medusa/commit/68d8daccd2a8508a13e211130e49017198b51fab)]:
- @medusajs/types@1.11.11
- @medusajs/utils@1.11.4
- @medusajs/modules-sdk@1.12.7
+3
View File
@@ -0,0 +1,3 @@
# Auth Module
The Auth Module is Medusa’s authentication engine engine. It provides functions to authenticate users through identity providers and store metadata about users that can be used for authorization purposes.
@@ -0,0 +1,41 @@
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { AuthProvider } from "@models"
export async function createAuthProviders(
manager: SqlEntityManager,
providerData: any[] = [
{
provider: "manual",
name: "manual",
is_active: true,
},
{
provider: "disabled",
name: "disabled",
},
{
provider: "store",
name: "store",
domain: "store",
is_active: true,
},
{
provider: "admin",
name: "admin",
domain: "admin",
is_active: true,
},
]
): Promise<AuthProvider[]> {
const authProviders: AuthProvider[] = []
for (const provider of providerData) {
const authProvider = manager.create(AuthProvider, provider)
authProviders.push(authProvider)
}
await manager.persistAndFlush(authProviders)
return authProviders
}
@@ -0,0 +1,34 @@
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { AuthUser } from "@models"
export async function createAuthUsers(
manager: SqlEntityManager,
userData: any[] = [
{
id: "test-id",
entity_id: "test-id",
provider: "manual",
},
{
id: "test-id-1",
entity_id: "test-id-1",
provider: "manual",
},
{
entity_id: "test-id-2",
provider: "store",
},
]
): Promise<AuthUser[]> {
const authUsers: AuthUser[] = []
for (const user of userData) {
const authUser = manager.create(AuthUser, user)
authUsers.push(authUser)
}
await manager.persistAndFlush(authUsers)
return authUsers
}
@@ -0,0 +1,271 @@
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { AuthProviderService } from "@services"
import { MikroOrmWrapper } from "../../../utils"
import { createAuthProviders } from "../../../__fixtures__/auth-provider"
import { createMedusaContainer } from "@medusajs/utils"
import { asValue } from "awilix"
import ContainerLoader from "../../../../src/loaders/container"
jest.setTimeout(30000)
describe("AuthProvider Service", () => {
let service: AuthProviderService
let testManager: SqlEntityManager
let repositoryManager: SqlEntityManager
beforeEach(async () => {
await MikroOrmWrapper.setupDatabase()
repositoryManager = await MikroOrmWrapper.forkManager()
testManager = await MikroOrmWrapper.forkManager()
const container = createMedusaContainer()
container.register("manager", asValue(repositoryManager))
await ContainerLoader({ container })
service = container.resolve("authProviderService")
await createAuthProviders(testManager)
})
afterEach(async () => {
await MikroOrmWrapper.clearDatabase()
})
describe("list", () => {
it("should list AuthProviders", async () => {
const authProviders = await service.list()
const serialized = JSON.parse(JSON.stringify(authProviders))
expect(serialized).toEqual([
expect.objectContaining({
provider: "manual",
}),
expect.objectContaining({
provider: "disabled",
}),
expect.objectContaining({
provider: "store",
}),
expect.objectContaining({
provider: "admin",
}),
])
})
it("should list authProviders by provider id", async () => {
const authProviders = await service.list({
provider: ["manual"],
})
expect(authProviders).toEqual([
expect.objectContaining({
provider: "manual",
}),
])
})
it("should list active authProviders", async () => {
const authProviders = await service.list({
is_active: true,
})
const serialized = JSON.parse(JSON.stringify(authProviders))
expect(serialized).toEqual([
expect.objectContaining({
provider: "manual",
}),
expect.objectContaining({
provider: "store",
}),
expect.objectContaining({
provider: "admin",
}),
])
})
})
describe("listAndCount", () => {
it("should list AuthProviders", async () => {
const [authProviders, count] = await service.listAndCount()
const serialized = JSON.parse(JSON.stringify(authProviders))
expect(count).toEqual(4)
expect(serialized).toEqual([
expect.objectContaining({
provider: "manual",
}),
expect.objectContaining({
provider: "disabled",
}),
expect.objectContaining({
provider: "store",
}),
expect.objectContaining({
provider: "admin",
}),
])
})
it("should listAndCount authProviders by provider", async () => {
const [authProviders, count] = await service.listAndCount({
provider: ["manual"],
})
expect(count).toEqual(1)
expect(authProviders).toEqual([
expect.objectContaining({
provider: "manual",
}),
])
})
it("should listAndCount active authProviders", async () => {
const [authProviders, count] = await service.listAndCount({
is_active: true,
})
const serialized = JSON.parse(JSON.stringify(authProviders))
expect(count).toEqual(3)
expect(serialized).toEqual([
expect.objectContaining({
provider: "manual",
}),
expect.objectContaining({
provider: "store",
}),
expect.objectContaining({
provider: "admin",
}),
])
})
})
describe("retrieve", () => {
const provider = "manual"
it("should return an authProvider for the given provider", async () => {
const authProvider = await service.retrieve(provider)
expect(authProvider).toEqual(
expect.objectContaining({
provider,
})
)
})
it("should throw an error when an authProvider with the given provider does not exist", async () => {
let error
try {
await service.retrieve("does-not-exist")
} catch (e) {
error = e
}
expect(error.message).toEqual(
"AuthProvider with provider: does-not-exist was not found"
)
})
it("should throw an error when a provider is not provided", async () => {
let error
try {
await service.retrieve(undefined as unknown as string)
} catch (e) {
error = e
}
expect(error.message).toEqual('"authProviderProvider" must be defined')
})
it("should return authProvider based on config select param", async () => {
const authProvider = await service.retrieve(provider, {
select: ["provider"],
})
const serialized = JSON.parse(JSON.stringify(authProvider))
expect(serialized).toEqual({
provider,
})
})
})
describe("delete", () => {
const provider = "manual"
it("should delete the authProviders given a provider successfully", async () => {
await service.delete([provider])
const authProviders = await service.list({
provider: [provider],
})
expect(authProviders).toHaveLength(0)
})
})
describe("update", () => {
const provider = "manual"
it("should throw an error when a id does not exist", async () => {
let error
try {
await service.update([
{
provider: "does-not-exist",
},
])
} catch (e) {
error = e
}
expect(error.message).toEqual(
'AuthProvider with provider "does-not-exist" not found'
)
})
it("should update authProvider", async () => {
await service.update([
{
provider: "manual",
name: "test",
},
])
const [provider] = await service.list({ provider: ["manual"] })
expect(provider).toEqual(
expect.objectContaining({
name: "test",
})
)
})
})
describe("create", () => {
it("should create a authProvider successfully", async () => {
await service.create([
{
provider: "test",
name: "test provider",
},
])
const [authProvider] = await service.list({
provider: ["test"],
})
expect(authProvider).toEqual(
expect.objectContaining({
provider: "test",
})
)
})
})
})
@@ -0,0 +1,247 @@
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { AuthUserService } from "@services"
import { MikroOrmWrapper } from "../../../utils"
import { createAuthProviders } from "../../../__fixtures__/auth-provider"
import { createAuthUsers } from "../../../__fixtures__/auth-user"
import { createMedusaContainer } from "@medusajs/utils"
import { asValue } from "awilix"
import ContainerLoader from "../../../../src/loaders/container"
jest.setTimeout(30000)
describe("AuthUser Service", () => {
let service: AuthUserService
let testManager: SqlEntityManager
let repositoryManager: SqlEntityManager
beforeEach(async () => {
await MikroOrmWrapper.setupDatabase()
repositoryManager = await MikroOrmWrapper.forkManager()
testManager = await MikroOrmWrapper.forkManager()
const container = createMedusaContainer()
container.register("manager", asValue(repositoryManager))
await ContainerLoader({ container })
service = container.resolve("authUserService")
await createAuthProviders(testManager)
await createAuthUsers(testManager)
})
afterEach(async () => {
await MikroOrmWrapper.clearDatabase()
})
describe("list", () => {
it("should list authUsers", async () => {
const authUsers = await service.list()
const serialized = JSON.parse(JSON.stringify(authUsers))
expect(serialized).toEqual([
expect.objectContaining({
provider: "manual",
}),
expect.objectContaining({
provider: "manual",
}),
expect.objectContaining({
provider: "store",
}),
])
})
it("should list authUsers by id", async () => {
const authUsers = await service.list({
id: ["test-id"],
})
expect(authUsers).toEqual([
expect.objectContaining({
id: "test-id",
}),
])
})
it("should list authUsers by provider_id", async () => {
const authUsers = await service.list({
provider_id: "manual",
})
const serialized = JSON.parse(JSON.stringify(authUsers))
expect(serialized).toEqual([
expect.objectContaining({
id: "test-id",
}),
expect.objectContaining({
id: "test-id-1",
}),
])
})
})
describe("listAndCount", () => {
it("should list authUsers", async () => {
const [authUsers, count] = await service.listAndCount()
const serialized = JSON.parse(JSON.stringify(authUsers))
expect(count).toEqual(3)
expect(serialized).toEqual([
expect.objectContaining({
provider: "manual",
}),
expect.objectContaining({
provider: "manual",
}),
expect.objectContaining({
provider: "store",
}),
])
})
it("should listAndCount authUsers by provider_id", async () => {
const [authUsers, count] = await service.listAndCount({
provider_id: "manual",
})
expect(count).toEqual(2)
expect(authUsers).toEqual([
expect.objectContaining({
id: "test-id",
}),
expect.objectContaining({
id: "test-id-1",
}),
])
})
})
describe("retrieve", () => {
const id = "test-id"
it("should return an authUser for the given id", async () => {
const authUser = await service.retrieve(id)
expect(authUser).toEqual(
expect.objectContaining({
id,
})
)
})
it("should return authUser based on config select param", async () => {
const authUser = await service.retrieve(id, {
select: ["id"],
})
const serialized = JSON.parse(JSON.stringify(authUser))
expect(serialized).toEqual({
id,
})
})
it("should throw an error when an authUser 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(
"AuthUser with id: does-not-exist was not found"
)
})
it("should throw an error when a authUserId is not provided", async () => {
let error
try {
await service.retrieve(undefined as unknown as string)
} catch (e) {
error = e
}
expect(error.message).toEqual('"authUserId" must be defined')
})
})
describe("delete", () => {
it("should delete the authUsers given an id successfully", async () => {
const id = "test-id"
await service.delete([id])
const authUsers = await service.list({
id: [id],
})
expect(authUsers).toHaveLength(0)
})
})
describe("update", () => {
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(
'AuthUser with id "does-not-exist" not found'
)
})
it("should update authUser", async () => {
const id = "test-id"
await service.update([
{
id,
provider_metadata: { email: "test@email.com" },
},
])
const [authUser] = await service.list({ id: [id] })
expect(authUser).toEqual(
expect.objectContaining({
provider_metadata: { email: "test@email.com" },
})
)
})
})
describe("create", () => {
it("should create a authUser successfully", async () => {
await service.create([
{
id: "test",
provider_id: "manual",
entity_id: "test"
},
])
const [authUser] = await service.list({
id: ["test"],
})
expect(authUser).toEqual(
expect.objectContaining({
id: "test",
})
)
})
})
})
@@ -0,0 +1,282 @@
import { IAuthModuleService } from "@medusajs/types"
import { MikroOrmWrapper } from "../../../utils"
import { Modules } from "@medusajs/modules-sdk"
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { createAuthProviders } from "../../../__fixtures__/auth-provider"
import { createAuthUsers } from "../../../__fixtures__/auth-user"
import { getInitModuleConfig } from "../../../utils/get-init-module-config"
import { initModules } from "medusa-test-utils"
jest.setTimeout(30000)
describe("AuthModuleService - AuthProvider", () => {
let service: IAuthModuleService
let testManager: SqlEntityManager
let shutdownFunc: () => Promise<void>
beforeAll(async () => {
const initModulesConfig = getInitModuleConfig()
const { medusaApp, shutdown } = await initModules(initModulesConfig)
service = medusaApp.modules[Modules.AUTH]
shutdownFunc = shutdown
})
beforeEach(async () => {
await MikroOrmWrapper.setupDatabase()
testManager = MikroOrmWrapper.forkManager()
await createAuthProviders(testManager)
await createAuthUsers(testManager)
})
afterAll(async () => {
await shutdownFunc()
})
afterEach(async () => {
await MikroOrmWrapper.clearDatabase()
})
describe("listAuthProviders", () => {
it("should list AuthProviders", async () => {
const authProviders = await service.listAuthProviders()
const serialized = JSON.parse(JSON.stringify(authProviders))
expect(serialized).toEqual(
expect.arrayContaining([
expect.objectContaining({
provider: "manual",
}),
expect.objectContaining({
provider: "disabled",
}),
expect.objectContaining({
provider: "store",
}),
expect.objectContaining({
provider: "admin",
}),
])
)
})
it("should list authProviders by id", async () => {
const authProviders = await service.listAuthProviders({
provider: ["manual"],
})
expect(authProviders).toEqual([
expect.objectContaining({
provider: "manual",
}),
])
})
it("should list active authProviders", async () => {
const authProviders = await service.listAuthProviders({
is_active: true,
})
const serialized = JSON.parse(JSON.stringify(authProviders))
expect(serialized).toEqual([
expect.objectContaining({
provider: "manual",
}),
expect.objectContaining({
provider: "store",
}),
expect.objectContaining({
provider: "admin",
}),
])
})
})
describe("listAndCountAuthProviders", () => {
it("should list and count AuthProviders", async () => {
const [authProviders, count] = await service.listAndCountAuthProviders()
const serialized = JSON.parse(JSON.stringify(authProviders))
expect(count).toEqual(4)
expect(serialized).toEqual([
expect.objectContaining({
provider: "manual",
}),
expect.objectContaining({
provider: "disabled",
}),
expect.objectContaining({
provider: "store",
}),
expect.objectContaining({
provider: "admin",
}),
])
})
it("should list and count authProviders by provider", async () => {
const [authProviders, count] = await service.listAndCountAuthProviders({
provider: ["manual"],
})
expect(count).toEqual(1)
expect(authProviders).toEqual([
expect.objectContaining({
provider: "manual",
}),
])
})
it("should list and count active authProviders", async () => {
const [authProviders, count] = await service.listAndCountAuthProviders({
is_active: true,
})
const serialized = JSON.parse(JSON.stringify(authProviders))
expect(count).toEqual(3)
expect(serialized).toEqual([
expect.objectContaining({
provider: "manual",
}),
expect.objectContaining({
provider: "store",
}),
expect.objectContaining({
provider: "admin",
}),
])
})
})
describe("retrieveAuthProvider", () => {
const provider = "manual"
it("should return an authProvider for the given provider", async () => {
const authProvider = await service.retrieveAuthProvider(provider)
expect(authProvider).toEqual(
expect.objectContaining({
provider,
})
)
})
it("should return authProvider based on config select param", async () => {
const authProvider = await service.retrieveAuthProvider(provider, {
select: ["provider"],
})
const serialized = JSON.parse(JSON.stringify(authProvider))
expect(serialized).toEqual({
provider,
})
})
it("should throw an error when an authProvider with the given provider does not exist", async () => {
let error
try {
await service.retrieveAuthProvider("does-not-exist")
} catch (e) {
error = e
}
expect(error.message).toEqual(
"AuthProvider with provider: does-not-exist was not found"
)
})
it("should throw an error when a provider is not provided", async () => {
let error
try {
await service.retrieveAuthProvider(undefined as unknown as string)
} catch (e) {
error = e
}
expect(error.message).toEqual('"authProviderProvider" must be defined')
})
})
describe("deleteAuthProvider", () => {
const provider = "manual"
it("should delete the authProviders given a provider successfully", async () => {
await service.deleteAuthProvider([provider])
const authProviders = await service.listAuthProviders({
provider: [provider],
})
expect(authProviders).toHaveLength(0)
})
})
describe("updateAuthProvider", () => {
const provider = "manual"
it("should throw an error when a id does not exist", async () => {
let error
try {
await service.updateAuthProvider([
{
provider: "does-not-exist",
},
])
} catch (e) {
error = e
}
expect(error.message).toEqual(
'AuthProvider with provider "does-not-exist" not found'
)
})
it("should update authProvider", async () => {
await service.updateAuthProvider([
{
provider: "manual",
name: "test",
},
])
const [provider] = await service.listAuthProviders({
provider: ["manual"],
})
expect(provider).toEqual(
expect.objectContaining({
name: "test",
})
)
})
})
describe("createAuthProvider", () => {
it("should create a authProvider successfully", async () => {
await service.createAuthProvider([
{
provider: "test",
name: "test provider",
},
])
const [authProvider] = await service.listAuthProviders({
provider: ["test"],
})
expect(authProvider).toEqual(
expect.objectContaining({
provider: "test",
})
)
})
})
})
@@ -0,0 +1,264 @@
import { IAuthModuleService } from "@medusajs/types"
import { MikroOrmWrapper } from "../../../utils"
import { Modules } from "@medusajs/modules-sdk"
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { createAuthProviders } from "../../../__fixtures__/auth-provider"
import { createAuthUsers } from "../../../__fixtures__/auth-user"
import { getInitModuleConfig } from "../../../utils/get-init-module-config"
import { initModules } from "medusa-test-utils"
jest.setTimeout(30000)
describe("AuthModuleService - AuthUser", () => {
let service: IAuthModuleService
let testManager: SqlEntityManager
let shutdownFunc: () => Promise<void>
beforeAll(async () => {
const initModulesConfig = getInitModuleConfig()
const { medusaApp, shutdown } = await initModules(initModulesConfig)
service = medusaApp.modules[Modules.AUTH]
shutdownFunc = shutdown
})
beforeEach(async () => {
await MikroOrmWrapper.setupDatabase()
testManager = MikroOrmWrapper.forkManager()
await createAuthProviders(testManager)
await createAuthUsers(testManager)
})
afterEach(async () => {
await MikroOrmWrapper.clearDatabase()
})
afterAll(async () => {
await shutdownFunc()
})
describe("listAuthUsers", () => {
it("should list authUsers", async () => {
const authUsers = await service.listAuthUsers()
const serialized = JSON.parse(JSON.stringify(authUsers))
expect(serialized).toEqual([
expect.objectContaining({
provider: "manual",
}),
expect.objectContaining({
provider: "manual",
}),
expect.objectContaining({
provider: "store",
}),
])
})
it("should list authUsers by id", async () => {
const authUsers = await service.listAuthUsers({
id: ["test-id"],
})
expect(authUsers).toEqual([
expect.objectContaining({
id: "test-id",
}),
])
})
it("should list authUsers by provider_id", async () => {
const authUsers = await service.listAuthUsers({
provider_id: "manual",
})
const serialized = JSON.parse(JSON.stringify(authUsers))
expect(serialized).toEqual([
expect.objectContaining({
id: "test-id",
}),
expect.objectContaining({
id: "test-id-1",
}),
])
})
})
describe("listAndCountAuthUsers", () => {
it("should list and count authUsers", async () => {
const [authUsers, count] = await service.listAndCountAuthUsers()
const serialized = JSON.parse(JSON.stringify(authUsers))
expect(count).toEqual(3)
expect(serialized).toEqual([
expect.objectContaining({
provider: "manual",
}),
expect.objectContaining({
provider: "manual",
}),
expect.objectContaining({
provider: "store",
}),
])
})
it("should listAndCount authUsers by provider_id", async () => {
const [authUsers, count] = await service.listAndCountAuthUsers({
provider_id: "manual",
})
expect(count).toEqual(2)
expect(authUsers).toEqual([
expect.objectContaining({
id: "test-id",
}),
expect.objectContaining({
id: "test-id-1",
}),
])
})
})
describe("retrieveAuthUser", () => {
const id = "test-id"
it("should return an authUser for the given id", async () => {
const authUser = await service.retrieveAuthUser(id)
expect(authUser).toEqual(
expect.objectContaining({
id,
})
)
})
it("should throw an error when an authUser with the given id does not exist", async () => {
let error
try {
await service.retrieveAuthUser("does-not-exist")
} catch (e) {
error = e
}
expect(error.message).toEqual(
"AuthUser with id: does-not-exist was not found"
)
})
it("should not return an authUser with password hash", async () => {
const authUser = await service.retrieveAuthUser("test-id-1")
expect(authUser).toEqual(
expect.objectContaining({
id: "test-id-1",
})
)
expect(authUser["password_hash"]).toEqual(undefined)
})
it("should throw an error when a authUserId is not provided", async () => {
let error
try {
await service.retrieveAuthUser(undefined as unknown as string)
} catch (e) {
error = e
}
expect(error.message).toEqual('"authUserId" must be defined')
})
it("should return authUser based on config select param", async () => {
const authUser = await service.retrieveAuthUser(id, {
select: ["id"],
})
const serialized = JSON.parse(JSON.stringify(authUser))
expect(serialized).toEqual({
id,
})
})
})
describe("deleteAuthUser", () => {
const id = "test-id"
it("should delete the authUsers given an id successfully", async () => {
await service.deleteAuthUser([id])
const authUsers = await service.listAuthUsers({
id: [id],
})
expect(authUsers).toHaveLength(0)
})
})
describe("updateAuthUser", () => {
const id = "test-id"
it("should throw an error when a id does not exist", async () => {
let error
try {
await service.updateAuthUser([
{
id: "does-not-exist",
},
])
} catch (e) {
error = e
}
expect(error.message).toEqual(
'AuthUser with id "does-not-exist" not found'
)
})
it("should update authUser", async () => {
await service.updateAuthUser([
{
id,
provider_metadata: { email: "test@email.com" },
},
])
const [authUser] = await service.listAuthUsers({ id: [id] })
expect(authUser).toEqual(
expect.objectContaining({
provider_metadata: { email: "test@email.com" },
})
)
})
})
describe("createAuthUser", () => {
it("should create a authUser successfully", async () => {
await service.createAuthUser([
{
id: "test",
provider_id: "manual",
entity_id: "test",
},
])
const [authUser, count] = await service.listAndCountAuthUsers({
id: ["test"],
})
expect(count).toEqual(1)
expect(authUser[0]).toEqual(
expect.objectContaining({
id: "test",
})
)
})
})
})
@@ -0,0 +1,96 @@
import { MedusaModule, Modules } from "@medusajs/modules-sdk"
import { IAuthModuleService } from "@medusajs/types"
import { MikroOrmWrapper } from "../../../utils"
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { createAuthProviders } from "../../../__fixtures__/auth-provider"
import { getInitModuleConfig } from "../../../utils/get-init-module-config"
import { initModules } from "medusa-test-utils"
jest.setTimeout(30000)
describe("AuthModuleService - AuthProvider", () => {
let service: IAuthModuleService
let testManager: SqlEntityManager
let shutdownFunc: () => Promise<void>
beforeAll(async () => {
const initModulesConfig = getInitModuleConfig()
const { medusaApp, shutdown } = await initModules(initModulesConfig)
service = medusaApp.modules[Modules.AUTH]
shutdownFunc = shutdown
})
beforeEach(async () => {
await MikroOrmWrapper.setupDatabase()
testManager = MikroOrmWrapper.forkManager()
if (service.__hooks?.onApplicationStart) {
await service.__hooks.onApplicationStart()
}
})
afterEach(async () => {
await MikroOrmWrapper.clearDatabase()
MedusaModule.clearInstances()
})
afterAll(async () => {
await shutdownFunc()
})
describe("listAuthProviders", () => {
it("should list default AuthProviders registered by loaders", async () => {
const authProviders = await service.listAuthProviders()
const serialized = JSON.parse(JSON.stringify(authProviders))
expect(serialized).toEqual(
expect.arrayContaining([
expect.objectContaining({
provider: "emailpass",
name: "Email/Password Authentication",
}),
expect.objectContaining({
provider: "google",
name: "Google Authentication",
}),
])
)
})
})
describe("authenticate", () => {
it("authenticate validates that a provider is registered in container", async () => {
await createAuthProviders(testManager, [
{
provider: "notRegistered",
name: "test",
},
])
const { success, error } = await service.authenticate("notRegistered", {} as any)
expect(success).toBe(false)
expect(error).toEqual(
"AuthenticationProvider with for provider: notRegistered wasn't registered in the module. Have you configured your options correctly?"
)
})
it("fails to authenticate using a valid provider with an invalid scope", async () => {
const { success, error } = await service.authenticate(
"emailpass",
{
scope: "non-existing",
} as any
)
expect(success).toBe(false)
expect(error).toEqual(
`Scope "non-existing" is not valid for provider emailpass`
)
})
})
})
@@ -0,0 +1,151 @@
import { AuthenticationInput, IAuthModuleService } from "@medusajs/types"
import { MedusaModule, Modules } from "@medusajs/modules-sdk"
import { MikroOrmWrapper } from "../../../utils"
import Scrypt from "scrypt-kdf"
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { createAuthProviders } from "../../../__fixtures__/auth-provider"
import { createAuthUsers } from "../../../__fixtures__/auth-user"
import { getInitModuleConfig } from "../../../utils/get-init-module-config"
import { initModules } from "medusa-test-utils"
jest.setTimeout(30000)
const seedDefaultData = async (testManager) => {
await createAuthProviders(testManager)
await createAuthUsers(testManager)
}
describe("AuthModuleService - AuthProvider", () => {
let service: IAuthModuleService
let testManager: SqlEntityManager
let shutdownFunc: () => Promise<void>
beforeAll(async () => {
const initModulesConfig = getInitModuleConfig()
const { medusaApp, shutdown } = await initModules(initModulesConfig)
service = medusaApp.modules[Modules.AUTH]
shutdownFunc = shutdown
})
afterAll(async () => {
await shutdownFunc()
})
beforeEach(async () => {
await MikroOrmWrapper.setupDatabase()
testManager = MikroOrmWrapper.forkManager()
if (service.__hooks?.onApplicationStart) {
await service.__hooks.onApplicationStart()
}
})
afterEach(async () => {
await MikroOrmWrapper.clearDatabase()
MedusaModule.clearInstances()
})
describe("authenticate", () => {
it("authenticate validates that a provider is registered in container", async () => {
const password = "supersecret"
const email = "test@test.com"
const passwordHash = (
await Scrypt.kdf(password, { logN: 15, r: 8, p: 1 })
).toString("base64")
await seedDefaultData(testManager)
await createAuthUsers(testManager, [
// Add authenticated user
{
provider: "emailpass",
entity_id: email,
provider_metadata: {
password: passwordHash,
},
},
])
const res = await service.authenticate("emailpass", {
body: {
email: "test@test.com",
password: password,
},
scope: "store",
})
expect(res).toEqual({
success: true,
authUser: expect.objectContaining({
entity_id: email,
provider_metadata: {},
}),
})
})
it("fails when no password is given", async () => {
const email = "test@test.com"
await seedDefaultData(testManager)
const res = await service.authenticate("emailpass", {
body: { email: "test@test.com" },
scope: "store",
})
expect(res).toEqual({
success: false,
error: "Password should be a string",
})
})
it("fails when no email is given", async () => {
await seedDefaultData(testManager)
const res = await service.authenticate("emailpass", {
body: { password: "supersecret" },
scope: "store",
})
expect(res).toEqual({
success: false,
error: "Email should be a string",
})
})
it("fails with an invalid password", async () => {
const password = "supersecret"
const email = "test@test.com"
const passwordHash = (
await Scrypt.kdf(password, { logN: 15, r: 8, p: 1 })
).toString("base64")
await seedDefaultData(testManager)
await createAuthUsers(testManager, [
// Add authenticated user
{
provider: "emailpass",
entity_id: email,
provider_metadata: {
password_hash: passwordHash,
},
},
])
const res = await service.authenticate("emailpass", {
body: {
email: "test@test.com",
password: "password",
},
scope: "store",
})
expect(res).toEqual({
success: false,
error: "Invalid email or password",
})
})
})
})
@@ -0,0 +1,6 @@
if (typeof process.env.DB_TEMP_NAME === "undefined") {
const tempName = parseInt(process.env.JEST_WORKER_ID || "1")
process.env.DB_TEMP_NAME = `medusa-auth-integration-${tempName}`
}
process.env.MEDUSA_AUTH_DB_SCHEMA = "public"
+3
View File
@@ -0,0 +1,3 @@
import { JestUtils } from "medusa-test-utils"
JestUtils.afterAllHookDropDatabase()
@@ -0,0 +1,6 @@
import { ModuleServiceInitializeOptions } from "@medusajs/types"
export const databaseOptions: ModuleServiceInitializeOptions["database"] = {
schema: "public",
clientUrl: "medusa-auth-test",
}
@@ -0,0 +1,18 @@
import * as AuthModels from "@models"
import { TestDatabaseUtils } from "medusa-test-utils"
const pathToMigrations = "../../src/migrations"
const mikroOrmEntities = AuthModels as unknown as any[]
export const MikroOrmWrapper = TestDatabaseUtils.getMikroOrmWrapper(
mikroOrmEntities,
pathToMigrations
)
export const MikroOrmConfig = TestDatabaseUtils.getMikroOrmConfig(
mikroOrmEntities,
pathToMigrations
)
export const DB_URL = TestDatabaseUtils.getDatabaseURL()
@@ -0,0 +1,42 @@
import { Modules, ModulesDefinition } from "@medusajs/modules-sdk"
import { DB_URL } from "./database"
export function getInitModuleConfig() {
const moduleOptions = {
defaultAdapterOptions: {
database: {
clientUrl: DB_URL,
schema: process.env.MEDUSA_AUTH_DB_SCHEMA,
},
},
providers: [
{
name: "emailpass",
scopes: {
admin: {},
store: {},
},
},
],
}
const injectedDependencies = {}
const modulesConfig_ = {
[Modules.AUTH]: {
definition: ModulesDefinition[Modules.AUTH],
options: moduleOptions,
},
}
return {
injectedDependencies,
modulesConfig: modulesConfig_,
databaseConfig: {
clientUrl: DB_URL,
schema: process.env.MEDUSA_AUTH_DB_SCHEMA,
},
joinerConfig: [],
}
}
@@ -0,0 +1,2 @@
export * from "./config"
export * from "./database"
+23
View File
@@ -0,0 +1,23 @@
module.exports = {
moduleNameMapper: {
"^@models": "<rootDir>/src/models",
"^@services": "<rootDir>/src/services",
"^@repositories": "<rootDir>/src/repositories",
"^@types": "<rootDir>/src/types",
"^@providers": "<rootDir>/src/providers",
},
transform: {
"^.+\\.[jt]s?$": [
"ts-jest",
{
tsConfig: "tsconfig.spec.json",
isolatedModules: true,
},
],
},
testEnvironment: `node`,
moduleFileExtensions: [`js`, `ts`],
modulePathIgnorePatterns: ["dist/"],
setupFiles: ["<rootDir>/integration-tests/setup-env.js"],
setupFilesAfterEnv: ["<rootDir>/integration-tests/setup.js"],
}
+8
View File
@@ -0,0 +1,8 @@
import * as entities from "./src/models"
module.exports = {
entities: Object.values(entities),
schema: "public",
clientUrl: "postgres://postgres@localhost/medusa-auth",
type: "postgresql",
}
+64
View File
@@ -0,0 +1,64 @@
{
"name": "@medusajs/auth",
"version": "0.0.2",
"description": "Medusa Auth module",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist"
],
"engines": {
"node": ">=16"
},
"bin": {
"medusa-auth-seed": "dist/scripts/bin/run-seed.js"
},
"repository": {
"type": "git",
"url": "https://github.com/medusajs/medusa",
"directory": "packages/auth"
},
"publishConfig": {
"access": "public"
},
"author": "Medusa",
"license": "MIT",
"scripts": {
"watch": "tsc --build --watch",
"watch:test": "tsc --build tsconfig.spec.json --watch",
"prepublishOnly": "cross-env NODE_ENV=production tsc --build && tsc-alias -p tsconfig.json",
"build": "rimraf dist && tsc --build && tsc-alias -p tsconfig.json",
"test": "jest --runInBand --bail --forceExit -- src/**/__tests__/**/*.ts",
"test:integration": "jest --runInBand --forceExit -- integration-tests/**/__tests__/**/*.ts",
"migration:generate": " MIKRO_ORM_CLI=./mikro-orm.config.dev.ts mikro-orm migration:generate",
"migration:initial": " MIKRO_ORM_CLI=./mikro-orm.config.dev.ts mikro-orm migration:create --initial",
"migration:create": " MIKRO_ORM_CLI=./mikro-orm.config.dev.ts mikro-orm migration:create",
"migration:up": " MIKRO_ORM_CLI=./mikro-orm.config.dev.ts mikro-orm migration:up",
"orm:cache:clear": " MIKRO_ORM_CLI=./mikro-orm.config.dev.ts mikro-orm cache:clear"
},
"devDependencies": {
"@mikro-orm/cli": "5.9.7",
"cross-env": "^5.2.1",
"jest": "^29.6.3",
"medusa-test-utils": "^1.1.40",
"rimraf": "^3.0.2",
"ts-jest": "^29.1.1",
"ts-node": "^10.9.1",
"tsc-alias": "^1.8.6",
"typescript": "^5.1.6"
},
"dependencies": {
"@medusajs/modules-sdk": "^1.12.7",
"@medusajs/types": "^1.11.11",
"@medusajs/utils": "^1.11.4",
"@mikro-orm/core": "5.9.7",
"@mikro-orm/migrations": "5.9.7",
"@mikro-orm/postgresql": "5.9.7",
"awilix": "^8.0.0",
"dotenv": "^16.1.4",
"jsonwebtoken": "^9.0.2",
"knex": "2.4.2",
"scrypt-kdf": "^2.0.1",
"simple-oauth2": "^5.0.0"
}
}
+11
View File
@@ -0,0 +1,11 @@
import {
moduleDefinition,
revertMigration,
runMigrations,
} from "./module-definition"
export default moduleDefinition
export { revertMigration, runMigrations }
export * from "./initialize"
export * from "./loaders"
+31
View File
@@ -0,0 +1,31 @@
import {
ExternalModuleDeclaration,
InternalModuleDeclaration,
MODULE_PACKAGE_NAMES,
MedusaModule,
Modules,
} from "@medusajs/modules-sdk"
import { IAuthModuleService, ModulesSdkTypes } from "@medusajs/types"
import { InitializeModuleInjectableDependencies } from "../types"
import { moduleDefinition } from "../module-definition"
export const initialize = async (
options?:
| ModulesSdkTypes.ModuleBootstrapDeclaration
| ModulesSdkTypes.ModuleServiceInitializeOptions
| ModulesSdkTypes.ModuleServiceInitializeCustomDataLayerOptions,
injectedDependencies?: InitializeModuleInjectableDependencies
): Promise<IAuthModuleService> => {
const loaded = await MedusaModule.bootstrap<IAuthModuleService>({
moduleKey: Modules.AUTH,
defaultPath: MODULE_PACKAGE_NAMES[Modules.AUTH],
declaration: options as
| InternalModuleDeclaration
| ExternalModuleDeclaration, // TODO: Add provider configuration
injectedDependencies,
moduleExports: moduleDefinition,
})
return loaded[Modules.AUTH]
}
+31
View File
@@ -0,0 +1,31 @@
import { AuthUser } from "@models"
import { MapToConfig } from "@medusajs/utils"
import { ModuleJoinerConfig } from "@medusajs/types"
import { Modules } from "@medusajs/modules-sdk"
export const LinkableKeys = {
auth_user_id: AuthUser.name,
}
const entityLinkableKeysMap: MapToConfig = {}
Object.entries(LinkableKeys).forEach(([key, value]) => {
entityLinkableKeysMap[value] ??= []
entityLinkableKeysMap[value].push({
mapTo: key,
valueFrom: key.split("_").pop()!,
})
})
export const entityNameToLinkableKeysMap: MapToConfig = entityLinkableKeysMap
export const joinerConfig: ModuleJoinerConfig = {
serviceName: Modules.AUTH,
primaryKeys: ["id"],
linkableKeys: LinkableKeys,
alias: {
name: ["auth_user", "auth_users"],
args: {
entity: AuthUser.name,
},
},
}
+38
View File
@@ -0,0 +1,38 @@
import * as AuthModels from "../models"
import {
InternalModuleDeclaration,
LoaderOptions,
Modules,
} from "@medusajs/modules-sdk"
import { EntitySchema } from "@mikro-orm/core"
import { ModulesSdkTypes } from "@medusajs/types"
import { ModulesSdkUtils } from "@medusajs/utils"
export default async (
{
options,
container,
logger,
}: LoaderOptions<
| ModulesSdkTypes.ModuleServiceInitializeOptions
| ModulesSdkTypes.ModuleServiceInitializeCustomDataLayerOptions
>,
moduleDeclaration?: InternalModuleDeclaration
): Promise<void> => {
const entities = Object.values(
AuthModels
) as unknown as EntitySchema[]
const pathToMigrations = __dirname + "/../migrations"
await ModulesSdkUtils.mikroOrmConnectionLoader({
moduleName: Modules.AUTH,
entities,
container,
options,
moduleDeclaration,
logger,
pathToMigrations,
})
}
+10
View File
@@ -0,0 +1,10 @@
import { ModulesSdkUtils } from "@medusajs/utils"
import * as ModuleModels from "@models"
import * as ModuleRepositories from "@repositories"
import * as ModuleServices from "@services"
export default ModulesSdkUtils.moduleContainerLoaderFactory({
moduleModels: ModuleModels,
moduleRepositories: ModuleRepositories,
moduleServices: ModuleServices,
})
+3
View File
@@ -0,0 +1,3 @@
export * from "./connection"
export * from "./container"
export * from "./providers"
+71
View File
@@ -0,0 +1,71 @@
import * as defaultProviders from "@providers"
import {
asClass,
AwilixContainer,
ClassOrFunctionReturning,
Constructor,
Resolver,
} from "awilix"
import {
AuthModuleProviderConfig,
AuthProviderScope,
LoaderOptions,
ModulesSdkTypes,
} from "@medusajs/types"
type AuthModuleProviders = {
providers: AuthModuleProviderConfig[]
}
export default async ({
container,
options,
}: LoaderOptions<
(
| ModulesSdkTypes.ModuleServiceInitializeOptions
| ModulesSdkTypes.ModuleServiceInitializeCustomDataLayerOptions
) &
AuthModuleProviders
>): Promise<void> => {
const providerMap = new Map(
options?.providers?.map((provider) => [provider.name, provider.scopes]) ??
[]
)
// if(options?.providers?.length) {
// TODO: implement plugin provider registration
// }
const providersToLoad = Object.values(defaultProviders)
for (const provider of providersToLoad) {
container.register({
[`auth_provider_${provider.PROVIDER}`]: asClass(
provider as Constructor<any>
)
.singleton()
.inject(() => ({ scopes: providerMap.get(provider.PROVIDER) ?? {} })),
})
}
container.register({
[`auth_providers`]: asArray(providersToLoad, providerMap),
})
}
function asArray(
resolvers: (ClassOrFunctionReturning<unknown> | Resolver<unknown>)[],
providerScopeMap: Map<string, Record<string, AuthProviderScope>>
): { resolve: (container: AwilixContainer) => unknown[] } {
return {
resolve: (container: AwilixContainer) =>
resolvers.map((resolver) =>
asClass(resolver as Constructor<any>)
.inject(() => ({
// @ts-ignore
scopes: providerScopeMap.get(resolver.PROVIDER) ?? {},
}))
.resolve(container)
),
}
}
@@ -0,0 +1,175 @@
{
"namespaces": [
"public"
],
"name": "public",
"tables": [
{
"columns": {
"provider": {
"name": "provider",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"name": {
"name": "name",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"domain": {
"name": "domain",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"default": "'all'",
"enumItems": [
"all",
"store",
"admin"
],
"mappedType": "enum"
},
"config": {
"name": "config",
"type": "jsonb",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"mappedType": "json"
},
"is_active": {
"name": "is_active",
"type": "boolean",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"default": "false",
"mappedType": "boolean"
}
},
"name": "auth_provider",
"schema": "public",
"indexes": [
{
"keyName": "auth_provider_pkey",
"columnNames": [
"provider"
],
"composite": false,
"primary": true,
"unique": true
}
],
"checks": [],
"foreignKeys": {}
},
{
"columns": {
"id": {
"name": "id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"entity_id": {
"name": "entity_id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": false,
"mappedType": "text"
},
"provider_id": {
"name": "provider_id",
"type": "text",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"mappedType": "text"
},
"user_metadata": {
"name": "user_metadata",
"type": "jsonb",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"mappedType": "json"
},
"app_metadata": {
"name": "app_metadata",
"type": "jsonb",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"mappedType": "json"
},
"provider_metadata": {
"name": "provider_metadata",
"type": "jsonb",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"mappedType": "json"
}
},
"name": "auth_user",
"schema": "public",
"indexes": [
{
"keyName": "IDX_auth_user_provider_entity_id",
"columnNames": [
"provider_id",
"entity_id"
],
"composite": true,
"primary": false,
"unique": true
},
{
"keyName": "auth_user_pkey",
"columnNames": [
"id"
],
"composite": false,
"primary": true,
"unique": true
}
],
"checks": [],
"foreignKeys": {
"auth_user_provider_id_foreign": {
"constraintName": "auth_user_provider_id_foreign",
"columnNames": [
"provider_id"
],
"localTableName": "public.auth_user",
"referencedColumnNames": [
"provider"
],
"referencedTableName": "public.auth_provider",
"deleteRule": "cascade"
}
}
}
]
}
@@ -0,0 +1,22 @@
import { Migration } from '@mikro-orm/migrations';
export class Migration20240122041959 extends Migration {
async up(): Promise<void> {
this.addSql('create table if not exists "auth_provider" ("provider" text not null, "name" text not null, "domain" text check ("domain" in (\'all\', \'store\', \'admin\')) not null default \'all\', "config" jsonb null, "is_active" boolean not null default false, constraint "auth_provider_pkey" primary key ("provider"));');
this.addSql('create table if not exists "auth_user" ("id" text not null, "entity_id" text not null, "provider_id" text null, "user_metadata" jsonb null, "app_metadata" jsonb null, "provider_metadata" jsonb null, constraint "auth_user_pkey" primary key ("id"));');
this.addSql('alter table "auth_user" add constraint "IDX_auth_user_provider_entity_id" unique ("provider_id", "entity_id");');
this.addSql('alter table "auth_user" add constraint if not exists "auth_user_provider_id_foreign" foreign key ("provider_id") references "auth_provider" ("provider") on delete cascade;');
}
async down(): Promise<void> {
this.addSql('alter table "auth_user" drop constraint if exists "auth_user_provider_id_foreign";');
this.addSql('drop table if exists "auth_provider" cascade;');
this.addSql('drop table if exists "auth_user" cascade;');
}
}
+31
View File
@@ -0,0 +1,31 @@
import {
Entity,
Enum,
OptionalProps,
PrimaryKey,
Property,
} from "@mikro-orm/core"
import { ProviderDomain } from "../types/repositories/auth-provider"
type OptionalFields = "domain" | "is_active" | "config"
@Entity()
export default class AuthProvider {
[OptionalProps]: OptionalFields
@PrimaryKey({ columnType: "text" })
provider!: string
@Property({ columnType: "text" })
name: string
@Enum({ items: () => ProviderDomain, default: ProviderDomain.ALL })
domain: ProviderDomain = ProviderDomain.ALL
@Property({ columnType: "jsonb", nullable: true })
config: Record<string, unknown> | null = null
@Property({ columnType: "boolean", default: false })
is_active = false
}
+55
View File
@@ -0,0 +1,55 @@
import {
BeforeCreate,
Cascade,
Entity,
Index,
ManyToOne,
OnInit,
OptionalProps,
PrimaryKey,
Property,
Unique,
} from "@mikro-orm/core"
import AuthProvider from "./auth-provider"
import { generateEntityId } from "@medusajs/utils"
type OptionalFields = "provider_metadata" | "app_metadata" | "user_metadata"
@Entity()
@Unique({ properties: ["provider","entity_id" ], name: "IDX_auth_user_provider_entity_id" })
export default class AuthUser {
[OptionalProps]: OptionalFields
@PrimaryKey({ columnType: "text" })
id!: string
@Property({ columnType: "text" })
entity_id: string
@ManyToOne(() => AuthProvider, {
joinColumn: "provider",
fieldName: "provider_id",
cascade: [Cascade.REMOVE],
})
provider: AuthProvider
@Property({ columnType: "jsonb", nullable: true })
user_metadata: Record<string, unknown> | null
@Property({ columnType: "jsonb", nullable: true })
app_metadata: Record<string, unknown> | null
@Property({ columnType: "jsonb", nullable: true })
provider_metadata: Record<string, unknown> | null
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "authusr")
}
@OnInit()
onInit() {
this.id = generateEntityId(this.id, "authusr")
}
}
+2
View File
@@ -0,0 +1,2 @@
export { default as AuthUser } from "./auth-user"
export { default as AuthProvider } from "./auth-provider"
+32
View File
@@ -0,0 +1,32 @@
import * as Models from "@models"
import { AuthModuleService } from "@services"
import { ModuleExports } from "@medusajs/types"
import { Modules } from "@medusajs/modules-sdk"
import { ModulesSdkUtils } from "@medusajs/utils"
import loadConnection from "./loaders/connection"
import loadContainer from "./loaders/container"
import loadProviders from "./loaders/providers"
const migrationScriptOptions = {
moduleName: Modules.AUTH,
models: Models,
pathToMigrations: __dirname + "/migrations",
}
export const runMigrations = ModulesSdkUtils.buildMigrationScript(
migrationScriptOptions
)
export const revertMigration = ModulesSdkUtils.buildRevertMigrationScript(
migrationScriptOptions
)
const service = AuthModuleService
const loaders = [loadContainer, loadConnection, loadProviders] as any
export const moduleDefinition: ModuleExports = {
service,
loaders,
runMigrations,
revertMigration,
}
@@ -0,0 +1,64 @@
import { AbstractAuthModuleProvider, isString } from "@medusajs/utils"
import { AuthenticationInput, AuthenticationResponse } from "@medusajs/types"
import { AuthUserService } from "@services"
import Scrypt from "scrypt-kdf"
class EmailPasswordProvider extends AbstractAuthModuleProvider {
public static PROVIDER = "emailpass"
public static DISPLAY_NAME = "Email/Password Authentication"
protected readonly authUserSerivce_: AuthUserService
constructor({ authUserService }: { authUserService: AuthUserService }) {
super(arguments[0])
this.authUserSerivce_ = authUserService
}
async authenticate(
userData: AuthenticationInput
): 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",
}
}
const authUser = await this.authUserSerivce_.retrieveByProviderAndEntityId(
email,
EmailPasswordProvider.PROVIDER
)
const password_hash = authUser.provider_metadata?.password
if (isString(password_hash)) {
const buf = Buffer.from(password_hash as string, "base64")
const success = await Scrypt.verify(buf, password)
if (success) {
delete authUser.provider_metadata!.password
return { success, authUser: JSON.parse(JSON.stringify(authUser)) }
}
}
return {
success: false,
error: "Invalid email or password",
}
}
}
export default EmailPasswordProvider
+226
View File
@@ -0,0 +1,226 @@
import {
AbstractAuthModuleProvider,
MedusaError,
} from "@medusajs/utils"
import {
AuthProviderScope,
AuthenticationInput,
AuthenticationResponse,
} from "@medusajs/types"
import { AuthProviderService, AuthUserService } from "@services"
import jwt, { JwtPayload } from "jsonwebtoken"
import { AuthorizationCode } from "simple-oauth2"
import url from "url"
type InjectedDependencies = {
authUserService: AuthUserService
authProviderService: AuthProviderService
}
type ProviderConfig = {
clientID: string
clientSecret: string
callbackURL: string
}
class GoogleProvider extends AbstractAuthModuleProvider {
public static PROVIDER = "google"
public static DISPLAY_NAME = "Google Authentication"
protected readonly authUserSerivce_: AuthUserService
protected readonly authProviderService_: AuthProviderService
constructor({ authUserService, authProviderService }: InjectedDependencies) {
super(arguments[0])
this.authUserSerivce_ = authUserService
this.authProviderService_ = authProviderService
}
async authenticate(
req: AuthenticationInput
): Promise<AuthenticationResponse> {
if (req.query?.error) {
return {
success: false,
error: `${req.query.error_description}, read more at: ${req.query.error_uri}`,
}
}
let config: ProviderConfig
try {
config = await this.getProviderConfig(req)
} catch (error) {
return { success: false, error: error.message }
}
return this.getRedirect(config)
}
async validateCallback(
req: AuthenticationInput
): Promise<AuthenticationResponse> {
if (req.query && req.query.error) {
return {
success: false,
error: `${req.query.error_description}, read more at: ${req.query.error_uri}`,
}
}
let config: ProviderConfig
try {
config = await this.getProviderConfig(req)
} catch (error) {
return { success: false, error: error.message }
}
const code = req.query?.code ?? req.body?.code
return await this.validateCallbackToken(code, req.scope, config)
}
// abstractable
async verify_(refreshToken: string, scope: string) {
const jwtData = (await jwt.decode(refreshToken, {
complete: true,
})) as JwtPayload
const entity_id = jwtData.payload.email
let authUser
try {
authUser = await this.authUserSerivce_.retrieveByProviderAndEntityId(
entity_id,
GoogleProvider.PROVIDER
)
} catch (error) {
if (error.type === MedusaError.Types.NOT_FOUND) {
authUser = await this.authUserSerivce_.create([
{
entity_id,
provider_id: GoogleProvider.PROVIDER,
user_metadata: jwtData!.payload,
app_metadata: { scope },
},
])
} else {
return { success: false, error: error.message }
}
}
return { success: true, authUser }
}
// abstractable
private async validateCallbackToken(
code: string,
scope: string,
{ clientID, callbackURL, clientSecret }: ProviderConfig
) {
const client = this.getAuthorizationCodeHandler({ clientID, clientSecret })
const tokenParams = {
code,
redirect_uri: callbackURL,
}
try {
const accessToken = await client.getToken(tokenParams)
return await this.verify_(accessToken.token.id_token, scope)
} catch (error) {
return { success: false, error: error.message }
}
}
private getConfigFromScope(config: AuthProviderScope): ProviderConfig {
const providerConfig: Partial<ProviderConfig> = {}
if (config.clientId) {
providerConfig.clientID = config.clientId
} else {
throw new Error("Google clientID is required")
}
if (config.clientSecret) {
providerConfig.clientSecret = config.clientSecret
} else {
throw new Error("Google clientSecret is required")
}
if (config.callbackURL) {
providerConfig.callbackURL = config.callbackUrl
} else {
throw new Error("Google callbackUrl is required")
}
return providerConfig as ProviderConfig
}
private originalURL(req: AuthenticationInput) {
const tls = req.connection.encrypted
const host = req.headers.host
const protocol = tls ? "https" : "http"
const path = req.url || ""
return protocol + "://" + host + path
}
private async getProviderConfig(
req: AuthenticationInput
): Promise<ProviderConfig> {
await this.authProviderService_.retrieve(GoogleProvider.PROVIDER)
const scopeConfig = this.scopes_[req.scope]
const config = this.getConfigFromScope(scopeConfig)
const { callbackURL } = config
const parsedCallbackUrl = !url.parse(callbackURL).protocol
? url.resolve(this.originalURL(req), callbackURL)
: callbackURL
return { ...config, callbackURL: parsedCallbackUrl }
}
// Abstractable
private getRedirect({ clientID, callbackURL, clientSecret }: ProviderConfig) {
const client = this.getAuthorizationCodeHandler({ clientID, clientSecret })
const location = client.authorizeURL({
redirect_uri: callbackURL,
scope: "email profile",
})
return { success: true, location }
}
private getAuthorizationCodeHandler({
clientID,
clientSecret,
}: {
clientID: string
clientSecret: string
}) {
const config = {
client: {
id: clientID,
secret: clientSecret,
},
auth: {
// TODO: abstract to not be google specific
authorizeHost: "https://accounts.google.com",
authorizePath: "/o/oauth2/v2/auth",
tokenHost: "https://www.googleapis.com",
tokenPath: "/oauth2/v4/token",
},
}
return new AuthorizationCode(config)
}
}
export default GoogleProvider
+2
View File
@@ -0,0 +1,2 @@
export { default as EmailPasswordProvider } from "./email-password"
export { default as GoogleProvider } from "./google"
+1
View File
@@ -0,0 +1 @@
export { MikroOrmBaseRepository as BaseRepository } from "@medusajs/utils"
+19
View File
@@ -0,0 +1,19 @@
#!/usr/bin/env node
import { EOL } from "os"
import { run } from "../seed"
const args = process.argv
const path = args.pop() as string
export default (async () => {
const { config } = await import("dotenv")
config()
if (!path) {
throw new Error(
`filePath is required.${EOL}Example: medusa-auth-seed <filePath>`
)
}
await run({ path })
})()
+65
View File
@@ -0,0 +1,65 @@
import * as AuthModels from "@models"
import { DALUtils, ModulesSdkUtils } from "@medusajs/utils"
import { LoaderOptions, Logger, ModulesSdkTypes } from "@medusajs/types"
import { EOL } from "os"
import { EntitySchema } from "@mikro-orm/core"
import { Modules } from "@medusajs/modules-sdk"
import { resolve } from "path"
export async function run({
options,
logger,
path,
}: Partial<
Pick<
LoaderOptions<ModulesSdkTypes.ModuleServiceInitializeOptions>,
"options" | "logger"
>
> & {
path: string
}) {
logger ??= console as unknown as Logger
logger.info(`Loading seed data from ${path}...`)
const { authenticationData } = await import(
resolve(process.cwd(), path)
).catch((e) => {
logger?.error(
`Failed to load seed data from ${path}. Please, provide a relative path and check that you export the following: authenticationData.${EOL}${e}`
)
throw e
})
const dbData = ModulesSdkUtils.loadDatabaseConfig(
Modules.AUTH,
options
)!
const entities = Object.values(
AuthModels
) as unknown as EntitySchema[]
const pathToMigrations = __dirname + "/../migrations"
const orm = await DALUtils.mikroOrmCreateConnection(
dbData,
entities,
pathToMigrations
)
const manager = orm.em.fork()
try {
logger.info("Seeding authentication data..")
// TODO: implement authentication seed data
// await createAuthUsers(manager, authUsersData)
} catch (e) {
logger.error(
`Failed to insert the seed data in the PostgreSQL database ${dbData.clientUrl}.${EOL}${e}`
)
}
await orm.close(true)
}
@@ -0,0 +1,5 @@
describe("Noop test", () => {
it("noop check", async () => {
expect(true).toBe(true)
})
})
+435
View File
@@ -0,0 +1,435 @@
import {
AuthenticationInput,
AuthenticationResponse,
AuthTypes,
Context,
DAL,
FindConfig,
InternalModuleDeclaration,
MedusaContainer,
ModuleJoinerConfig,
} from "@medusajs/types"
import { AuthProvider, AuthUser } from "@models"
import { joinerConfig } from "../joiner-config"
import { AuthProviderService, AuthUserService } from "@services"
import {
AbstractAuthModuleProvider,
InjectManager,
InjectTransactionManager,
MedusaContext,
MedusaError,
} from "@medusajs/utils"
import {
AuthProviderDTO,
AuthUserDTO,
CreateAuthProviderDTO,
CreateAuthUserDTO,
FilterableAuthProviderProps,
FilterableAuthUserProps,
UpdateAuthUserDTO,
} from "@medusajs/types"
import { ServiceTypes } from "@types"
type InjectedDependencies = {
baseRepository: DAL.RepositoryService
authUserService: AuthUserService<any>
authProviderService: AuthProviderService<any>
}
export default class AuthModuleService<
TAuthUser extends AuthUser = AuthUser,
TAuthProvider extends AuthProvider = AuthProvider
> implements AuthTypes.IAuthModuleService
{
__joinerConfig(): ModuleJoinerConfig {
return joinerConfig
}
__hooks = {
onApplicationStart: async () => await this.createProvidersOnLoad(),
}
protected __container__: MedusaContainer
protected baseRepository_: DAL.RepositoryService
protected authUserService_: AuthUserService<TAuthUser>
protected authProviderService_: AuthProviderService<TAuthProvider>
constructor(
{
authUserService,
authProviderService,
baseRepository,
}: InjectedDependencies,
protected readonly moduleDeclaration: InternalModuleDeclaration
) {
this.__container__ = arguments[0]
this.baseRepository_ = baseRepository
this.authUserService_ = authUserService
this.authProviderService_ = authProviderService
}
async retrieveAuthProvider(
provider: string,
config: FindConfig<AuthProviderDTO> = {},
sharedContext: Context = {}
): Promise<AuthProviderDTO> {
const authProvider = await this.authProviderService_.retrieve(
provider,
config,
sharedContext
)
return await this.baseRepository_.serialize<AuthTypes.AuthProviderDTO>(
authProvider,
{ populate: true }
)
}
async listAuthProviders(
filters: FilterableAuthProviderProps = {},
config: FindConfig<AuthProviderDTO> = {},
sharedContext: Context = {}
): Promise<AuthProviderDTO[]> {
const authProviders = await this.authProviderService_.list(
filters,
config,
sharedContext
)
return await this.baseRepository_.serialize<
AuthTypes.AuthProviderDTO[]
>(authProviders, { populate: true })
}
@InjectManager("baseRepository_")
async listAndCountAuthProviders(
filters: FilterableAuthProviderProps = {},
config: FindConfig<AuthProviderDTO>,
@MedusaContext() sharedContext: Context = {}
): Promise<[AuthTypes.AuthProviderDTO[], number]> {
const [authProviders, count] = await this.authProviderService_.listAndCount(
filters,
config,
sharedContext
)
return [
await this.baseRepository_.serialize<
AuthTypes.AuthProviderDTO[]
>(authProviders, { populate: true }),
count,
]
}
async createAuthProvider(
data: CreateAuthProviderDTO[],
sharedContext?: Context
): Promise<AuthProviderDTO[]>
async createAuthProvider(
data: CreateAuthProviderDTO,
sharedContext?: Context
): Promise<AuthProviderDTO>
@InjectManager("baseRepository_")
async createAuthProvider(
data: CreateAuthProviderDTO | CreateAuthProviderDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<
AuthTypes.AuthProviderDTO | AuthTypes.AuthProviderDTO[]
> {
const input = Array.isArray(data) ? data : [data]
const providers = await this.createAuthProviders_(input, sharedContext)
const serializedProviders = await this.baseRepository_.serialize<
AuthTypes.AuthProviderDTO[]
>(providers, {
populate: true,
})
return Array.isArray(data) ? serializedProviders : serializedProviders[0]
}
@InjectTransactionManager("baseRepository_")
protected async createAuthProviders_(
data: any[],
@MedusaContext() sharedContext: Context
): Promise<TAuthProvider[]> {
return await this.authProviderService_.create(data, sharedContext)
}
updateAuthProvider(
data: AuthTypes.UpdateAuthProviderDTO[],
sharedContext?: Context
): Promise<AuthProviderDTO[]>
updateAuthProvider(
data: AuthTypes.UpdateAuthProviderDTO,
sharedContext?: Context
): Promise<AuthProviderDTO>
@InjectManager("baseRepository_")
async updateAuthProvider(
data:
| AuthTypes.UpdateAuthProviderDTO[]
| AuthTypes.UpdateAuthProviderDTO,
@MedusaContext() sharedContext: Context = {}
): Promise<
AuthTypes.AuthProviderDTO | AuthTypes.AuthProviderDTO[]
> {
const input = Array.isArray(data) ? data : [data]
const providers = await this.updateAuthProvider_(input, sharedContext)
const serializedProviders = await this.baseRepository_.serialize<
AuthTypes.AuthProviderDTO[]
>(providers, {
populate: true,
})
return Array.isArray(data) ? serializedProviders : serializedProviders[0]
}
async updateAuthProvider_(
data: AuthTypes.UpdateAuthProviderDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<TAuthProvider[]> {
return await this.authProviderService_.update(data, sharedContext)
}
@InjectTransactionManager("baseRepository_")
async deleteAuthProvider(
ids: string[],
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
await this.authProviderService_.delete(ids, sharedContext)
}
@InjectManager("baseRepository_")
async retrieveAuthUser(
id: string,
config: FindConfig<AuthUserDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<AuthUserDTO> {
const authUser = await this.authUserService_.retrieve(
id,
config,
sharedContext
)
return await this.baseRepository_.serialize<AuthTypes.AuthUserDTO>(
authUser,
{
exclude: ["password_hash"],
}
)
}
@InjectManager("baseRepository_")
async listAuthUsers(
filters: FilterableAuthUserProps = {},
config: FindConfig<AuthUserDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<AuthUserDTO[]> {
const authUsers = await this.authUserService_.list(
filters,
config,
sharedContext
)
return await this.baseRepository_.serialize<
AuthTypes.AuthUserDTO[]
>(authUsers, {
populate: true,
})
}
@InjectManager("baseRepository_")
async listAndCountAuthUsers(
filters: FilterableAuthUserProps = {},
config: FindConfig<AuthUserDTO> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<[AuthUserDTO[], number]> {
const [authUsers, count] = await this.authUserService_.listAndCount(
filters,
config,
sharedContext
)
return [
await this.baseRepository_.serialize<AuthTypes.AuthUserDTO[]>(
authUsers,
{
populate: true,
}
),
count,
]
}
createAuthUser(
data: CreateAuthUserDTO[],
sharedContext?: Context
): Promise<AuthUserDTO[]>
createAuthUser(
data: CreateAuthUserDTO,
sharedContext?: Context
): Promise<AuthUserDTO>
@InjectManager("baseRepository_")
async createAuthUser(
data: CreateAuthUserDTO[] | CreateAuthUserDTO,
@MedusaContext() sharedContext: Context = {}
): Promise<
AuthTypes.AuthUserDTO | AuthTypes.AuthUserDTO[]
> {
const input = Array.isArray(data) ? data : [data]
const authUsers = await this.createAuthUsers_(input, sharedContext)
const serializedUsers = await this.baseRepository_.serialize<
AuthTypes.AuthUserDTO[]
>(authUsers, {
populate: true,
})
return Array.isArray(data) ? serializedUsers : serializedUsers[0]
}
@InjectTransactionManager("baseRepository_")
protected async createAuthUsers_(
data: CreateAuthUserDTO[],
@MedusaContext() sharedContext: Context
): Promise<TAuthUser[]> {
return await this.authUserService_.create(data, sharedContext)
}
updateAuthUser(
data: UpdateAuthUserDTO[],
sharedContext?: Context
): Promise<AuthUserDTO[]>
updateAuthUser(
data: UpdateAuthUserDTO,
sharedContext?: Context
): Promise<AuthUserDTO>
@InjectManager("baseRepository_")
async updateAuthUser(
data: UpdateAuthUserDTO | UpdateAuthUserDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<
AuthTypes.AuthUserDTO | AuthTypes.AuthUserDTO[]
> {
const input = Array.isArray(data) ? data : [data]
const updatedUsers = await this.updateAuthUsers_(input, sharedContext)
const serializedUsers = await this.baseRepository_.serialize<
AuthTypes.AuthUserDTO[]
>(updatedUsers, {
populate: true,
})
return Array.isArray(data) ? serializedUsers : serializedUsers[0]
}
@InjectTransactionManager("baseRepository_")
protected async updateAuthUsers_(
data: UpdateAuthUserDTO[],
@MedusaContext() sharedContext: Context
): Promise<TAuthUser[]> {
return await this.authUserService_.update(data, sharedContext)
}
@InjectTransactionManager("baseRepository_")
async deleteAuthUser(
ids: string[],
@MedusaContext() sharedContext: Context = {}
): Promise<void> {
await this.authUserService_.delete(ids, sharedContext)
}
protected getRegisteredAuthenticationProvider(
provider: string,
{ scope }: AuthenticationInput
): AbstractAuthModuleProvider {
let containerProvider: AbstractAuthModuleProvider
try {
containerProvider = this.__container__[`auth_provider_${provider}`]
} catch (error) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`AuthenticationProvider with for provider: ${provider} wasn't registered in the module. Have you configured your options correctly?`
)
}
containerProvider.validateScope(scope)
return containerProvider
}
async authenticate(
provider: string,
authenticationData: AuthenticationInput
): Promise<AuthenticationResponse> {
try {
await this.retrieveAuthProvider(provider, {})
const registeredProvider = this.getRegisteredAuthenticationProvider(
provider,
authenticationData
)
return await registeredProvider.authenticate(authenticationData)
} catch (error) {
return { success: false, error: error.message }
}
}
async validateCallback(
provider: string,
authenticationData: AuthenticationInput
): Promise<AuthenticationResponse> {
try {
await this.retrieveAuthProvider(provider, {})
const registeredProvider = this.getRegisteredAuthenticationProvider(
provider,
authenticationData
)
return await registeredProvider.validateCallback(authenticationData)
} catch (error) {
return { success: false, error: error.message }
}
}
private async createProvidersOnLoad() {
const providersToLoad = this.__container__["auth_providers"]
const providers = await this.authProviderService_.list({
provider: providersToLoad.map((p) => p.provider),
})
const loadedProvidersMap = new Map(providers.map((p) => [p.provider, p]))
const providersToCreate: ServiceTypes.CreateAuthProviderDTO[] = []
for (const provider of providersToLoad) {
if (loadedProvidersMap.has(provider.provider)) {
continue
}
providersToCreate.push({
provider: provider.provider,
name: provider.displayName,
})
}
await this.authProviderService_.create(providersToCreate)
}
}
@@ -0,0 +1,24 @@
import { DAL } from "@medusajs/types"
import { ModulesSdkUtils } from "@medusajs/utils"
import { AuthProvider } from "@models"
import { ServiceTypes } from "@types"
type InjectedDependencies = {
authProviderRepository: DAL.RepositoryService
}
export default class AuthProviderService<
TEntity extends AuthProvider = AuthProvider
> extends ModulesSdkUtils.abstractServiceFactory<
InjectedDependencies,
{
create: ServiceTypes.CreateAuthProviderDTO
update: ServiceTypes.UpdateAuthProviderDTO
}
>(AuthProvider)<TEntity> {
constructor(container: InjectedDependencies) {
// @ts-ignore
super(...arguments)
}
}
+57
View File
@@ -0,0 +1,57 @@
import { AuthTypes, Context, DAL, FindConfig } from "@medusajs/types"
import {
InjectManager,
MedusaContext,
MedusaError,
ModulesSdkUtils,
} from "@medusajs/utils"
import { AuthUser } from "@models"
import { ServiceTypes, RepositoryTypes } from "@types"
type InjectedDependencies = {
authUserRepository: DAL.RepositoryService
}
export default class AuthUserService<
TEntity extends AuthUser = AuthUser
> extends ModulesSdkUtils.abstractServiceFactory<
InjectedDependencies,
{
create: ServiceTypes.CreateAuthUserDTO
}
>(AuthUser)<TEntity> {
protected readonly authUserRepository_: RepositoryTypes.IAuthUserRepository<TEntity>
constructor(container: InjectedDependencies) {
// @ts-ignore
super(...arguments)
this.authUserRepository_ = container.authUserRepository
}
@InjectManager("authUserRepository_")
async retrieveByProviderAndEntityId<
TEntityMethod = AuthTypes.AuthUserDTO
>(
entityId: string,
provider: string,
config: FindConfig<TEntityMethod> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity> {
const queryConfig = ModulesSdkUtils.buildQuery<TEntity>(
{ entity_id: entityId, provider },
{ ...config, take: 1 }
)
const [result] = await this.authUserRepository_.find(
queryConfig,
sharedContext
)
if (!result) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`AuthUser with entity_id: "${entityId}" and provider: "${provider}" not found`
)
}
return result
}
}
+3
View File
@@ -0,0 +1,3 @@
export { default as AuthModuleService } from "./auth-module"
export { default as AuthProviderService } from "./auth-provider"
export { default as AuthUserService } from "./auth-user"
+8
View File
@@ -0,0 +1,8 @@
import { Logger } from "@medusajs/types"
export type InitializeModuleInjectableDependencies = {
logger?: Logger
}
export * as RepositoryTypes from "./repositories"
export * as ServiceTypes from "./services"
@@ -0,0 +1,26 @@
import { AuthProvider } from "@models"
export type CreateAuthProviderDTO = {
provider: string
name: string
domain?: ProviderDomain
is_active?: boolean
config?: Record<string, unknown>
}
export type UpdateAuthProviderDTO = {
update: {
provider: string
name?: string
domain?: ProviderDomain
is_active?: boolean
config?: Record<string, unknown>
}
provider: AuthProvider
}
export enum ProviderDomain {
ALL = "all",
STORE = "store",
ADMIN = "admin",
}
@@ -0,0 +1,19 @@
import { AuthUser } from "@models"
export type CreateAuthUserDTO = {
provider_id: string
entity_id: string
provider_metadata?: Record<string, unknown>
user_metadata?: Record<string, unknown>
app_metadata?: Record<string, unknown>
}
export type UpdateAuthUserDTO = {
update: {
id: string
provider_metadata?: Record<string, unknown>
user_metadata?: Record<string, unknown>
app_metadata?: Record<string, unknown>
}
user: AuthUser
}
@@ -0,0 +1,28 @@
import { AuthProvider, AuthUser } from "@models"
import { CreateAuthProviderDTO, UpdateAuthProviderDTO } from "./auth-provider"
import { DAL } from "@medusajs/types"
import { CreateAuthUserDTO, UpdateAuthUserDTO } from "./auth-user"
export * from "./auth-user"
export * from "./auth-provider"
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface IAuthProviderRepository<
TEntity extends AuthProvider = AuthProvider
> extends DAL.RepositoryService<
TEntity,
{
create: CreateAuthProviderDTO
update: UpdateAuthProviderDTO
}
> {}
// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface IAuthUserRepository<TEntity extends AuthUser = AuthUser>
extends DAL.RepositoryService<
TEntity,
{
create: CreateAuthUserDTO
update: UpdateAuthUserDTO
}
> {}
@@ -0,0 +1,31 @@
export type AuthProviderDTO = {
provider: string
name: string
domain: ProviderDomain
is_active: boolean
config: Record<string, unknown>
}
export type CreateAuthProviderDTO = {
provider: string
name: string
domain?: ProviderDomain
is_active?: boolean
config?: Record<string, unknown>
}
export type UpdateAuthProviderDTO = {
provider: string
name?: string
domain?: ProviderDomain
is_active?: boolean
config?: Record<string, unknown>
}
export enum ProviderDomain {
ALL = "all",
STORE = "store",
ADMIN = "admin",
}
export type FilterableAuthProviderProps = {}
@@ -0,0 +1,28 @@
import { AuthProviderDTO } from "./auth-provider"
export type AuthUserDTO = {
id: string
provider_id: string
entity_id: string
provider: AuthProviderDTO
provider_metadata?: Record<string, unknown>
user_metadata: Record<string, unknown>
app_metadata: Record<string, unknown>
}
export type CreateAuthUserDTO = {
entity_id: string
provider_id: string
provider_metadata?: Record<string, unknown>
user_metadata?: Record<string, unknown>
app_metadata?: Record<string, unknown>
}
export type UpdateAuthUserDTO = {
id: string
provider_metadata?: Record<string, unknown>
user_metadata?: Record<string, unknown>
app_metadata?: Record<string, unknown>
}
export type FilterableAuthUserProps = {}
@@ -0,0 +1,2 @@
export * from "./auth-user"
export * from "./auth-provider"
+53
View File
@@ -0,0 +1,53 @@
{
"compilerOptions": {
"lib": [
"es2020"
],
"target": "es2020",
"outDir": "./dist",
"esModuleInterop": true,
"declaration": true,
"module": "commonjs",
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"sourceMap": false,
"noImplicitReturns": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noImplicitThis": true,
"allowJs": true,
"skipLibCheck": true,
"downlevelIteration": true,
// to use ES5 specific tooling
"baseUrl": ".",
"resolveJsonModule": true,
"paths": {
"@models": [
"./src/models"
],
"@services": [
"./src/services"
],
"@repositories": [
"./src/repositories"
],
"@types": [
"./src/types"
],
"@providers": [
"./src/providers"
]
}
},
"include": [
"src"
],
"exclude": [
"dist",
"./src/**/__tests__",
"./src/**/__mocks__",
"./src/**/__fixtures__",
"node_modules"
]
}
+8
View File
@@ -0,0 +1,8 @@
{
"extends": "./tsconfig.json",
"include": ["src", "integration-tests"],
"exclude": ["node_modules", "dist"],
"compilerOptions": {
"sourceMap": true
}
}