chore(server): drop api-host branch in OAuth discovery (#19768)
## Summary Follow-up to #19755. Simplifies `OAuthDiscoveryController` by dropping the `authorization_endpoint → frontend base URL` branch that was there to make `api.twenty.com/mcp` paste-able in MCP clients. We've decided not to support pasting `api.twenty.com/mcp` — users can paste `app.twenty.com/mcp`, `<workspace>.twenty.com/mcp`, or a custom domain, all of which serve both frontend and API. On those hosts, `authorization_endpoint` was already pointed at the same host as `issuer`, which is what we want. ## Change - Remove `isApiHost` helper and the `authorizeBase` branch — use `issuer` for `authorization_endpoint`. - Drop now-unused `TwentyConfigService` and `DomainServerConfigService` injections. - Drop duplicate `DomainServerConfigModule` import from `application-oauth.module.ts` (the module is no longer needed). Net diff: +1 / -22 across 2 files. ## Breaking change MCP clients configured with `https://api.twenty.com/mcp` will stop working. They should be reconfigured with the host matching the workspace they're connecting to (`<workspace>.twenty.com/mcp`, `app.twenty.com/mcp`, or a custom domain). ## Test plan - [x] `yarn jest --testPathPatterns="mcp-auth.guard"` → 2/2 passing (unchanged) - [x] `tsc --noEmit` clean on modified files - [ ] Manual verification on staging: `app.twenty.com/mcp` and `<workspace>.twenty.com/mcp` OAuth flow still works end-to-end Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
-3
@@ -19,7 +19,6 @@ import { TwentyConfigModule } from 'src/engine/core-modules/twenty-config/twenty
|
||||
import { UserWorkspaceEntity } from 'src/engine/core-modules/user-workspace/user-workspace.entity';
|
||||
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||
import { DomainServerConfigModule } from 'src/engine/core-modules/domain/domain-server-config/domain-server-config.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -33,13 +32,11 @@ import { DomainServerConfigModule } from 'src/engine/core-modules/domain/domain-
|
||||
ApplicationCoreModule,
|
||||
ApplicationInstallModule,
|
||||
TokenModule,
|
||||
DomainServerConfigModule,
|
||||
FeatureFlagModule,
|
||||
PermissionsModule,
|
||||
ThrottlerModule,
|
||||
TwentyConfigModule,
|
||||
WorkspaceCacheStorageModule,
|
||||
DomainServerConfigModule,
|
||||
],
|
||||
controllers: [
|
||||
OAuthTokenController,
|
||||
|
||||
+1
-19
@@ -4,18 +4,13 @@ import { type Request } from 'express';
|
||||
|
||||
import { ALL_OAUTH_SCOPES } from 'src/engine/core-modules/application/application-oauth/constants/oauth-scopes';
|
||||
import { ApplicationRegistrationService } from 'src/engine/core-modules/application/application-registration/application-registration.service';
|
||||
import { DomainServerConfigService } from 'src/engine/core-modules/domain/domain-server-config/services/domain-server-config.service';
|
||||
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
|
||||
import { NoPermissionGuard } from 'src/engine/guards/no-permission.guard';
|
||||
import { PublicEndpointGuard } from 'src/engine/guards/public-endpoint.guard';
|
||||
import { cleanServerUrl } from 'src/utils/clean-server-url';
|
||||
import { TWENTY_CLI_APPLICATION_REGISTRATION } from 'src/engine/workspace-manager/twenty-standard-application/constants/twenty-cli-application-registration.constant';
|
||||
|
||||
@Controller('.well-known')
|
||||
export class OAuthDiscoveryController {
|
||||
constructor(
|
||||
private readonly twentyConfigService: TwentyConfigService,
|
||||
private readonly domainServerConfigService: DomainServerConfigService,
|
||||
private readonly applicationRegistrationService: ApplicationRegistrationService,
|
||||
) {}
|
||||
|
||||
@@ -23,13 +18,6 @@ export class OAuthDiscoveryController {
|
||||
@UseGuards(PublicEndpointGuard, NoPermissionGuard)
|
||||
async getAuthorizationServerMetadata(@Req() request: Request) {
|
||||
const issuer = this.getRequestBaseUrl(request);
|
||||
// /authorize is served by the frontend; SERVER_URL (API-only) has no such
|
||||
// route, so we route the client to the default frontend base URL in that
|
||||
// case. All other hosts (app.twenty.com, workspace subdomains, custom
|
||||
// domains) serve both frontend and API.
|
||||
const authorizeBase = this.isApiHost(request)
|
||||
? cleanServerUrl(this.domainServerConfigService.getBaseUrl().toString())
|
||||
: issuer;
|
||||
|
||||
const cliRegistration =
|
||||
await this.applicationRegistrationService.findOneByUniversalIdentifier(
|
||||
@@ -38,7 +26,7 @@ export class OAuthDiscoveryController {
|
||||
|
||||
return {
|
||||
issuer,
|
||||
authorization_endpoint: `${authorizeBase}/authorize`,
|
||||
authorization_endpoint: `${issuer}/authorize`,
|
||||
token_endpoint: `${issuer}/oauth/token`,
|
||||
registration_endpoint: `${issuer}/oauth/register`,
|
||||
revocation_endpoint: `${issuer}/oauth/revoke`,
|
||||
@@ -80,10 +68,4 @@ export class OAuthDiscoveryController {
|
||||
private getRequestBaseUrl(request: Request): string {
|
||||
return `${request.protocol}://${request.get('host')}`;
|
||||
}
|
||||
|
||||
private isApiHost(request: Request): boolean {
|
||||
const serverUrl = this.twentyConfigService.get('SERVER_URL');
|
||||
|
||||
return request.get('host') === new URL(serverUrl).host;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user