Skip to content

Commit 715dbf7

Browse files
committed
refactor(oauth): remove redirect URI settings from system configuration
- Removed oauthGoogleRedirectUri and oauthGithubRedirectUri from SYSTEM_SETTING_DEFINITIONS and related service logic. - Updated SystemSettingService to eliminate handling of redirect URIs for Google and GitHub. - Adjusted UI schema and types to reflect the removal of redirect URI fields. - Cleaned up associated validation and parsing logic to streamline OAuth configuration. Signed-off-by: Innei <tukon479@gmail.com>
1 parent 66c5721 commit 715dbf7

File tree

9 files changed

+43
-135
lines changed

9 files changed

+43
-135
lines changed

be/apps/core/src/modules/configuration/system-setting/system-setting.constants.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,6 @@ export const SYSTEM_SETTING_DEFINITIONS = {
5858
defaultValue: null as string | null,
5959
isSensitive: true,
6060
},
61-
oauthGoogleRedirectUri: {
62-
key: 'system.auth.oauth.google.redirectUri',
63-
schema: nullableUrl,
64-
defaultValue: null as string | null,
65-
isSensitive: false,
66-
},
6761
oauthGithubClientId: {
6862
key: 'system.auth.oauth.github.clientId',
6963
schema: nullableNonEmptyString,
@@ -76,12 +70,6 @@ export const SYSTEM_SETTING_DEFINITIONS = {
7670
defaultValue: null as string | null,
7771
isSensitive: true,
7872
},
79-
oauthGithubRedirectUri: {
80-
key: 'system.auth.oauth.github.redirectUri',
81-
schema: nullableUrl,
82-
defaultValue: null as string | null,
83-
isSensitive: false,
84-
},
8573
} as const
8674

8775
export type SystemSettingField = keyof typeof SYSTEM_SETTING_DEFINITIONS

be/apps/core/src/modules/configuration/system-setting/system-setting.service.ts

Lines changed: 0 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,6 @@ export class SystemSettingService {
7171
SYSTEM_SETTING_DEFINITIONS.oauthGoogleClientSecret.schema,
7272
SYSTEM_SETTING_DEFINITIONS.oauthGoogleClientSecret.defaultValue,
7373
)
74-
const oauthGoogleRedirectUri = this.normalizeRedirectPath(
75-
this.parseSetting(
76-
rawValues[SYSTEM_SETTING_DEFINITIONS.oauthGoogleRedirectUri.key],
77-
SYSTEM_SETTING_DEFINITIONS.oauthGoogleRedirectUri.schema,
78-
SYSTEM_SETTING_DEFINITIONS.oauthGoogleRedirectUri.defaultValue,
79-
),
80-
)
81-
8274
const oauthGithubClientId = this.parseSetting(
8375
rawValues[SYSTEM_SETTING_DEFINITIONS.oauthGithubClientId.key],
8476
SYSTEM_SETTING_DEFINITIONS.oauthGithubClientId.schema,
@@ -89,14 +81,6 @@ export class SystemSettingService {
8981
SYSTEM_SETTING_DEFINITIONS.oauthGithubClientSecret.schema,
9082
SYSTEM_SETTING_DEFINITIONS.oauthGithubClientSecret.defaultValue,
9183
)
92-
const oauthGithubRedirectUri = this.normalizeRedirectPath(
93-
this.parseSetting(
94-
rawValues[SYSTEM_SETTING_DEFINITIONS.oauthGithubRedirectUri.key],
95-
SYSTEM_SETTING_DEFINITIONS.oauthGithubRedirectUri.schema,
96-
SYSTEM_SETTING_DEFINITIONS.oauthGithubRedirectUri.defaultValue,
97-
),
98-
)
99-
10084
return {
10185
allowRegistration,
10286
maxRegistrableUsers,
@@ -105,10 +89,8 @@ export class SystemSettingService {
10589
oauthGatewayUrl,
10690
oauthGoogleClientId,
10791
oauthGoogleClientSecret,
108-
oauthGoogleRedirectUri,
10992
oauthGithubClientId,
11093
oauthGithubClientSecret,
111-
oauthGithubRedirectUri,
11294
}
11395
}
11496

@@ -195,13 +177,6 @@ export class SystemSettingService {
195177
}
196178
}
197179

