feat(auth): Revamp authentication setup (#7387)

* chore: Clean up authentication middlewares

* chore: Rename AuthUser to AuthIdentity

* feat: Define link between user, customer, and auth identity

* feat: Use links for auth, update auth context content

* fix: Adjust user create command with new auth setup

* fix: Make auth login more dynamic, review fixes

* fix: Change test assertions for created by
This commit is contained in:
Stevche Radevski
2024-05-22 10:27:32 +02:00
committed by GitHub
parent b7df447682
commit 5ede560f70
88 changed files with 887 additions and 1014 deletions

View File

@@ -1,7 +1,7 @@
import { AuthUser } from "@models"
import { AuthIdentity } from "@models"
import { SqlEntityManager } from "@mikro-orm/postgresql"
export async function createAuthUsers(
export async function createAuthIdentities(
manager: SqlEntityManager,
userData: any[] = [
{
@@ -22,16 +22,16 @@ export async function createAuthUsers(
scope: "store",
},
]
): Promise<AuthUser[]> {
const authUsers: AuthUser[] = []
): Promise<AuthIdentity[]> {
const authIdentities: AuthIdentity[] = []
for (const user of userData) {
const authUser = manager.create(AuthUser, user)
const authIdentity = manager.create(AuthIdentity, user)
authUsers.push(authUser)
authIdentities.push(authIdentity)
}
await manager.persistAndFlush(authUsers)
await manager.persistAndFlush(authIdentities)
return authUsers
return authIdentities
}

View File

@@ -1,4 +1,4 @@
import { createAuthUsers } from "../../../__fixtures__/auth-user"
import { createAuthIdentities } from "../../../__fixtures__/auth-identity"
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
import { Modules } from "@medusajs/modules-sdk"
import { IAuthModuleService } from "@medusajs/types"
@@ -11,15 +11,15 @@ moduleIntegrationTestRunner({
MikroOrmWrapper,
service,
}: SuiteOptions<IAuthModuleService>) => {
describe("AuthUser Service", () => {
describe("AuthIdentity Service", () => {
beforeEach(async () => {
await createAuthUsers(MikroOrmWrapper.forkManager())
await createAuthIdentities(MikroOrmWrapper.forkManager())
})
describe("list", () => {
it("should list authUsers", async () => {
const authUsers = await service.list()
const serialized = JSON.parse(JSON.stringify(authUsers))
it("should list authIdentities", async () => {
const authIdentities = await service.list()
const serialized = JSON.parse(JSON.stringify(authIdentities))
expect(serialized).toEqual([
expect.objectContaining({
@@ -34,24 +34,24 @@ moduleIntegrationTestRunner({
])
})
it("should list authUsers by id", async () => {
const authUsers = await service.list({
it("should list authIdentities by id", async () => {
const authIdentities = await service.list({
id: ["test-id"],
})
expect(authUsers).toEqual([
expect(authIdentities).toEqual([
expect.objectContaining({
id: "test-id",
}),
])
})
it("should list authUsers by provider_id", async () => {
const authUsers = await service.list({
it("should list authIdentities by provider_id", async () => {
const authIdentities = await service.list({
provider: "manual",
})
const serialized = JSON.parse(JSON.stringify(authUsers))
const serialized = JSON.parse(JSON.stringify(authIdentities))
expect(serialized).toEqual([
expect.objectContaining({
@@ -65,9 +65,9 @@ moduleIntegrationTestRunner({
})
describe("listAndCount", () => {
it("should list authUsers", async () => {
const [authUsers, count] = await service.listAndCount()
const serialized = JSON.parse(JSON.stringify(authUsers))
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([
@@ -83,13 +83,13 @@ moduleIntegrationTestRunner({
])
})
it("should listAndCount authUsers by provider_id", async () => {
const [authUsers, count] = await service.listAndCount({
it("should listAndCount authIdentities by provider_id", async () => {
const [authIdentities, count] = await service.listAndCount({
provider: "manual",
})
expect(count).toEqual(2)
expect(authUsers).toEqual([
expect(authIdentities).toEqual([
expect.objectContaining({
id: "test-id",
}),
@@ -103,29 +103,29 @@ moduleIntegrationTestRunner({
describe("retrieve", () => {
const id = "test-id"
it("should return an authUser for the given id", async () => {
const authUser = await service.retrieve(id)
it("should return an authIdentity for the given id", async () => {
const authIdentity = await service.retrieve(id)
expect(authUser).toEqual(
expect(authIdentity).toEqual(
expect.objectContaining({
id,
})
)
})
it("should return authUser based on config select param", async () => {
const authUser = await service.retrieve(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(authUser))
const serialized = JSON.parse(JSON.stringify(authIdentity))
expect(serialized).toEqual({
id,
})
})
it("should throw an error when an authUser with the given id does not exist", async () => {
it("should throw an error when an authIdentity with the given id does not exist", async () => {
let error
try {
@@ -135,11 +135,11 @@ moduleIntegrationTestRunner({
}
expect(error.message).toEqual(
"AuthUser with id: does-not-exist was not found"
"AuthIdentity with id: does-not-exist was not found"
)
})
it("should throw an error when a authUserId is not provided", async () => {
it("should throw an error when a authIdentityId is not provided", async () => {
let error
try {
@@ -148,21 +148,21 @@ moduleIntegrationTestRunner({
error = e
}
expect(error.message).toEqual("authUser - id must be defined")
expect(error.message).toEqual("authIdentity - id must be defined")
})
})
describe("delete", () => {
it("should delete the authUsers given an id successfully", async () => {
it("should delete the authIdentities given an id successfully", async () => {
const id = "test-id"
await service.delete([id])
const authUsers = await service.list({
const authIdentities = await service.list({
id: [id],
})
expect(authUsers).toHaveLength(0)
expect(authIdentities).toHaveLength(0)
})
})
@@ -181,11 +181,11 @@ moduleIntegrationTestRunner({
}
expect(error.message).toEqual(
'AuthUser with id "does-not-exist" not found'
'AuthIdentity with id "does-not-exist" not found'
)
})
it("should update authUser", async () => {
it("should update authIdentity", async () => {
const id = "test-id"
await service.update([
@@ -195,8 +195,8 @@ moduleIntegrationTestRunner({
},
])
const [authUser] = await service.list({ id: [id] })
expect(authUser).toEqual(
const [authIdentity] = await service.list({ id: [id] })
expect(authIdentity).toEqual(
expect.objectContaining({
provider_metadata: { email: "test@email.com" },
})
@@ -205,7 +205,7 @@ moduleIntegrationTestRunner({
})
describe("create", () => {
it("should create a authUser successfully", async () => {
it("should create a authIdentity successfully", async () => {
await service.create([
{
id: "test",
@@ -215,11 +215,11 @@ moduleIntegrationTestRunner({
},
])
const [authUser] = await service.list({
const [authIdentity] = await service.list({
id: ["test"],
})
expect(authUser).toEqual(
expect(authIdentity).toEqual(
expect.objectContaining({
id: "test",
})

View File

@@ -1,6 +1,6 @@
import { IAuthModuleService } from "@medusajs/types"
import { Modules } from "@medusajs/modules-sdk"
import { createAuthUsers } from "../../../__fixtures__/auth-user"
import { createAuthIdentities } from "../../../__fixtures__/auth-identity"
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
jest.setTimeout(30000)
@@ -11,16 +11,16 @@ moduleIntegrationTestRunner({
MikroOrmWrapper,
service,
}: SuiteOptions<IAuthModuleService>) => {
describe("AuthModuleService - AuthUser", () => {
describe("AuthModuleService - AuthIdentity", () => {
beforeEach(async () => {
await createAuthUsers(MikroOrmWrapper.forkManager())
await createAuthIdentities(MikroOrmWrapper.forkManager())
})
describe("listAuthUsers", () => {
it("should list authUsers", async () => {
const authUsers = await service.list()
describe("listAuthIdentities", () => {
it("should list authIdentities", async () => {
const authIdentities = await service.list()
expect(authUsers).toEqual([
expect(authIdentities).toEqual([
expect.objectContaining({
provider: "store",
}),
@@ -33,24 +33,24 @@ moduleIntegrationTestRunner({
])
})
it("should list authUsers by id", async () => {
const authUsers = await service.list({
it("should list authIdentities by id", async () => {
const authIdentities = await service.list({
id: ["test-id"],
})
expect(authUsers).toEqual([
expect(authIdentities).toEqual([
expect.objectContaining({
id: "test-id",
}),
])
})
it("should list authUsers by provider", async () => {
const authUsers = await service.list({
it("should list authIdentities by provider", async () => {
const authIdentities = await service.list({
provider: "manual",
})
expect(authUsers).toEqual([
expect(authIdentities).toEqual([
expect.objectContaining({
id: "test-id",
}),
@@ -61,12 +61,12 @@ moduleIntegrationTestRunner({
})
})
describe("listAndCountAuthUsers", () => {
it("should list and count authUsers", async () => {
const [authUsers, count] = await service.listAndCount()
describe("listAndCountAuthIdentities", () => {
it("should list and count authIdentities", async () => {
const [authIdentities, count] = await service.listAndCount()
expect(count).toEqual(3)
expect(authUsers).toEqual([
expect(authIdentities).toEqual([
expect.objectContaining({
provider: "store",
}),
@@ -79,13 +79,13 @@ moduleIntegrationTestRunner({
])
})
it("should listAndCount authUsers by provider_id", async () => {
const [authUsers, count] = await service.listAndCount({
it("should listAndCount authIdentities by provider_id", async () => {
const [authIdentities, count] = await service.listAndCount({
provider: "manual",
})
expect(count).toEqual(2)
expect(authUsers).toEqual([
expect(authIdentities).toEqual([
expect.objectContaining({
id: "test-id",
}),
@@ -96,20 +96,20 @@ moduleIntegrationTestRunner({
})
})
describe("retrieveAuthUser", () => {
describe("retrieveAuthIdentity", () => {
const id = "test-id"
it("should return an authUser for the given id", async () => {
const authUser = await service.retrieve(id)
it("should return an authIdentity for the given id", async () => {
const authIdentity = await service.retrieve(id)
expect(authUser).toEqual(
expect(authIdentity).toEqual(
expect.objectContaining({
id,
})
)
})
it("should throw an error when an authUser with the given id does not exist", async () => {
it("should throw an error when an authIdentity with the given id does not exist", async () => {
let error
try {
@@ -119,22 +119,22 @@ moduleIntegrationTestRunner({
}
expect(error.message).toEqual(
"AuthUser with id: does-not-exist was not found"
"AuthIdentity with id: does-not-exist was not found"
)
})
it("should not return an authUser with password hash", async () => {
const authUser = await service.retrieve("test-id-1")
it("should not return an authIdentity with password hash", async () => {
const authIdentity = await service.retrieve("test-id-1")
expect(authUser).toEqual(
expect(authIdentity).toEqual(
expect.objectContaining({
id: "test-id-1",
})
)
expect(authUser["password_hash"]).toEqual(undefined)
expect(authIdentity["password_hash"]).toEqual(undefined)
})
it("should throw an error when a authUserId is not provided", async () => {
it("should throw an error when a authIdentityId is not provided", async () => {
let error
try {
@@ -143,35 +143,35 @@ moduleIntegrationTestRunner({
error = e
}
expect(error.message).toEqual("authUser - id must be defined")
expect(error.message).toEqual("authIdentity - id must be defined")
})
it("should return authUser based on config select param", async () => {
const authUser = await service.retrieve(id, {
it("should return authIdentity based on config select param", async () => {
const authIdentity = await service.retrieve(id, {
select: ["id"],
})
expect(authUser).toEqual({
expect(authIdentity).toEqual({
id,
})
})
})
describe("deleteAuthUser", () => {
describe("deleteAuthIdentity", () => {
const id = "test-id"
it("should delete the authUsers given an id successfully", async () => {
it("should delete the authIdentities given an id successfully", async () => {
await service.delete([id])
const authUsers = await service.list({
const authIdentities = await service.list({
id: [id],
})
expect(authUsers).toHaveLength(0)
expect(authIdentities).toHaveLength(0)
})
})
describe("updateAuthUser", () => {
describe("updateAuthIdentity", () => {
const id = "test-id"
it("should throw an error when a id does not exist", async () => {
@@ -188,11 +188,11 @@ moduleIntegrationTestRunner({
}
expect(error.message).toEqual(
'AuthUser with id "does-not-exist" not found'
'AuthIdentity with id "does-not-exist" not found'
)
})
it("should update authUser", async () => {
it("should update authIdentity", async () => {
await service.update([
{
id,
@@ -200,8 +200,8 @@ moduleIntegrationTestRunner({
},
])
const [authUser] = await service.list({ id: [id] })
expect(authUser).toEqual(
const [authIdentity] = await service.list({ id: [id] })
expect(authIdentity).toEqual(
expect.objectContaining({
provider_metadata: { email: "test@email.com" },
})
@@ -209,8 +209,8 @@ moduleIntegrationTestRunner({
})
})
describe("createAuthUser", () => {
it("should create a authUser successfully", async () => {
describe("createAuthIdentity", () => {
it("should create a authIdentity successfully", async () => {
await service.create([
{
id: "test",
@@ -220,12 +220,12 @@ moduleIntegrationTestRunner({
},
])
const [authUser, count] = await service.listAndCount({
const [authIdentity, count] = await service.listAndCount({
id: ["test"],
})
expect(count).toEqual(1)
expect(authUser[0]).toEqual(
expect(authIdentity[0]).toEqual(
expect.objectContaining({
id: "test",
})

View File

@@ -1,4 +1,4 @@
import { MedusaModule, Modules } from "@medusajs/modules-sdk"
import { Modules } from "@medusajs/modules-sdk"
import { IAuthModuleService } from "@medusajs/types"
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"

View File

@@ -2,12 +2,12 @@ import { MedusaModule, Modules } from "@medusajs/modules-sdk"
import { IAuthModuleService } from "@medusajs/types"
import Scrypt from "scrypt-kdf"
import { createAuthUsers } from "../../../__fixtures__/auth-user"
import { createAuthIdentities } from "../../../__fixtures__/auth-identity"
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
jest.setTimeout(30000)
const seedDefaultData = async (manager) => {
await createAuthUsers(manager)
await createAuthIdentities(manager)
}
moduleIntegrationTestRunner({
@@ -37,7 +37,7 @@ moduleIntegrationTestRunner({
).toString("base64")
await seedDefaultData(MikroOrmWrapper.forkManager())
await createAuthUsers(MikroOrmWrapper.forkManager(), [
await createAuthIdentities(MikroOrmWrapper.forkManager(), [
// Add authenticated user
{
provider: "emailpass",
@@ -59,7 +59,7 @@ moduleIntegrationTestRunner({
expect(res).toEqual({
success: true,
authUser: expect.objectContaining({
authIdentity: expect.objectContaining({
entity_id: email,
provider_metadata: {},
}),
@@ -102,7 +102,7 @@ moduleIntegrationTestRunner({
).toString("base64")
await seedDefaultData(MikroOrmWrapper.forkManager())
await createAuthUsers(MikroOrmWrapper.forkManager(), [
await createAuthIdentities(MikroOrmWrapper.forkManager(), [
// Add authenticated user
{
provider: "emailpass",

View File

@@ -1,10 +1,10 @@
import { AuthUser } from "@models"
import { AuthIdentity } 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,
auth_identity_id: AuthIdentity.name,
}
const entityLinkableKeysMap: MapToConfig = {}
@@ -23,9 +23,9 @@ export const joinerConfig: ModuleJoinerConfig = {
primaryKeys: ["id"],
linkableKeys: LinkableKeys,
alias: {
name: ["auth_user", "auth_users"],
name: ["auth_identity", "auth_identities"],
args: {
entity: AuthUser.name,
entity: AuthIdentity.name,
},
},
}

View File

@@ -1,7 +1,5 @@
{
"namespaces": [
"public"
],
"namespaces": ["public"],
"name": "public",
"tables": [
{
@@ -70,25 +68,19 @@
"mappedType": "json"
}
},
"name": "auth_user",
"name": "auth_identity",
"schema": "public",
"indexes": [
{
"keyName": "IDX_auth_user_provider_scope_entity_id",
"columnNames": [
"provider",
"scope",
"entity_id"
],
"keyName": "IDX_auth_identity_provider_scope_entity_id",
"columnNames": ["provider", "scope", "entity_id"],
"composite": true,
"primary": false,
"unique": true
},
{
"keyName": "auth_user_pkey",
"columnNames": [
"id"
],
"keyName": "auth_identity_pkey",
"columnNames": ["id"],
"composite": false,
"primary": true,
"unique": true

View File

@@ -3,14 +3,14 @@ import { Migration } from "@mikro-orm/migrations"
export class Migration20240205025924 extends Migration {
async up(): Promise<void> {
this.addSql(
'create table if not exists "auth_user" ("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_user_pkey" primary key ("id"));'
'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_user" add constraint "IDX_auth_user_provider_scope_entity_id" unique ("provider", "scope", "entity_id");'
'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_user" cascade;')
this.addSql('drop table if exists "auth_identity" cascade;')
}
}

View File

@@ -15,9 +15,9 @@ type OptionalFields = "provider_metadata" | "app_metadata" | "user_metadata"
@Entity()
@Unique({
properties: ["provider", "scope", "entity_id"],
name: "IDX_auth_user_provider_scope_entity_id",
name: "IDX_auth_identity_provider_scope_entity_id",
})
export default class AuthUser {
export default class AuthIdentity {
[OptionalProps]: OptionalFields
@PrimaryKey({ columnType: "text" })
@@ -43,11 +43,11 @@ export default class AuthUser {
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "authusr")
this.id = generateEntityId(this.id, "authid")
}
@OnInit()
onInit() {
this.id = generateEntityId(this.id, "authusr")
this.id = generateEntityId(this.id, "authid")
}
}

View File

@@ -1 +1 @@
export { default as AuthUser } from "./auth-user"
export { default as AuthIdentity } from "./auth-identity"

View File

@@ -5,24 +5,26 @@ import {
isString,
} from "@medusajs/utils"
import { AuthUserService } from "@services"
import { AuthIdentityService } from "@services"
import Scrypt from "scrypt-kdf"
const EXPIRATION = "1d"
class EmailPasswordProvider extends AbstractAuthModuleProvider {
public static PROVIDER = "emailpass"
public static DISPLAY_NAME = "Email/Password Authentication"
protected readonly authUserSerivce_: AuthUserService
protected readonly authIdentitySerivce_: AuthIdentityService
constructor({ authUserService }: { authUserService: AuthUserService }) {
constructor({
authIdentityService,
}: {
authIdentityService: AuthIdentityService
}) {
super(arguments[0], {
provider: EmailPasswordProvider.PROVIDER,
displayName: EmailPasswordProvider.DISPLAY_NAME,
})
this.authUserSerivce_ = authUserService
this.authIdentitySerivce_ = authIdentityService
}
private getHashConfig() {
@@ -54,18 +56,19 @@ class EmailPasswordProvider extends AbstractAuthModuleProvider {
error: "Email should be a string",
}
}
let authUser
let authIdentity
try {
authUser = await this.authUserSerivce_.retrieveByProviderAndEntityId(
email,
EmailPasswordProvider.PROVIDER
)
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 [createdAuthUser] = await this.authUserSerivce_.create([
const [createdAuthIdentity] = await this.authIdentitySerivce_.create([
{
entity_id: email,
provider: EmailPasswordProvider.PROVIDER,
@@ -78,13 +81,13 @@ class EmailPasswordProvider extends AbstractAuthModuleProvider {
return {
success: true,
authUser: JSON.parse(JSON.stringify(createdAuthUser)),
authIdentity: JSON.parse(JSON.stringify(createdAuthIdentity)),
}
}
return { success: false, error: error.message }
}
const password_hash = authUser.provider_metadata?.password
const password_hash = authIdentity.provider_metadata?.password
if (isString(password_hash)) {
const buf = Buffer.from(password_hash as string, "base64")
@@ -92,9 +95,12 @@ class EmailPasswordProvider extends AbstractAuthModuleProvider {
const success = await Scrypt.verify(buf, password)
if (success) {
delete authUser.provider_metadata!.password
delete authIdentity.provider_metadata!.password
return { success, authUser: JSON.parse(JSON.stringify(authUser)) }
return {
success,
authIdentity: JSON.parse(JSON.stringify(authIdentity)),
}
}
}

View File

@@ -1,13 +1,13 @@
import { AuthenticationInput, AuthenticationResponse } from "@medusajs/types"
import { AbstractAuthModuleProvider, MedusaError } from "@medusajs/utils"
import { AuthUserService } from "@services"
import { AuthIdentityService } from "@services"
import jwt, { JwtPayload } from "jsonwebtoken"
import { AuthorizationCode } from "simple-oauth2"
import url from "url"
type InjectedDependencies = {
authUserService: AuthUserService
authIdentityService: AuthIdentityService
}
type ProviderConfig = {
@@ -21,15 +21,15 @@ class GoogleProvider extends AbstractAuthModuleProvider {
public static PROVIDER = "google"
public static DISPLAY_NAME = "Google Authentication"
protected readonly authUserService_: AuthUserService
protected readonly authIdentityService_: AuthIdentityService
constructor({ authUserService }: InjectedDependencies) {
constructor({ authIdentityService }: InjectedDependencies) {
super(arguments[0], {
provider: GoogleProvider.PROVIDER,
displayName: GoogleProvider.DISPLAY_NAME,
})
this.authUserService_ = authUserService
this.authIdentityService_ = authIdentityService
}
async authenticate(
@@ -83,16 +83,17 @@ class GoogleProvider extends AbstractAuthModuleProvider {
}) as JwtPayload
const entity_id = jwtData.payload.email
let authUser
let authIdentity
try {
authUser = await this.authUserService_.retrieveByProviderAndEntityId(
entity_id,
GoogleProvider.PROVIDER
)
authIdentity =
await this.authIdentityService_.retrieveByProviderAndEntityId(
entity_id,
GoogleProvider.PROVIDER
)
} catch (error) {
if (error.type === MedusaError.Types.NOT_FOUND) {
const [createdAuthUser] = await this.authUserService_.create([
const [createdAuthIdentity] = await this.authIdentityService_.create([
{
entity_id,
provider: GoogleProvider.PROVIDER,
@@ -100,7 +101,7 @@ class GoogleProvider extends AbstractAuthModuleProvider {
scope: this.scope_,
},
])
authUser = createdAuthUser
authIdentity = createdAuthIdentity
} else {
return { success: false, error: error.message }
}
@@ -108,7 +109,7 @@ class GoogleProvider extends AbstractAuthModuleProvider {
return {
success: true,
authUser,
authIdentity,
}
}
@@ -127,7 +128,7 @@ class GoogleProvider extends AbstractAuthModuleProvider {
try {
const accessToken = await client.getToken(tokenParams)
const { authUser, success } = await this.verify_(
const { authIdentity, success } = await this.verify_(
accessToken.token.id_token
)
@@ -135,7 +136,7 @@ class GoogleProvider extends AbstractAuthModuleProvider {
return {
success,
authUser,
authIdentity,
successRedirectUrl,
}
} catch (error) {

View File

@@ -1,19 +0,0 @@
#!/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 })
})()

View File

@@ -1,65 +0,0 @@
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)
}

View File

@@ -11,40 +11,42 @@ import {
MedusaError,
ModulesSdkUtils,
} from "@medusajs/utils"
import { AuthUser } from "@models"
import { AuthIdentity } from "@models"
type InjectedDependencies = {
baseRepository: DAL.RepositoryService
authUserRepository: DAL.RepositoryService
authIdentityRepository: DAL.RepositoryService
}
export default class AuthUserService<
TEntity extends AuthUser = AuthUser
export default class AuthIdentityService<
TEntity extends AuthIdentity = AuthIdentity
> extends ModulesSdkUtils.internalModuleServiceFactory<InjectedDependencies>(
AuthUser
AuthIdentity
)<TEntity> {
protected readonly authUserRepository_: RepositoryService<TEntity>
protected readonly authIdentityRepository_: RepositoryService<TEntity>
protected baseRepository_: DAL.RepositoryService
constructor(container: InjectedDependencies) {
// @ts-ignore
super(...arguments)
this.authUserRepository_ = container.authUserRepository
this.authIdentityRepository_ = container.authIdentityRepository
this.baseRepository_ = container.baseRepository
}
@InjectManager("authUserRepository_")
async retrieveByProviderAndEntityId<TEntityMethod = AuthTypes.AuthUserDTO>(
@InjectManager("authIdentityRepository_")
async retrieveByProviderAndEntityId<
TEntityMethod = AuthTypes.AuthIdentityDTO
>(
entityId: string,
provider: string,
config: FindConfig<TEntityMethod> = {},
@MedusaContext() sharedContext: Context = {}
): Promise<AuthTypes.AuthUserDTO> {
): Promise<AuthTypes.AuthIdentityDTO> {
const queryConfig = ModulesSdkUtils.buildQuery<TEntity>(
{ entity_id: entityId, provider },
{ ...config, take: 1 }
)
const [result] = await this.authUserRepository_.find(
const [result] = await this.authIdentityRepository_.find(
queryConfig,
sharedContext
)
@@ -52,10 +54,12 @@ export default class AuthUserService<
if (!result) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`AuthUser with entity_id: "${entityId}" and provider: "${provider}" not found`
`AuthIdentity with entity_id: "${entityId}" and provider: "${provider}" not found`
)
}
return await this.baseRepository_.serialize<AuthTypes.AuthUserDTO>(result)
return await this.baseRepository_.serialize<AuthTypes.AuthIdentityDTO>(
result
)
}
}

View File

@@ -2,17 +2,14 @@ import {
AuthenticationInput,
AuthenticationResponse,
AuthTypes,
AuthUserDTO,
Context,
CreateAuthUserDTO,
DAL,
InternalModuleDeclaration,
ModuleJoinerConfig,
ModulesSdkTypes,
UpdateAuthUserDTO,
} from "@medusajs/types"
import { AuthUser } from "@models"
import { AuthIdentity } from "@models"
import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config"
@@ -26,33 +23,35 @@ import {
type InjectedDependencies = {
baseRepository: DAL.RepositoryService
authUserService: ModulesSdkTypes.InternalModuleService<any>
authIdentityService: ModulesSdkTypes.InternalModuleService<any>
}
const generateMethodForModels = [AuthUser]
const generateMethodForModels = [AuthIdentity]
export default class AuthModuleService<TAuthUser extends AuthUser = AuthUser>
export default class AuthModuleService<
TAuthIdentity extends AuthIdentity = AuthIdentity
>
extends ModulesSdkUtils.abstractModuleServiceFactory<
InjectedDependencies,
AuthTypes.AuthUserDTO,
AuthTypes.AuthIdentityDTO,
{
AuthUser: { dto: AuthUserDTO }
AuthIdentity: { dto: AuthTypes.AuthIdentityDTO }
}
>(AuthUser, generateMethodForModels, entityNameToLinkableKeysMap)
>(AuthIdentity, generateMethodForModels, entityNameToLinkableKeysMap)
implements AuthTypes.IAuthModuleService
{
protected baseRepository_: DAL.RepositoryService
protected authUserService_: ModulesSdkTypes.InternalModuleService<TAuthUser>
protected authIdentityService_: ModulesSdkTypes.InternalModuleService<TAuthIdentity>
constructor(
{ authUserService, baseRepository }: InjectedDependencies,
{ authIdentityService, baseRepository }: InjectedDependencies,
protected readonly moduleDeclaration: InternalModuleDeclaration
) {
// @ts-ignore
super(...arguments)
this.baseRepository_ = baseRepository
this.authUserService_ = authUserService
this.authIdentityService_ = authIdentityService
}
__joinerConfig(): ModuleJoinerConfig {
@@ -60,21 +59,27 @@ export default class AuthModuleService<TAuthUser extends AuthUser = AuthUser>
}
create(
data: CreateAuthUserDTO[],
data: AuthTypes.CreateAuthIdentityDTO[],
sharedContext?: Context
): Promise<AuthUserDTO[]>
): Promise<AuthTypes.AuthIdentityDTO[]>
create(data: CreateAuthUserDTO, sharedContext?: Context): Promise<AuthUserDTO>
create(
data: AuthTypes.CreateAuthIdentityDTO,
sharedContext?: Context
): Promise<AuthTypes.AuthIdentityDTO>
@InjectManager("baseRepository_")
async create(
data: CreateAuthUserDTO[] | CreateAuthUserDTO,
data: AuthTypes.CreateAuthIdentityDTO[] | AuthTypes.CreateAuthIdentityDTO,
@MedusaContext() sharedContext: Context = {}
): Promise<AuthTypes.AuthUserDTO | AuthTypes.AuthUserDTO[]> {
const authUsers = await this.authUserService_.create(data, sharedContext)
): Promise<AuthTypes.AuthIdentityDTO | AuthTypes.AuthIdentityDTO[]> {
const authIdentities = await this.authIdentityService_.create(
data,
sharedContext
)
return await this.baseRepository_.serialize<AuthTypes.AuthUserDTO[]>(
authUsers,
return await this.baseRepository_.serialize<AuthTypes.AuthIdentityDTO[]>(
authIdentities,
{
populate: true,
}
@@ -82,22 +87,28 @@ export default class AuthModuleService<TAuthUser extends AuthUser = AuthUser>
}
update(
data: UpdateAuthUserDTO[],
data: AuthTypes.UpdateAuthIdentityDTO[],
sharedContext?: Context
): Promise<AuthUserDTO[]>
): Promise<AuthTypes.AuthIdentityDTO[]>
update(data: UpdateAuthUserDTO, sharedContext?: Context): Promise<AuthUserDTO>
update(
data: AuthTypes.UpdateAuthIdentityDTO,
sharedContext?: Context
): Promise<AuthTypes.AuthIdentityDTO>
// TODO: should be pluralized, see convention about the methods naming or the abstract module service interface definition @engineering
@InjectManager("baseRepository_")
async update(
data: UpdateAuthUserDTO | UpdateAuthUserDTO[],
data: AuthTypes.UpdateAuthIdentityDTO | AuthTypes.UpdateAuthIdentityDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<AuthTypes.AuthUserDTO | AuthTypes.AuthUserDTO[]> {
const updatedUsers = await this.authUserService_.update(data, sharedContext)
): Promise<AuthTypes.AuthIdentityDTO | AuthTypes.AuthIdentityDTO[]> {
const updatedUsers = await this.authIdentityService_.update(
data,
sharedContext
)
const serializedUsers = await this.baseRepository_.serialize<
AuthTypes.AuthUserDTO[]
AuthTypes.AuthIdentityDTO[]
>(updatedUsers, {
populate: true,
})

View File

@@ -1,2 +1,2 @@
export { default as AuthModuleService } from "./auth-module"
export { default as AuthUserService } from "./auth-user"
export { default as AuthIdentityService } from "./auth-identity"

View File

@@ -3,6 +3,3 @@ import { Logger } from "@medusajs/types"
export type InitializeModuleInjectableDependencies = {
logger?: Logger
}
export * as RepositoryTypes from "./repositories"
export * as ServiceTypes from "./services"

View File

@@ -1,19 +0,0 @@
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
}

View File

@@ -1 +0,0 @@
export * from "./auth-user"

View File

@@ -1,28 +0,0 @@
export type AuthUserDTO = {
id: string
provider_id: string
entity_id: string
scope: string
provider: string
provider_metadata?: Record<string, unknown>
user_metadata: Record<string, unknown>
app_metadata: Record<string, unknown>
}
export type CreateAuthUserDTO = {
entity_id: string
provider: string
scope: 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 = {}

View File

@@ -1 +0,0 @@
export * from "./auth-user"