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:
@@ -8,18 +8,15 @@ export async function createAuthIdentities(
|
||||
id: "test-id",
|
||||
entity_id: "test-id",
|
||||
provider: "manual",
|
||||
scope: "store",
|
||||
},
|
||||
{
|
||||
id: "test-id-1",
|
||||
entity_id: "test-id-1",
|
||||
provider: "manual",
|
||||
scope: "store",
|
||||
},
|
||||
{
|
||||
entity_id: "test-id-2",
|
||||
provider: "store",
|
||||
scope: "store",
|
||||
},
|
||||
]
|
||||
): Promise<AuthIdentity[]> {
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import {
|
||||
AuthIdentityDTO,
|
||||
AuthIdentityProviderService,
|
||||
AuthenticationInput,
|
||||
AuthenticationResponse,
|
||||
} from "@medusajs/types"
|
||||
import { AbstractAuthModuleProvider, MedusaError } from "@medusajs/utils"
|
||||
|
||||
export class AuthServiceFixtures extends AbstractAuthModuleProvider {
|
||||
constructor() {
|
||||
super(
|
||||
{},
|
||||
{ provider: "plaintextpass", displayName: "plaintextpass Fixture" }
|
||||
)
|
||||
}
|
||||
|
||||
async authenticate(
|
||||
authenticationData: AuthenticationInput,
|
||||
service: AuthIdentityProviderService
|
||||
): Promise<AuthenticationResponse> {
|
||||
const { email, password } = authenticationData.body ?? {}
|
||||
let authIdentity: AuthIdentityDTO | undefined
|
||||
try {
|
||||
authIdentity = await service.retrieve({
|
||||
entity_id: email,
|
||||
provider: this.provider,
|
||||
})
|
||||
|
||||
if (authIdentity.provider_metadata?.password === password) {
|
||||
return {
|
||||
success: true,
|
||||
authIdentity,
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (error.type === MedusaError.Types.NOT_FOUND) {
|
||||
const createdAuthIdentity = await service.create({
|
||||
entity_id: email,
|
||||
provider: this.provider,
|
||||
provider_metadata: {
|
||||
password,
|
||||
},
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
authIdentity: createdAuthIdentity,
|
||||
}
|
||||
}
|
||||
|
||||
return { success: false, error: error.message }
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: "Invalid email or password",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const services = [AuthServiceFixtures]
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./default-provider"
|
||||
@@ -1,6 +1,6 @@
|
||||
import { IAuthModuleService } from "@medusajs/types"
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { createAuthIdentities } from "../../../__fixtures__/auth-identity"
|
||||
import { createAuthIdentities } from "../../__fixtures__/auth-identity"
|
||||
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
@@ -216,7 +216,6 @@ moduleIntegrationTestRunner({
|
||||
id: "test",
|
||||
provider: "manual",
|
||||
entity_id: "test",
|
||||
scope: "store",
|
||||
},
|
||||
])
|
||||
|
||||
@@ -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",
|
||||
})
|
||||
)
|
||||
})
|
||||
}),
|
||||
})
|
||||
@@ -1,231 +0,0 @@
|
||||
import { createAuthIdentities } from "../../../__fixtures__/auth-identity"
|
||||
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { IAuthModuleService } from "@medusajs/types"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
moduleIntegrationTestRunner({
|
||||
moduleName: Modules.AUTH,
|
||||
testSuite: ({
|
||||
MikroOrmWrapper,
|
||||
service,
|
||||
}: SuiteOptions<IAuthModuleService>) => {
|
||||
describe("AuthIdentity Service", () => {
|
||||
beforeEach(async () => {
|
||||
await createAuthIdentities(MikroOrmWrapper.forkManager())
|
||||
})
|
||||
|
||||
describe("list", () => {
|
||||
it("should list authIdentities", async () => {
|
||||
const authIdentities = await service.list()
|
||||
const serialized = JSON.parse(JSON.stringify(authIdentities))
|
||||
|
||||
expect(serialized).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_id", async () => {
|
||||
const authIdentities = await service.list({
|
||||
provider: "manual",
|
||||
})
|
||||
|
||||
const serialized = JSON.parse(JSON.stringify(authIdentities))
|
||||
|
||||
expect(serialized).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "test-id",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: "test-id-1",
|
||||
}),
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe("listAndCount", () => {
|
||||
it("should list authIdentities", async () => {
|
||||
const [authIdentities, count] = await service.listAndCount()
|
||||
const serialized = JSON.parse(JSON.stringify(authIdentities))
|
||||
|
||||
expect(count).toEqual(3)
|
||||
expect(serialized).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("retrieve", () => {
|
||||
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 return authIdentity based on config select param", async () => {
|
||||
const authIdentity = await service.retrieve(id, {
|
||||
select: ["id"],
|
||||
})
|
||||
|
||||
const serialized = JSON.parse(JSON.stringify(authIdentity))
|
||||
|
||||
expect(serialized).toEqual({
|
||||
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 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")
|
||||
})
|
||||
})
|
||||
|
||||
describe("delete", () => {
|
||||
it("should delete the authIdentities given an id successfully", async () => {
|
||||
const id = "test-id"
|
||||
|
||||
await service.delete([id])
|
||||
|
||||
const authIdentities = await service.list({
|
||||
id: [id],
|
||||
})
|
||||
|
||||
expect(authIdentities).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(
|
||||
'AuthIdentity with id "does-not-exist" not found'
|
||||
)
|
||||
})
|
||||
|
||||
it("should update authIdentity", async () => {
|
||||
const id = "test-id"
|
||||
|
||||
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("create", () => {
|
||||
it("should create a authIdentity successfully", async () => {
|
||||
await service.create([
|
||||
{
|
||||
id: "test",
|
||||
provider: "manual",
|
||||
entity_id: "test",
|
||||
scope: "store",
|
||||
},
|
||||
])
|
||||
|
||||
const [authIdentity] = await service.list({
|
||||
id: ["test"],
|
||||
})
|
||||
|
||||
expect(authIdentity).toEqual(
|
||||
expect.objectContaining({
|
||||
id: "test",
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -1,41 +0,0 @@
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
|
||||
import { IAuthModuleService } from "@medusajs/types"
|
||||
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
|
||||
moduleIntegrationTestRunner({
|
||||
moduleName: Modules.AUTH,
|
||||
testSuite: ({
|
||||
MikroOrmWrapper,
|
||||
service,
|
||||
}: SuiteOptions<IAuthModuleService>) => {
|
||||
describe("AuthModuleService - AuthProvider", () => {
|
||||
describe("authenticate", () => {
|
||||
it("authenticate validates that a provider is registered in container", async () => {
|
||||
const { success, error } = await service.authenticate(
|
||||
"notRegistered",
|
||||
{} as any
|
||||
)
|
||||
|
||||
expect(success).toBe(false)
|
||||
expect(error).toEqual(
|
||||
"AuthenticationProvider: 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", {
|
||||
authScope: "non-existing",
|
||||
} as any)
|
||||
|
||||
expect(success).toBe(false)
|
||||
expect(error).toEqual(
|
||||
`Scope "non-existing" is not valid for provider emailpass`
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -1,133 +0,0 @@
|
||||
import { MedusaModule, Modules } from "@medusajs/modules-sdk"
|
||||
|
||||
import { IAuthModuleService } from "@medusajs/types"
|
||||
import Scrypt from "scrypt-kdf"
|
||||
import { createAuthIdentities } from "../../../__fixtures__/auth-identity"
|
||||
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||
|
||||
jest.setTimeout(30000)
|
||||
const seedDefaultData = async (manager) => {
|
||||
await createAuthIdentities(manager)
|
||||
}
|
||||
|
||||
moduleIntegrationTestRunner({
|
||||
moduleName: Modules.AUTH,
|
||||
moduleOptions: {
|
||||
providers: [
|
||||
{
|
||||
name: "emailpass",
|
||||
scopes: {
|
||||
admin: {},
|
||||
store: {},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
testSuite: ({
|
||||
MikroOrmWrapper,
|
||||
service,
|
||||
}: SuiteOptions<IAuthModuleService>) => {
|
||||
describe("AuthModuleService - AuthProvider", () => {
|
||||
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(MikroOrmWrapper.forkManager())
|
||||
await createAuthIdentities(MikroOrmWrapper.forkManager(), [
|
||||
// Add authenticated user
|
||||
{
|
||||
provider: "emailpass",
|
||||
entity_id: email,
|
||||
scope: "store",
|
||||
provider_metadata: {
|
||||
password: passwordHash,
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const res = await service.authenticate("emailpass", {
|
||||
body: {
|
||||
email: "test@test.com",
|
||||
password: password,
|
||||
},
|
||||
authScope: "store",
|
||||
} as any)
|
||||
|
||||
expect(res).toEqual({
|
||||
success: true,
|
||||
authIdentity: expect.objectContaining({
|
||||
entity_id: email,
|
||||
provider_metadata: {},
|
||||
}),
|
||||
})
|
||||
})
|
||||
|
||||
it("fails when no password is given", async () => {
|
||||
await seedDefaultData(MikroOrmWrapper.forkManager())
|
||||
|
||||
const res = await service.authenticate("emailpass", {
|
||||
body: { email: "test@test.com" },
|
||||
authScope: "store",
|
||||
} as any)
|
||||
|
||||
expect(res).toEqual({
|
||||
success: false,
|
||||
error: "Password should be a string",
|
||||
})
|
||||
})
|
||||
|
||||
it("fails when no email is given", async () => {
|
||||
await seedDefaultData(MikroOrmWrapper.forkManager())
|
||||
|
||||
const res = await service.authenticate("emailpass", {
|
||||
body: { password: "supersecret" },
|
||||
authScope: "store",
|
||||
} as any)
|
||||
|
||||
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(MikroOrmWrapper.forkManager())
|
||||
await createAuthIdentities(MikroOrmWrapper.forkManager(), [
|
||||
// Add authenticated user
|
||||
{
|
||||
provider: "emailpass",
|
||||
scope: "store",
|
||||
entity_id: email,
|
||||
provider_metadata: {
|
||||
password_hash: passwordHash,
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
const res = await service.authenticate("emailpass", {
|
||||
body: {
|
||||
email: "test@test.com",
|
||||
password: "password",
|
||||
},
|
||||
authScope: "store",
|
||||
} as any)
|
||||
|
||||
expect(res).toEqual({
|
||||
success: false,
|
||||
error: "Invalid email or password",
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
@@ -48,6 +48,7 @@
|
||||
"typescript": "^5.1.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@medusajs/auth-emailpass": "0.0.1",
|
||||
"@medusajs/modules-sdk": "^1.12.9",
|
||||
"@medusajs/types": "^1.11.14",
|
||||
"@medusajs/utils": "^1.11.7",
|
||||
|
||||
@@ -1,21 +1,26 @@
|
||||
import * as defaultProviders from "@providers"
|
||||
import EmailPassProvider from "@medusajs/auth-emailpass"
|
||||
|
||||
import { LoaderOptions, ModulesSdkTypes, ModuleProvider } from "@medusajs/types"
|
||||
import { Lifetime, asFunction, asValue } from "awilix"
|
||||
import { moduleProviderLoader } from "@medusajs/modules-sdk"
|
||||
import {
|
||||
AuthModuleProviderConfig,
|
||||
AuthProviderScope,
|
||||
LoaderOptions,
|
||||
ModulesSdkTypes,
|
||||
} from "@medusajs/types"
|
||||
import {
|
||||
AwilixContainer,
|
||||
ClassOrFunctionReturning,
|
||||
Constructor,
|
||||
Resolver,
|
||||
asClass,
|
||||
} from "awilix"
|
||||
AuthIdentifiersRegistrationName,
|
||||
AuthProviderRegistrationPrefix,
|
||||
} from "@types"
|
||||
|
||||
type AuthModuleProviders = {
|
||||
providers: AuthModuleProviderConfig[]
|
||||
const registrationFn = async (klass, container, pluginOptions) => {
|
||||
Object.entries(pluginOptions.config || []).map(([name, config]) => {
|
||||
container.register({
|
||||
[AuthProviderRegistrationPrefix + name]: asFunction(
|
||||
(cradle) => new klass(cradle, config),
|
||||
{
|
||||
lifetime: klass.LIFE_TIME || Lifetime.SINGLETON,
|
||||
}
|
||||
),
|
||||
})
|
||||
|
||||
container.registerAdd(AuthIdentifiersRegistrationName, asValue(name))
|
||||
})
|
||||
}
|
||||
|
||||
export default async ({
|
||||
@@ -25,48 +30,28 @@ export default async ({
|
||||
(
|
||||
| ModulesSdkTypes.ModuleServiceInitializeOptions
|
||||
| ModulesSdkTypes.ModuleServiceInitializeCustomDataLayerOptions
|
||||
) &
|
||||
AuthModuleProviders
|
||||
) & { providers: ModuleProvider[] }
|
||||
>): Promise<void> => {
|
||||
const providerMap = new Map(
|
||||
options?.providers?.map((provider) => [provider.name, provider.scopes]) ??
|
||||
[]
|
||||
)
|
||||
// TODO: Temporary settings used by the starter, remove once the auth module is updated
|
||||
const isLegacyOptions =
|
||||
options?.providers?.length && !!(options?.providers[0] as any)?.name
|
||||
|
||||
// if(options?.providers?.length) {
|
||||
// TODO: implement plugin provider registration
|
||||
// }
|
||||
// Note: For now we want to inject some providers out of the box
|
||||
const providerConfig = [
|
||||
{
|
||||
resolve: EmailPassProvider,
|
||||
options: {
|
||||
config: {
|
||||
emailpass: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
...(isLegacyOptions ? [] : options?.providers ?? []),
|
||||
]
|
||||
|
||||
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),
|
||||
await moduleProviderLoader({
|
||||
container,
|
||||
providers: providerConfig,
|
||||
registerServiceFn: registrationFn,
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,15 +31,6 @@
|
||||
"nullable": false,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"scope": {
|
||||
"name": "scope",
|
||||
"type": "text",
|
||||
"unsigned": false,
|
||||
"autoincrement": false,
|
||||
"primary": false,
|
||||
"nullable": false,
|
||||
"mappedType": "text"
|
||||
},
|
||||
"user_metadata": {
|
||||
"name": "user_metadata",
|
||||
"type": "jsonb",
|
||||
@@ -72,8 +63,8 @@
|
||||
"schema": "public",
|
||||
"indexes": [
|
||||
{
|
||||
"keyName": "IDX_auth_identity_provider_scope_entity_id",
|
||||
"columnNames": ["provider", "scope", "entity_id"],
|
||||
"keyName": "IDX_auth_identity_provider_entity_id",
|
||||
"columnNames": ["provider", "entity_id"],
|
||||
"composite": true,
|
||||
"primary": false,
|
||||
"unique": true
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
import { Migration } from "@mikro-orm/migrations"
|
||||
|
||||
export class Migration20240205025924 extends Migration {
|
||||
async up(): Promise<void> {
|
||||
this.addSql(
|
||||
'create table if not exists "auth_identity" ("id" text not null, "entity_id" text not null, "provider" text not null, "scope" text not null, "user_metadata" jsonb null, "app_metadata" jsonb not null, "provider_metadata" jsonb null, constraint "auth_identity_pkey" primary key ("id"));'
|
||||
)
|
||||
this.addSql(
|
||||
'alter table "auth_identity" add constraint "IDX_auth_identity_provider_scope_entity_id" unique ("provider", "scope", "entity_id");'
|
||||
)
|
||||
}
|
||||
|
||||
async down(): Promise<void> {
|
||||
this.addSql('drop table if exists "auth_identity" cascade;')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Migration } from "@mikro-orm/migrations"
|
||||
|
||||
export class Migration20240205025928 extends Migration {
|
||||
async up(): Promise<void> {
|
||||
this.addSql(
|
||||
'create table if not exists "auth_identity" ("id" text not null, "entity_id" text not null, "provider" text not null, "user_metadata" jsonb null, "app_metadata" jsonb null, "provider_metadata" jsonb null, constraint "auth_identity_pkey" primary key ("id"));'
|
||||
)
|
||||
this.addSql(
|
||||
'alter table "auth_identity" add constraint "IDX_auth_identity_provider_entity_id" unique ("provider", "entity_id");'
|
||||
)
|
||||
|
||||
this.addSql('alter table "auth_identity" drop column if exists "scope";')
|
||||
this.addSql(
|
||||
`alter table "auth_identity" alter column "app_metadata" drop not null;`
|
||||
)
|
||||
}
|
||||
|
||||
async down(): Promise<void> {
|
||||
this.addSql('drop table if exists "auth_identity" cascade;')
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,8 @@ type OptionalFields = "provider_metadata" | "app_metadata" | "user_metadata"
|
||||
|
||||
@Entity()
|
||||
@Unique({
|
||||
properties: ["provider", "scope", "entity_id"],
|
||||
name: "IDX_auth_identity_provider_scope_entity_id",
|
||||
properties: ["provider", "entity_id"],
|
||||
name: "IDX_auth_identity_provider_entity_id",
|
||||
})
|
||||
export default class AuthIdentity {
|
||||
[OptionalProps]: OptionalFields
|
||||
@@ -29,14 +29,11 @@ export default class AuthIdentity {
|
||||
@Property({ columnType: "text" })
|
||||
provider: string
|
||||
|
||||
@Property({ columnType: "text" })
|
||||
scope: string
|
||||
|
||||
@Property({ columnType: "jsonb", nullable: true })
|
||||
user_metadata: Record<string, unknown> | null
|
||||
|
||||
@Property({ columnType: "jsonb" })
|
||||
app_metadata: Record<string, unknown> = {}
|
||||
@Property({ columnType: "jsonb", nullable: true })
|
||||
app_metadata: Record<string, unknown> | null
|
||||
|
||||
@Property({ columnType: "jsonb", nullable: true })
|
||||
provider_metadata: Record<string, unknown> | null = null
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AuthModuleService } from "@services"
|
||||
import { ModuleExports } from "@medusajs/types"
|
||||
import { AuthModuleService } from "@services"
|
||||
import loadProviders from "./loaders/providers"
|
||||
|
||||
const service = AuthModuleService
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
import { AuthenticationInput, AuthenticationResponse } from "@medusajs/types"
|
||||
import {
|
||||
AbstractAuthModuleProvider,
|
||||
MedusaError,
|
||||
isString,
|
||||
} from "@medusajs/utils"
|
||||
|
||||
import { AuthIdentityService } from "@services"
|
||||
import Scrypt from "scrypt-kdf"
|
||||
|
||||
class EmailPasswordProvider extends AbstractAuthModuleProvider {
|
||||
public static PROVIDER = "emailpass"
|
||||
public static DISPLAY_NAME = "Email/Password Authentication"
|
||||
|
||||
protected readonly authIdentitySerivce_: AuthIdentityService
|
||||
|
||||
constructor({
|
||||
authIdentityService,
|
||||
}: {
|
||||
authIdentityService: AuthIdentityService
|
||||
}) {
|
||||
super(arguments[0], {
|
||||
provider: EmailPasswordProvider.PROVIDER,
|
||||
displayName: EmailPasswordProvider.DISPLAY_NAME,
|
||||
})
|
||||
|
||||
this.authIdentitySerivce_ = authIdentityService
|
||||
}
|
||||
|
||||
private getHashConfig() {
|
||||
const scopeConfig = this.scopeConfig_.hashConfig as
|
||||
| Scrypt.ScryptParams
|
||||
| undefined
|
||||
|
||||
const defaultHashConfig = { logN: 15, r: 8, p: 1 }
|
||||
|
||||
// Return custom defined hash config or default hash parameters
|
||||
return scopeConfig ?? defaultHashConfig
|
||||
}
|
||||
|
||||
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",
|
||||
}
|
||||
}
|
||||
let authIdentity
|
||||
|
||||
try {
|
||||
authIdentity =
|
||||
await this.authIdentitySerivce_.retrieveByProviderAndEntityId(
|
||||
email,
|
||||
EmailPasswordProvider.PROVIDER
|
||||
)
|
||||
} catch (error) {
|
||||
if (error.type === MedusaError.Types.NOT_FOUND) {
|
||||
const password_hash = await Scrypt.kdf(password, this.getHashConfig())
|
||||
|
||||
const [createdAuthIdentity] = await this.authIdentitySerivce_.create([
|
||||
{
|
||||
entity_id: email,
|
||||
provider: EmailPasswordProvider.PROVIDER,
|
||||
scope: this.scope_,
|
||||
provider_metadata: {
|
||||
password: password_hash.toString("base64"),
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
return {
|
||||
success: true,
|
||||
authIdentity: JSON.parse(JSON.stringify(createdAuthIdentity)),
|
||||
}
|
||||
}
|
||||
return { success: false, error: error.message }
|
||||
}
|
||||
|
||||
const password_hash = authIdentity.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 authIdentity.provider_metadata!.password
|
||||
|
||||
return {
|
||||
success,
|
||||
authIdentity: JSON.parse(JSON.stringify(authIdentity)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
error: "Invalid email or password",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default EmailPasswordProvider
|
||||
@@ -1,13 +1,12 @@
|
||||
import { AuthenticationInput, AuthenticationResponse } from "@medusajs/types"
|
||||
import { AbstractAuthModuleProvider, MedusaError } from "@medusajs/utils"
|
||||
import { AuthIdentityService } from "@services"
|
||||
import jwt, { JwtPayload } from "jsonwebtoken"
|
||||
|
||||
import { AuthorizationCode } from "simple-oauth2"
|
||||
import url from "url"
|
||||
|
||||
type InjectedDependencies = {
|
||||
authIdentityService: AuthIdentityService
|
||||
authIdentityService: any
|
||||
}
|
||||
|
||||
type ProviderConfig = {
|
||||
@@ -18,15 +17,12 @@ type ProviderConfig = {
|
||||
}
|
||||
|
||||
class GoogleProvider extends AbstractAuthModuleProvider {
|
||||
public static PROVIDER = "google"
|
||||
public static DISPLAY_NAME = "Google Authentication"
|
||||
protected readonly authIdentityService_: any
|
||||
|
||||
protected readonly authIdentityService_: AuthIdentityService
|
||||
|
||||
constructor({ authIdentityService }: InjectedDependencies) {
|
||||
constructor({ authIdentityService }: InjectedDependencies, options: any) {
|
||||
super(arguments[0], {
|
||||
provider: GoogleProvider.PROVIDER,
|
||||
displayName: GoogleProvider.DISPLAY_NAME,
|
||||
provider: "google",
|
||||
displayName: "Google Authentication",
|
||||
})
|
||||
|
||||
this.authIdentityService_ = authIdentityService
|
||||
@@ -72,6 +68,9 @@ class GoogleProvider extends AbstractAuthModuleProvider {
|
||||
}
|
||||
|
||||
const code = req.query?.code ?? req.body?.code
|
||||
if (!code) {
|
||||
return { success: false, error: "No code provided" }
|
||||
}
|
||||
|
||||
return await this.validateCallbackToken(code, config)
|
||||
}
|
||||
@@ -89,16 +88,15 @@ class GoogleProvider extends AbstractAuthModuleProvider {
|
||||
authIdentity =
|
||||
await this.authIdentityService_.retrieveByProviderAndEntityId(
|
||||
entity_id,
|
||||
GoogleProvider.PROVIDER
|
||||
this.provider
|
||||
)
|
||||
} catch (error) {
|
||||
if (error.type === MedusaError.Types.NOT_FOUND) {
|
||||
const [createdAuthIdentity] = await this.authIdentityService_.create([
|
||||
{
|
||||
entity_id,
|
||||
provider: GoogleProvider.PROVIDER,
|
||||
provider: this.provider,
|
||||
user_metadata: jwtData!.payload,
|
||||
scope: this.scope_,
|
||||
},
|
||||
])
|
||||
authIdentity = createdAuthIdentity
|
||||
@@ -132,7 +130,7 @@ class GoogleProvider extends AbstractAuthModuleProvider {
|
||||
accessToken.token.id_token
|
||||
)
|
||||
|
||||
const { successRedirectUrl } = this.getConfigFromScope()
|
||||
const { successRedirectUrl } = this.getConfig()
|
||||
|
||||
return {
|
||||
success,
|
||||
@@ -144,8 +142,10 @@ class GoogleProvider extends AbstractAuthModuleProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private getConfigFromScope(): ProviderConfig {
|
||||
const config: Partial<ProviderConfig> = { ...this.scopeConfig_ }
|
||||
private getConfig(): ProviderConfig {
|
||||
// TODO: Fetch this from provider config
|
||||
// const config: Partial<ProviderConfig> = { ...this.scopeConfig_ }
|
||||
const config = {} as any
|
||||
|
||||
if (!config.clientID) {
|
||||
throw new Error("Google clientID is required")
|
||||
@@ -163,7 +163,7 @@ class GoogleProvider extends AbstractAuthModuleProvider {
|
||||
}
|
||||
|
||||
private originalURL(req: AuthenticationInput) {
|
||||
const host = req.headers.host
|
||||
const host = req.headers?.host
|
||||
const protocol = req.protocol
|
||||
const path = req.url || ""
|
||||
|
||||
@@ -173,7 +173,7 @@ class GoogleProvider extends AbstractAuthModuleProvider {
|
||||
private async getProviderConfig(
|
||||
req: AuthenticationInput
|
||||
): Promise<ProviderConfig> {
|
||||
const config = this.getConfigFromScope()
|
||||
const config = this.getConfig()
|
||||
|
||||
const callbackURL = config.callbackURL
|
||||
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
export { default as EmailPasswordProvider } from "./email-password"
|
||||
export { default as GoogleProvider } from "./google"
|
||||
export { default as GoogleProvider } from "./google"
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
import {
|
||||
AuthTypes,
|
||||
Context,
|
||||
DAL,
|
||||
FindConfig,
|
||||
RepositoryService,
|
||||
} from "@medusajs/types"
|
||||
import {
|
||||
InjectManager,
|
||||
MedusaContext,
|
||||
MedusaError,
|
||||
ModulesSdkUtils,
|
||||
} from "@medusajs/utils"
|
||||
import { AuthIdentity } from "@models"
|
||||
|
||||
type InjectedDependencies = {
|
||||
baseRepository: DAL.RepositoryService
|
||||
authIdentityRepository: DAL.RepositoryService
|
||||
}
|
||||
|
||||
export default class AuthIdentityService<
|
||||
TEntity extends AuthIdentity = AuthIdentity
|
||||
> extends ModulesSdkUtils.internalModuleServiceFactory<InjectedDependencies>(
|
||||
AuthIdentity
|
||||
)<TEntity> {
|
||||
protected readonly authIdentityRepository_: RepositoryService<TEntity>
|
||||
protected baseRepository_: DAL.RepositoryService
|
||||
|
||||
constructor(container: InjectedDependencies) {
|
||||
// @ts-ignore
|
||||
super(...arguments)
|
||||
this.authIdentityRepository_ = container.authIdentityRepository
|
||||
this.baseRepository_ = container.baseRepository
|
||||
}
|
||||
|
||||
@InjectManager("authIdentityRepository_")
|
||||
async retrieveByProviderAndEntityId<
|
||||
TEntityMethod = AuthTypes.AuthIdentityDTO
|
||||
>(
|
||||
entityId: string,
|
||||
provider: string,
|
||||
config: FindConfig<TEntityMethod> = {},
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<AuthTypes.AuthIdentityDTO> {
|
||||
const queryConfig = ModulesSdkUtils.buildQuery<TEntity>(
|
||||
{ entity_id: entityId, provider },
|
||||
{ ...config, take: 1 }
|
||||
)
|
||||
const [result] = await this.authIdentityRepository_.find(
|
||||
queryConfig,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
if (!result) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`AuthIdentity with entity_id: "${entityId}" and provider: "${provider}" not found`
|
||||
)
|
||||
}
|
||||
|
||||
return await this.baseRepository_.serialize<AuthTypes.AuthIdentityDTO>(
|
||||
result
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import {
|
||||
AuthenticationInput,
|
||||
AuthenticationResponse,
|
||||
AuthIdentityProviderService,
|
||||
AuthTypes,
|
||||
Context,
|
||||
DAL,
|
||||
@@ -14,16 +15,17 @@ import { AuthIdentity } from "@models"
|
||||
import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config"
|
||||
|
||||
import {
|
||||
AbstractAuthModuleProvider,
|
||||
InjectManager,
|
||||
MedusaContext,
|
||||
MedusaError,
|
||||
ModulesSdkUtils,
|
||||
} from "@medusajs/utils"
|
||||
import AuthProviderService from "./auth-provider"
|
||||
|
||||
type InjectedDependencies = {
|
||||
baseRepository: DAL.RepositoryService
|
||||
authIdentityService: ModulesSdkTypes.InternalModuleService<any>
|
||||
authProviderService: AuthProviderService
|
||||
}
|
||||
|
||||
const generateMethodForModels = [AuthIdentity]
|
||||
@@ -42,9 +44,14 @@ export default class AuthModuleService<
|
||||
{
|
||||
protected baseRepository_: DAL.RepositoryService
|
||||
protected authIdentityService_: ModulesSdkTypes.InternalModuleService<TAuthIdentity>
|
||||
protected readonly authProviderService_: AuthProviderService
|
||||
|
||||
constructor(
|
||||
{ authIdentityService, baseRepository }: InjectedDependencies,
|
||||
{
|
||||
authIdentityService,
|
||||
authProviderService,
|
||||
baseRepository,
|
||||
}: InjectedDependencies,
|
||||
protected readonly moduleDeclaration: InternalModuleDeclaration
|
||||
) {
|
||||
// @ts-ignore
|
||||
@@ -52,6 +59,7 @@ export default class AuthModuleService<
|
||||
|
||||
this.baseRepository_ = baseRepository
|
||||
this.authIdentityService_ = authIdentityService
|
||||
this.authProviderService_ = authProviderService
|
||||
}
|
||||
|
||||
__joinerConfig(): ModuleJoinerConfig {
|
||||
@@ -116,34 +124,16 @@ export default class AuthModuleService<
|
||||
return Array.isArray(data) ? serializedUsers : serializedUsers[0]
|
||||
}
|
||||
|
||||
protected getRegisteredAuthenticationProvider(
|
||||
provider: string,
|
||||
{ authScope }: AuthenticationInput
|
||||
): AbstractAuthModuleProvider {
|
||||
let containerProvider: AbstractAuthModuleProvider
|
||||
try {
|
||||
containerProvider = this.__container__[`auth_provider_${provider}`]
|
||||
} catch (error) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`AuthenticationProvider: ${provider} wasn't registered in the module. Have you configured your options correctly?`
|
||||
)
|
||||
}
|
||||
|
||||
return containerProvider.withScope(authScope)
|
||||
}
|
||||
|
||||
async authenticate(
|
||||
provider: string,
|
||||
authenticationData: AuthenticationInput
|
||||
): Promise<AuthenticationResponse> {
|
||||
try {
|
||||
const registeredProvider = this.getRegisteredAuthenticationProvider(
|
||||
return await this.authProviderService_.authenticate(
|
||||
provider,
|
||||
authenticationData
|
||||
authenticationData,
|
||||
this.getAuthIdentityProviderService()
|
||||
)
|
||||
|
||||
return await registeredProvider.authenticate(authenticationData)
|
||||
} catch (error) {
|
||||
return { success: false, error: error.message }
|
||||
}
|
||||
@@ -154,14 +144,49 @@ export default class AuthModuleService<
|
||||
authenticationData: AuthenticationInput
|
||||
): Promise<AuthenticationResponse> {
|
||||
try {
|
||||
const registeredProvider = this.getRegisteredAuthenticationProvider(
|
||||
return await this.authProviderService_.validateCallback(
|
||||
provider,
|
||||
authenticationData
|
||||
authenticationData,
|
||||
this.getAuthIdentityProviderService()
|
||||
)
|
||||
|
||||
return await registeredProvider.validateCallback(authenticationData)
|
||||
} catch (error) {
|
||||
return { success: false, error: error.message }
|
||||
}
|
||||
}
|
||||
|
||||
getAuthIdentityProviderService(): AuthIdentityProviderService {
|
||||
return {
|
||||
retrieve: async ({ entity_id, provider }) => {
|
||||
const authIdentities = await this.authIdentityService_.list({
|
||||
entity_id,
|
||||
provider,
|
||||
})
|
||||
|
||||
if (!authIdentities.length) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`AuthIdentity with entity_id "${entity_id}" not found`
|
||||
)
|
||||
}
|
||||
|
||||
if (authIdentities.length > 1) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Multiple authIdentities found for entity_id "${entity_id}"`
|
||||
)
|
||||
}
|
||||
|
||||
return await this.baseRepository_.serialize<AuthTypes.AuthIdentityDTO>(
|
||||
authIdentities[0]
|
||||
)
|
||||
},
|
||||
create: async (data: AuthTypes.CreateAuthIdentityDTO) => {
|
||||
const createdAuthIdentity = await this.authIdentityService_.create(data)
|
||||
|
||||
return await this.baseRepository_.serialize<AuthTypes.AuthIdentityDTO>(
|
||||
createdAuthIdentity
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
56
packages/modules/auth/src/services/auth-provider.ts
Normal file
56
packages/modules/auth/src/services/auth-provider.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import {
|
||||
AuthTypes,
|
||||
AuthenticationInput,
|
||||
AuthIdentityProviderService,
|
||||
AuthenticationResponse,
|
||||
} from "@medusajs/types"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { AuthProviderRegistrationPrefix } from "@types"
|
||||
|
||||
type InjectedDependencies = {
|
||||
[
|
||||
key: `${typeof AuthProviderRegistrationPrefix}${string}`
|
||||
]: AuthTypes.IAuthProvider
|
||||
}
|
||||
|
||||
export default class AuthProviderService {
|
||||
protected dependencies: InjectedDependencies
|
||||
|
||||
constructor(container: InjectedDependencies) {
|
||||
this.dependencies = container
|
||||
}
|
||||
|
||||
protected retrieveProviderRegistration(
|
||||
providerId: string
|
||||
): AuthTypes.IAuthProvider {
|
||||
try {
|
||||
return this.dependencies[`${AuthProviderRegistrationPrefix}${providerId}`]
|
||||
} catch (err) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`Could not find a auth provider with id: ${providerId}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
async authenticate(
|
||||
provider: string,
|
||||
auth: AuthenticationInput,
|
||||
authIdentityProviderService: AuthIdentityProviderService
|
||||
): Promise<AuthenticationResponse> {
|
||||
const providerHandler = this.retrieveProviderRegistration(provider)
|
||||
return await providerHandler.authenticate(auth, authIdentityProviderService)
|
||||
}
|
||||
|
||||
async validateCallback(
|
||||
provider: string,
|
||||
auth: AuthenticationInput,
|
||||
authIdentityProviderService: AuthIdentityProviderService
|
||||
): Promise<AuthenticationResponse> {
|
||||
const providerHandler = this.retrieveProviderRegistration(provider)
|
||||
return await providerHandler.validateCallback(
|
||||
auth,
|
||||
authIdentityProviderService
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,2 @@
|
||||
export { default as AuthModuleService } from "./auth-module"
|
||||
export { default as AuthIdentityService } from "./auth-identity"
|
||||
export { default as AuthProviderService } from "./auth-provider"
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
import { ModuleProviderExports } from "@medusajs/types"
|
||||
import { ModuleServiceInitializeOptions } from "@medusajs/types"
|
||||
import { Logger } from "@medusajs/types"
|
||||
|
||||
export type InitializeModuleInjectableDependencies = {
|
||||
logger?: Logger
|
||||
}
|
||||
|
||||
export const AuthIdentifiersRegistrationName = "auth_providers_identifier"
|
||||
|
||||
export const AuthProviderRegistrationPrefix = "au_"
|
||||
|
||||
export type AuthModuleOptions = Partial<ModuleServiceInitializeOptions> & {
|
||||
/**
|
||||
* Providers to be registered
|
||||
*/
|
||||
providers?: {
|
||||
/**
|
||||
* The module provider to be registered
|
||||
*/
|
||||
resolve: string | ModuleProviderExports
|
||||
options: {
|
||||
/**
|
||||
* key value pair of the provider name and the configuration to be passed to the provider constructor
|
||||
*/
|
||||
config: Record<string, unknown>
|
||||
}
|
||||
}[]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user