198-
if (patch.oauthGoogleRedirectUri !== undefined) {
199-
const sanitized = this.normalizeRedirectPath(patch.oauthGoogleRedirectUri)
200-
if (sanitized !== current.oauthGoogleRedirectUri) {
201-
enqueueUpdate('oauthGoogleRedirectUri', sanitized)
202-
}
203-
}
204-
205180
if (patch.oauthGithubClientId !== undefined) {
206181
const sanitized = this.normalizeNullableString(patch.oauthGithubClientId)
207182
if (sanitized !== current.oauthGithubClientId) {
@@ -216,13 +191,6 @@ export class SystemSettingService {
216191
}
217192
}
218193

219-
if (patch.oauthGithubRedirectUri !== undefined) {
220-
const sanitized = this.normalizeRedirectPath(patch.oauthGithubRedirectUri)
221-
if (sanitized !== current.oauthGithubRedirectUri) {
222-
enqueueUpdate('oauthGithubRedirectUri', sanitized)
223-
}
224-
}
225-
226194
if (updates.length === 0) {
227195
return current
228196
}
@@ -291,15 +259,13 @@ export class SystemSettingService {
291259
providers.google = {
292260
clientId: settings.oauthGoogleClientId,
293261
clientSecret: settings.oauthGoogleClientSecret,
294-
redirectPath: settings.oauthGoogleRedirectUri,
295262
}
296263
}
297264

298265
if (settings.oauthGithubClientId && settings.oauthGithubClientSecret) {
299266
providers.github = {
300267
clientId: settings.oauthGithubClientId,
301268
clientSecret: settings.oauthGithubClientSecret,
302-
redirectPath: settings.oauthGithubRedirectUri,
303269
}
304270
}
305271

@@ -337,36 +303,6 @@ export class SystemSettingService {
337303
}
338304
}
339305

340-
private normalizeRedirectPath(value: string | null | undefined): string | null {
341-
if (value === undefined || value === null) {
342-
return null
343-
}
344-
345-
const trimmed = value.trim()
346-
if (trimmed.length === 0) {
347-
return null
348-
}
349-
350-
const ensureLeadingSlash = (input: string): string | null => {
351-
if (!input.startsWith('/')) {
352-
return null
353-
}
354-
return input
355-
}
356-
357-
try {
358-
if (trimmed.startsWith('http://') || trimmed.startsWith('https://')) {
359-
const url = new URL(trimmed)
360-
const pathWithQuery = `${url.pathname}${url.search ?? ''}`
361-
return ensureLeadingSlash(pathWithQuery) ?? null
362-
}
363-
} catch {
364-
// fall through to path handling
365-
}
366-
367-
return ensureLeadingSlash(trimmed)
368-
}
369-
370306
private buildStats(settings: SystemSettings, totalUsers: number): SystemSettingStats {
371307
const remaining =
372308
settings.maxRegistrableUsers === null ? null : Math.max(settings.maxRegistrableUsers - totalUsers, 0)

be/apps/core/src/modules/configuration/system-setting/system-setting.types.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,8 @@ export interface SystemSettings {
1010
oauthGatewayUrl: string | null
1111
oauthGoogleClientId: string | null
1212
oauthGoogleClientSecret: string | null
13-
oauthGoogleRedirectUri: string | null
1413
oauthGithubClientId: string | null
1514
oauthGithubClientSecret: string | null
16-
oauthGithubRedirectUri: string | null
1715
}
1816

1917
export type SystemSettingValueMap = {

be/apps/core/src/modules/configuration/system-setting/system-setting.ui-schema.ts

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { UiNode, UiSchema } from 'core/modules/ui/ui-schema/ui-schema.type'
22

33
import type { SystemSettingField } from './system-setting.constants'
44

5-
export const SYSTEM_SETTING_UI_SCHEMA_VERSION = '1.2.0'
5+
export const SYSTEM_SETTING_UI_SCHEMA_VERSION = '1.3.0'
66

77
export const SYSTEM_SETTING_UI_SCHEMA: UiSchema<SystemSettingField> = {
88
version: SYSTEM_SETTING_UI_SCHEMA_VERSION,
@@ -74,7 +74,7 @@ export const SYSTEM_SETTING_UI_SCHEMA: UiSchema<SystemSettingField> = {
7474
type: 'field',
7575
id: 'oauth-gateway-url',
7676
title: 'OAuth 网关地址',
77-
description: '统一的 OAuth 回调入口,例如 https://auth.afilmory.art。留空则直接回调到租户域名。',
77+
description: '所有第三方登录统一走该回调入口(例如 https://auth.afilmory.art)。留空则回退到租户域名。',
7878
helperText: '必须包含 http/https 协议,结尾无需斜杠。',
7979
key: 'oauthGatewayUrl',
8080
component: {
@@ -113,17 +113,6 @@ export const SYSTEM_SETTING_UI_SCHEMA: UiSchema<SystemSettingField> = {
113113
autoComplete: 'off',
114114
},
115115
},
116-
{
117-
type: 'field',
118-
id: 'oauth-google-redirect-uri',
119-
title: 'Redirect URI',
120-
description: 'OAuth 回调路径,域名会自动使用当前租户如 slug.主域名。',
121-
key: 'oauthGoogleRedirectUri',
122-
component: {
123-
type: 'text',
124-
placeholder: '/api/auth/callback/google',
125-
},
126-
},
127116
],
128117
},
129118
{
@@ -157,17 +146,6 @@ export const SYSTEM_SETTING_UI_SCHEMA: UiSchema<SystemSettingField> = {
157146
autoComplete: 'off',
158147
},
159148
},
160-
{
161-
type: 'field',
162-
id: 'oauth-github-redirect-uri',
163-
title: 'Redirect URI',
164-
description: 'GitHub 回调路径,域名会自动使用租户的 subdomain。',
165-
key: 'oauthGithubRedirectUri',
166-
component: {
167-
type: 'text',
168-
placeholder: '/api/auth/callback/github',
169-
},
170-
},
171149
],
172150
},
173151
],

