update oauth

This commit is contained in:
lendry
2026-06-25 14:40:05 +03:00
parent 1796008a28
commit 6c63343fc7
19 changed files with 623 additions and 200 deletions

View File

@@ -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 {}

View 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);
}
}

View 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']
};
}