fix: root account creation

- Introduced an explicit tenant ID parameter in the AuthProvider to allow for tenant-specific authentication.
- Updated the createAuthForEndpoint method to utilize the explicit tenant ID when provided.
- Added a new method, getAuthForTenant, to facilitate fetching authentication for a specific tenant.
- Updated related services to use the new tenant-specific authentication logic.
- Added pg-native as a dependency and updated pnpm-lock.yaml accordingly.

Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
Innei
2025-12-05 20:32:47 +08:00
parent 9b1a1b7bf6
commit b8eeb95c4f
5 changed files with 26 additions and 8 deletions

View File

@@ -158,6 +158,7 @@ export class AuthProvider implements OnModuleInit {
private async createAuthForEndpoint(
tenantSlug: string | null,
options: AuthModuleOptions,
explicitTenantId?: string | null,
): Promise<BetterAuthInstance> {
const db = this.drizzleProvider.getDb()
const socialProviders = this.buildBetterAuthProvidersForHost(options.socialProviders, options.oauthGatewayUrl)
@@ -166,7 +167,12 @@ export class AuthProvider implements OnModuleInit {
// This ensures that user lookups (by email) and account lookups (by provider)
// are scoped to the current tenant, allowing the same email/social account
// to exist as different users in different tenants
const ensureTenantId = async () => await this.resolveTenantIdOrProvision(tenantSlug)
const ensureTenantId = async () => {
if (explicitTenantId) {
return explicitTenantId
}
return await this.resolveTenantIdOrProvision(tenantSlug)
}
return betterAuth({
database: tenantAwareDrizzleAdapter(
@@ -209,7 +215,7 @@ export class AuthProvider implements OnModuleInit {
user: {
create: {
before: async (user) => {
const tenantId = await ensureTenantId()
const tenantId = explicitTenantId ?? (await ensureTenantId())
if (!tenantId) {
throw new APIError('BAD_REQUEST', {
message: 'Missing tenant context during account creation.',
@@ -229,7 +235,7 @@ export class AuthProvider implements OnModuleInit {
session: {
create: {
before: async (session) => {
const tenantId = this.resolveTenantIdFromContext()
const tenantId = explicitTenantId ?? this.resolveTenantIdFromContext()
const fallbackTenantId = tenantId ?? session.tenantId ?? (await ensureTenantId())
return {
data: {
@@ -243,7 +249,7 @@ export class AuthProvider implements OnModuleInit {
account: {
create: {
before: async (account) => {
const tenantId = this.resolveTenantIdFromContext()
const tenantId = explicitTenantId ?? this.resolveTenantIdFromContext()
const resolvedTenantId = tenantId ?? (await ensureTenantId())
if (!resolvedTenantId) {
return { data: account }
@@ -334,6 +340,12 @@ export class AuthProvider implements OnModuleInit {
return await instancePromise
}
async getAuthForTenant(tenant: { id: string; slug?: string | null }): Promise<BetterAuthInstance> {
const options = await this.config.getOptions()
const tenantSlug = tenant.slug ?? null
return await this.createAuthForEndpoint(tenantSlug, options, tenant.id)
}
private computeOptionsSignature(options: AuthModuleOptions): string {
const hash = createHash('sha256')
hash.update(options.baseDomain)

View File

@@ -5,6 +5,7 @@ import { env } from '@afilmory/env'
import { createLogger } from '@afilmory/framework'
import { DbAccessor } from 'core/database/database.provider'
import { STATIC_DASHBOARD_BASENAME } from 'core/modules/infrastructure/static-web/static-dashboard.service'
import { ROOT_TENANT_SLUG } from 'core/modules/platform/tenant/tenant.constants'
import { eq } from 'drizzle-orm'
import { injectable } from 'tsyringe'
@@ -59,7 +60,7 @@ export class RootAccountProvisioner {
const password = randomBytes(16).toString('base64url')
try {
const auth = await this.authProvider.getAuth()
const auth = await this.authProvider.getAuthForTenant({ id: rootTenantId, slug: ROOT_TENANT_SLUG })
const result = await auth.api.signUpEmail({
body: {
email,

View File

@@ -19,6 +19,7 @@
"drizzle-orm": "^0.45.0",
"nodejs-snowflake": "^2.0.1",
"pg": "^8.16.3",
"pg-native": "3.5.2",
"zod": "^4.1.13"
},
"devDependencies": {

8
pnpm-lock.yaml generated
View File

@@ -1325,6 +1325,9 @@ importers:
pg:
specifier: ^8.16.3
version: 8.16.3(pg-native@3.5.2)
pg-native:
specifier: 3.5.2
version: 3.5.2
zod:
specifier: ^4.1.13
version: 4.1.13
@@ -1617,7 +1620,7 @@ importers:
version: 0.17.0(synckit@0.11.11)(typescript@5.9.3)
unplugin-dts:
specifier: 1.0.0-beta.6
version: 1.0.0-beta.6(@microsoft/api-extractor@7.52.13(@types/node@24.10.1))(esbuild@0.25.12)(rolldown@1.0.0-beta.53)(rollup@4.53.3)(typescript@5.9.3)(vite@8.0.0-beta.0(@types/node@24.10.1)(esbuild@0.25.12)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1))
version: 1.0.0-beta.6(@microsoft/api-extractor@7.52.13(@types/node@24.10.1))(esbuild@0.25.12)(rollup@4.53.3)(typescript@5.9.3)(vite@8.0.0-beta.0(@types/node@24.10.1)(esbuild@0.25.12)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1))
vite:
specifier: 8.0.0-beta.0
version: 8.0.0-beta.0(@types/node@24.10.1)(esbuild@0.25.12)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1)
@@ -24679,7 +24682,7 @@ snapshots:
magic-string-ast: 1.0.3
unplugin: 2.3.11
unplugin-dts@1.0.0-beta.6(@microsoft/api-extractor@7.52.13(@types/node@24.10.1))(esbuild@0.25.12)(rolldown@1.0.0-beta.53)(rollup@4.53.3)(typescript@5.9.3)(vite@8.0.0-beta.0(@types/node@24.10.1)(esbuild@0.25.12)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1)):
unplugin-dts@1.0.0-beta.6(@microsoft/api-extractor@7.52.13(@types/node@24.10.1))(esbuild@0.25.12)(rollup@4.53.3)(typescript@5.9.3)(vite@8.0.0-beta.0(@types/node@24.10.1)(esbuild@0.25.12)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1)):
dependencies:
'@rollup/pluginutils': 5.3.0(rollup@4.53.3)
'@volar/typescript': 2.4.23
@@ -24693,7 +24696,6 @@ snapshots:
optionalDependencies:
'@microsoft/api-extractor': 7.52.13(@types/node@24.10.1)
esbuild: 0.25.12
rolldown: 1.0.0-beta.53
rollup: 4.53.3
vite: 8.0.0-beta.0(@types/node@24.10.1)(esbuild@0.25.12)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.1)
transitivePeerDependencies:

View File

@@ -24,4 +24,6 @@ catalog:
onlyBuiltDependencies:
- esbuild
- libpq
- puppeteer
- simple-git-hooks