be/apps/core/src/modules/platform/auth/auth.config.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { injectable } from 'tsyringe'
44
export interface SocialProviderOptions {
55
clientId: string
66
clientSecret: string
7-
redirectPath?: string | null
87
}
98

109
export interface SocialProvidersConfig {

be/apps/core/src/modules/platform/auth/auth.controller.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,13 @@ function resolveSocialProviderMetadata(id: string): { name: string; icon: string
4343
function buildProviderResponse(socialProviders: SocialProvidersConfig) {
4444
return Object.entries(socialProviders)
4545
.filter(([, config]) => Boolean(config))
46-
.map(([id, config]) => {
46+
.map(([id]) => {
4747
const metadata = resolveSocialProviderMetadata(id)
4848
return {
4949
id,
5050
name: metadata.name,
5151
icon: metadata.icon,
52-
callbackPath: config?.redirectPath ?? null,
52+
callbackPath: `/api/auth/callback/${id}`,
5353
}
5454
})
5555
}
@@ -120,7 +120,7 @@ export class AuthController {
120120
}
121121

122122
if (!tenantContext || isPlaceholderTenantContext(tenantContext)) {
123-
const {tenantId} = (authContext.user as { tenantId?: string | null })
123+
const { tenantId } = authContext.user as { tenantId?: string | null }
124124
if (tenantId) {
125125
try {
126126
const aggregate = await this.tenantService.getById(tenantId)

be/apps/core/src/modules/platform/auth/auth.provider.ts

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { createHash } from 'node:crypto'
2+
13
import { authAccounts, authSessions, authUsers, authVerifications, generateId } from '@afilmory/db'
24
import type { OnModuleInit } from '@afilmory/framework'
35
import { createLogger, HttpContext } from '@afilmory/framework'
@@ -21,7 +23,6 @@ const logger = createLogger('Auth')
2123

2224
@injectable()
2325
export class AuthProvider implements OnModuleInit {
24-
private moduleOptionsPromise?: Promise<AuthModuleOptions>
2526
private instances = new Map<string, Promise<BetterAuthInstance>>()
2627
private placeholderTenantId: string | null = null
2728

@@ -33,7 +34,7 @@ export class AuthProvider implements OnModuleInit {
3334
) {}
3435

3536
async onModuleInit(): Promise<void> {
36-
await this.getAuth()
37+
await this.config.getOptions()
3738
}
3839

3940
private resolveTenantIdFromContext(): string | null {
@@ -71,13 +72,6 @@ export class AuthProvider implements OnModuleInit {
7172
return sanitizedSlug ? `better-auth-${sanitizedSlug}` : 'better-auth'
7273
}
7374

74-
private async getModuleOptions(): Promise<AuthModuleOptions> {
75-
if (!this.moduleOptionsPromise) {
76-
this.moduleOptionsPromise = this.config.getOptions()
77-
}
78-
return this.moduleOptionsPromise
79-
}
80-
8175
private async resolveFallbackTenantId(): Promise<string | null> {
8276
if (this.placeholderTenantId) {
8377
return this.placeholderTenantId
@@ -152,7 +146,7 @@ export class AuthProvider implements OnModuleInit {
152146

153147
return entries.reduce<Record<string, { clientId: string; clientSecret: string; redirectURI?: string }>>(
154148
(acc, [key, value]) => {
155-
const redirectUri = this.buildRedirectUri(tenantSlug, key, value, oauthGatewayUrl)
149+
const redirectUri = this.buildRedirectUri(tenantSlug, key, oauthGatewayUrl)
156150
acc[key] = {
157151
clientId: value.clientId,
158152
clientSecret: value.clientSecret,
@@ -167,13 +161,10 @@ export class AuthProvider implements OnModuleInit {
167161
private buildRedirectUri(
168162
tenantSlug: string | null,
169163
provider: keyof SocialProvidersConfig,
170-
options: SocialProviderOptions,
171164
oauthGatewayUrl: string | null,
172165
): string | null {
173-
const basePath = options.redirectPath ?? `/api/auth/callback/${provider}`
174-
if (!basePath.startsWith('/')) {
175-
return null
176-
}
166+
const basePath = `/api/auth/callback/${provider}`
167+
177168
if (oauthGatewayUrl) {
178169
return this.buildGatewayRedirectUri(oauthGatewayUrl, basePath, tenantSlug)
179170
}
@@ -193,8 +184,10 @@ export class AuthProvider implements OnModuleInit {
193184
return `${normalizedBase}${basePath}${query ? `?${query}` : ''}`
194185
}
195186

196-
private async createAuthForEndpoint(tenantSlug: string | null): Promise<BetterAuthInstance> {
197-
const options = await this.getModuleOptions()
187+
private async createAuthForEndpoint(
188+
tenantSlug: string | null,
189+
options: AuthModuleOptions,
190+
): Promise<BetterAuthInstance> {
198191
const db = this.drizzleProvider.getDb()
199192
const socialProviders = this.buildBetterAuthProvidersForHost(
200193
tenantSlug,
@@ -323,18 +316,19 @@ export class AuthProvider implements OnModuleInit {
323316
}
324317

325318
async getAuth(): Promise<BetterAuthInstance> {
326-
const options = await this.getModuleOptions()
319+
const options = await this.config.getOptions()
327320
const endpoint = this.resolveRequestEndpoint()
328321
const fallbackHost = options.baseDomain.trim().toLowerCase()
329322
const requestedHost = (endpoint.host ?? fallbackHost).trim().toLowerCase()
330323
const tenantSlug = this.resolveTenantSlugFromContext()
331324
const host = this.applyTenantSlugToHost(requestedHost || fallbackHost, fallbackHost, tenantSlug)
332325
const protocol = this.determineProtocol(host, endpoint.protocol)
333326
const slugKey = tenantSlug ?? 'global'
334-
const cacheKey = `${protocol}://${host}::${slugKey}`
327+
const optionSignature = this.computeOptionsSignature(options)
328+
const cacheKey = `${protocol}://${host}::${slugKey}::${optionSignature}`
335329

336330
if (!this.instances.has(cacheKey)) {
337-
const instancePromise = this.createAuthForEndpoint(tenantSlug).then((instance) => {
331+
const instancePromise = this.createAuthForEndpoint(tenantSlug, options).then((instance) => {
338332
logger.info(`Better Auth initialized for ${cacheKey}`)
339333
return instance
340334
})
@@ -344,6 +338,29 @@ export class AuthProvider implements OnModuleInit {
344338
return await this.instances.get(cacheKey)!
345339
}
346340

341+
private computeOptionsSignature(options: AuthModuleOptions): string {
342+
const hash = createHash('sha256')
343+
hash.update(options.baseDomain)
344+
hash.update('|gateway=')
345+
hash.update(options.oauthGatewayUrl ?? 'null')
346+
347+
const providerEntries = Object.entries(options.socialProviders)
348+
.sort(([a], [b]) => a.localeCompare(b))
349+
.map(([provider, config]) => {
350+
const secretHash = config?.clientSecret
351+
? createHash('sha256').update(config.clientSecret).digest('hex')
352+
: 'null'
353+
return {
354+
provider,
355+
clientId: config?.clientId ?? '',
356+
secretHash,
357+
}
358+
})
359+
360+
hash.update(JSON.stringify(providerEntries))
361+
return hash.digest('hex')
362+
}
363+
347364
async handler(context: Context): Promise<Response> {
348365
const auth = await this.getAuth()
349366
return auth.handler(context.req.raw)

be/apps/core/src/modules/platform/super-admin/super-admin.dto.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
11
import { createZodDto } from '@afilmory/framework'
22
import { z } from 'zod'
33

4-
const redirectPathInputSchema = z
5-
.string()
6-
.trim()
7-
.refine((value) => value.length === 0 || value.startsWith('/'), {
8-
message: '路径必须以 / 开头',
9-
})
10-
114
const updateSuperAdminSettingsSchema = z
125
.object({
136
allowRegistration: z.boolean().optional(),
@@ -30,10 +23,8 @@ const updateSuperAdminSettingsSchema = z
3023
.optional(),
3124
oauthGoogleClientId: z.string().trim().min(1).nullable().optional(),
3225
oauthGoogleClientSecret: z.string().trim().min(1).nullable().optional(),
33-
oauthGoogleRedirectUri: redirectPathInputSchema.nullable().optional(),
3426
oauthGithubClientId: z.string().trim().min(1).nullable().optional(),
3527
oauthGithubClientSecret: z.string().trim().min(1).nullable().optional(),
36-
oauthGithubRedirectUri: redirectPathInputSchema.nullable().optional(),
3728
})
3829
.refine((value) => Object.values(value).some((entry) => entry !== undefined), {
3930
message: '至少需要更新一项设置',

0 commit comments

Comments
 (0)