update oauth
This commit is contained in:
@@ -14,6 +14,7 @@ import { ProfileController } from './controllers/profile.controller';
|
||||
import { DocumentsController } from './controllers/documents.controller';
|
||||
import { AddressesController } from './controllers/addresses.controller';
|
||||
import { OAuthController } from './controllers/oauth.controller';
|
||||
import { WellKnownController, OAuthAuthorizeDiscoveryController } from './controllers/well-known.controller';
|
||||
import { AdvancedAuthController } from './controllers/advanced-auth.controller';
|
||||
import { FamilyController } from './controllers/family.controller';
|
||||
import { ChatController } from './controllers/chat.controller';
|
||||
@@ -54,7 +55,7 @@ import { AdminGuard, SuperAdminGuard } from './guards/admin.guard';
|
||||
}
|
||||
])
|
||||
],
|
||||
controllers: [AuthController, AdminController, RbacController, SecurityController, SettingsController, PublicSettingsController, HealthController, ProfileController, DocumentsController, AddressesController, OAuthController, AdvancedAuthController, FamilyController, ChatController, NotificationsController, MediaController],
|
||||
controllers: [AuthController, AdminController, RbacController, SecurityController, SettingsController, PublicSettingsController, HealthController, ProfileController, DocumentsController, AddressesController, OAuthController, WellKnownController, OAuthAuthorizeDiscoveryController, AdvancedAuthController, FamilyController, ChatController, NotificationsController, MediaController],
|
||||
providers: [CoreGrpcService, AdminGuard, SuperAdminGuard]
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
45
apps/api-gateway/src/controllers/well-known.controller.ts
Normal file
45
apps/api-gateway/src/controllers/well-known.controller.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
import { CoreGrpcService } from '../core-grpc.service';
|
||||
import { buildOpenIdConfiguration, resolveOAuthIssuer } from '../lib/oauth-issuer';
|
||||
|
||||
@ApiTags('OpenID Connect')
|
||||
@Controller('.well-known')
|
||||
export class WellKnownController {
|
||||
constructor(private readonly core: CoreGrpcService) {}
|
||||
|
||||
@Get('openid-configuration')
|
||||
@ApiOperation({
|
||||
summary: 'OpenID Connect Discovery',
|
||||
description: 'Метаданные OIDC-провайдера. Issuer и endpoints берутся из PUBLIC_API_URL в настройках.'
|
||||
})
|
||||
async openIdConfiguration() {
|
||||
const issuer = await resolveOAuthIssuer(this.core);
|
||||
return buildOpenIdConfiguration(issuer);
|
||||
}
|
||||
|
||||
@Get('jwks.json')
|
||||
@ApiOperation({
|
||||
summary: 'JWKS',
|
||||
description: 'Пустой набор ключей: токены подписываются HS256 на стороне IdP. Для проверки id_token используйте userinfo.'
|
||||
})
|
||||
jwks() {
|
||||
return { keys: [] };
|
||||
}
|
||||
}
|
||||
|
||||
@ApiTags('OpenID Connect')
|
||||
@Controller('oauth/authorize')
|
||||
export class OAuthAuthorizeDiscoveryController {
|
||||
constructor(private readonly core: CoreGrpcService) {}
|
||||
|
||||
@Get('.well-known/openid-configuration')
|
||||
@ApiOperation({
|
||||
summary: 'OpenID Discovery (alias)',
|
||||
description: 'Совместимость с клиентами, ошибочно использующими authorization endpoint как issuer.'
|
||||
})
|
||||
async openIdConfigurationAlias() {
|
||||
const issuer = await resolveOAuthIssuer(this.core);
|
||||
return buildOpenIdConfiguration(issuer);
|
||||
}
|
||||
}
|
||||
56
apps/api-gateway/src/lib/oauth-issuer.ts
Normal file
56
apps/api-gateway/src/lib/oauth-issuer.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { CoreGrpcService } from '../core-grpc.service';
|
||||
|
||||
function normalizeBaseUrl(url: string) {
|
||||
return url.trim().replace(/\/+$/, '');
|
||||
}
|
||||
|
||||
export async function resolveOAuthIssuer(core: CoreGrpcService, fallback = 'http://localhost:3000'): Promise<string> {
|
||||
const envIssuer = process.env.PUBLIC_API_URL?.trim();
|
||||
try {
|
||||
const response = (await firstValueFrom(core.settings.GetSetting({ key: 'PUBLIC_API_URL' }))) as { value?: string };
|
||||
const fromDb = response.value?.trim();
|
||||
if (fromDb) {
|
||||
return normalizeBaseUrl(fromDb);
|
||||
}
|
||||
} catch {
|
||||
// fallback ниже
|
||||
}
|
||||
|
||||
if (envIssuer) {
|
||||
return normalizeBaseUrl(envIssuer);
|
||||
}
|
||||
|
||||
try {
|
||||
const response = (await firstValueFrom(core.settings.GetSetting({ key: 'PROJECT_DOMAIN' }))) as { value?: string };
|
||||
const domain = response.value?.trim();
|
||||
if (domain) {
|
||||
if (domain.startsWith('http://') || domain.startsWith('https://')) {
|
||||
return normalizeBaseUrl(domain);
|
||||
}
|
||||
return `https://${domain.replace(/^\/+/, '')}`;
|
||||
}
|
||||
} catch {
|
||||
// fallback ниже
|
||||
}
|
||||
|
||||
return normalizeBaseUrl(fallback);
|
||||
}
|
||||
|
||||
export function buildOpenIdConfiguration(issuer: string) {
|
||||
const base = normalizeBaseUrl(issuer);
|
||||
return {
|
||||
issuer: base,
|
||||
authorization_endpoint: `${base}/oauth/authorize`,
|
||||
token_endpoint: `${base}/oauth/token`,
|
||||
userinfo_endpoint: `${base}/oauth/userinfo`,
|
||||
jwks_uri: `${base}/.well-known/jwks.json`,
|
||||
response_types_supported: ['code'],
|
||||
subject_types_supported: ['public'],
|
||||
id_token_signing_alg_values_supported: ['HS256'],
|
||||
scopes_supported: ['openid', 'profile', 'email', 'phone', 'address', 'documents'],
|
||||
token_endpoint_auth_methods_supported: ['client_secret_post'],
|
||||
grant_types_supported: ['authorization_code', 'refresh_token'],
|
||||
claims_supported: ['sub', 'email', 'phone', 'name', 'picture']
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user