fix and update
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import { Body, Controller, Get, Headers, Ip, Param, Post } from '@nestjs/common';
|
||||
import { Body, Controller, Get, Headers, Param, Post, Req } from '@nestjs/common';
|
||||
import type { Request } from 'express';
|
||||
import { enrichAuthClientMeta } from '../client-request.util';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { ApiBearerAuth, ApiBody, ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||
import { CoreGrpcService } from '../core-grpc.service';
|
||||
@@ -33,14 +35,14 @@ export class AdvancedAuthController {
|
||||
@ApiOperation({ summary: 'Создать QR-сессию', description: 'Создает временную QR-сессию для входа с другого устройства.' })
|
||||
@ApiBody({ type: QrSessionDto })
|
||||
@ApiResponse({ status: 201, description: 'QR-сессия создана' })
|
||||
createQr(@Body() dto: QrSessionDto, @Ip() ipAddress?: string, @Headers('user-agent') userAgent?: string) {
|
||||
return this.core.advancedAuth.CreateQrSession({
|
||||
deviceName: dto.deviceName,
|
||||
fingerprint: dto.fingerprint,
|
||||
deviceType: dto.deviceType ?? 'WEB',
|
||||
ipAddress,
|
||||
userAgent
|
||||
});
|
||||
createQr(@Body() dto: QrSessionDto, @Req() req: Request) {
|
||||
return this.core.advancedAuth.CreateQrSession(
|
||||
enrichAuthClientMeta(req, {
|
||||
deviceName: dto.deviceName,
|
||||
fingerprint: dto.fingerprint,
|
||||
deviceType: dto.deviceType ?? 'WEB'
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@Get('qr/session/:sessionId')
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { Body, Controller, Get, Headers, Ip, Post, UseInterceptors } from '@nestjs/common';
|
||||
import { Body, Controller, Get, Headers, Post, Req, UseInterceptors } from '@nestjs/common';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { ApiBearerAuth, ApiBody, ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
import type { Request } from 'express';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { CoreGrpcService } from '../core-grpc.service';
|
||||
import { enrichAuthClientMeta } from '../client-request.util';
|
||||
import { BeginTotpLoginDto, IdentifyDto, LdapLoginDto, LoginDto, PasswordlessOtpDto, PasswordlessVerifyDto, PasswordLoginDto, RefreshSessionDto, RegisterDto, VerifyPinDto, VerifyTotpLoginDto } from '../dto/auth.dto';
|
||||
import { resolveAuthorizedPayload, verifyAccessToken } from '../session-auth';
|
||||
import { FedcmCookieInterceptor } from '../interceptors/fedcm-cookie.interceptor';
|
||||
@@ -26,8 +28,8 @@ export class AuthController {
|
||||
@Post('login')
|
||||
@ApiOperation({ summary: 'Вход по почте, телефону или логину', description: 'Возвращает JWT и refresh token. Если PIN включен, сессия создается в ограниченном режиме.' })
|
||||
@ApiBody({ type: LoginDto })
|
||||
login(@Body() dto: LoginDto) {
|
||||
return this.core.auth.Login(dto);
|
||||
login(@Body() dto: LoginDto, @Req() req: Request) {
|
||||
return this.core.auth.Login(enrichAuthClientMeta(req, dto));
|
||||
}
|
||||
|
||||
@Post('identify')
|
||||
@@ -40,29 +42,29 @@ export class AuthController {
|
||||
@Post('otp/send')
|
||||
@ApiOperation({ summary: 'Отправить OTP для входа', description: 'Passwordless-first вход: пользователь вводит почту или телефон, сервер создает 6-значный код и пишет его в console.log.' })
|
||||
@ApiBody({ type: PasswordlessOtpDto })
|
||||
sendOtp(@Body() dto: PasswordlessOtpDto, @Ip() ipAddress?: string) {
|
||||
return this.core.auth.SendOtp({ recipient: dto.recipient, channel: dto.channel, ipAddress });
|
||||
sendOtp(@Body() dto: PasswordlessOtpDto, @Req() req: Request) {
|
||||
return this.core.auth.SendOtp({ recipient: dto.recipient, channel: dto.channel, ipAddress: enrichAuthClientMeta(req, {}).ipAddress });
|
||||
}
|
||||
|
||||
@Post('otp/verify')
|
||||
@ApiOperation({ summary: 'Проверить OTP для входа', description: 'Если пользователь не существует, создает его. Если пароль не задан, сразу возвращает JWT. Если пароль задан, возвращает requiresPassword=true и tempAuthToken.' })
|
||||
@ApiBody({ type: PasswordlessVerifyDto })
|
||||
verifyOtp(@Body() dto: PasswordlessVerifyDto) {
|
||||
return this.core.auth.VerifyOtp(dto);
|
||||
verifyOtp(@Body() dto: PasswordlessVerifyDto, @Req() req: Request) {
|
||||
return this.core.auth.VerifyOtp(enrichAuthClientMeta(req, dto));
|
||||
}
|
||||
|
||||
@Post('login/password')
|
||||
@ApiOperation({ summary: 'Войти по паролю', description: 'Identifier-first парольный шаг. Принимает login+password, либо tempAuthToken+password для совместимости, затем выдает JWT.' })
|
||||
@ApiBody({ type: PasswordLoginDto })
|
||||
loginWithPassword(@Body() dto: PasswordLoginDto) {
|
||||
return this.core.auth.LoginWithPassword(dto);
|
||||
loginWithPassword(@Body() dto: PasswordLoginDto, @Req() req: Request) {
|
||||
return this.core.auth.LoginWithPassword(enrichAuthClientMeta(req, dto));
|
||||
}
|
||||
|
||||
@Post('ldap/login')
|
||||
@ApiOperation({ summary: 'Войти через LDAP/LDAPS', description: 'Аутентификация через корпоративный LDAP-сервер. Требует включённой настройки LDAP_ENABLED.' })
|
||||
@ApiBody({ type: LdapLoginDto })
|
||||
loginWithLdap(@Body() dto: LdapLoginDto) {
|
||||
return this.core.auth.LoginWithLdap(dto);
|
||||
loginWithLdap(@Body() dto: LdapLoginDto, @Req() req: Request) {
|
||||
return this.core.auth.LoginWithLdap(enrichAuthClientMeta(req, dto));
|
||||
}
|
||||
|
||||
@Post('pin/verify')
|
||||
@@ -75,15 +77,15 @@ export class AuthController {
|
||||
@Post('totp/begin')
|
||||
@ApiOperation({ summary: 'Начать вход по TOTP', description: 'Создаёт challenge для входа через приложение-аутентификатор вместо SMS/email OTP.' })
|
||||
@ApiBody({ type: BeginTotpLoginDto })
|
||||
beginTotpLogin(@Body() dto: BeginTotpLoginDto) {
|
||||
return this.core.auth.BeginTotpLogin(dto);
|
||||
beginTotpLogin(@Body() dto: BeginTotpLoginDto, @Req() req: Request) {
|
||||
return this.core.auth.BeginTotpLogin(enrichAuthClientMeta(req, dto));
|
||||
}
|
||||
|
||||
@Post('totp/verify')
|
||||
@ApiOperation({ summary: 'Подтвердить TOTP при входе', description: 'Завершает вход после проверки кода из Google Authenticator или аналога.' })
|
||||
@ApiBody({ type: VerifyTotpLoginDto })
|
||||
verifyTotpLogin(@Body() dto: VerifyTotpLoginDto) {
|
||||
return this.core.auth.VerifyTotpLogin(dto);
|
||||
verifyTotpLogin(@Body() dto: VerifyTotpLoginDto, @Req() req: Request) {
|
||||
return this.core.auth.VerifyTotpLogin(enrichAuthClientMeta(req, dto));
|
||||
}
|
||||
|
||||
@Post('refresh')
|
||||
|
||||
@@ -194,15 +194,9 @@ export class FedcmController {
|
||||
);
|
||||
}
|
||||
|
||||
@Post('session/sync')
|
||||
@HttpCode(200)
|
||||
@ApiOperation({
|
||||
summary: 'Синхронизировать FedCM cookie',
|
||||
description: 'Устанавливает HttpOnly cookie для FedCM по Bearer access token (для уже авторизованных пользователей IdP).'
|
||||
})
|
||||
async syncSession(
|
||||
@Headers('authorization') authorization: string | undefined,
|
||||
@Res({ passthrough: true }) res: Response
|
||||
private async syncFedcmSessionFromAuthorization(
|
||||
authorization: string | undefined,
|
||||
res: Response
|
||||
) {
|
||||
const payload = await verifyAccessToken(this.jwt, authorization);
|
||||
if (!payload.sessionId) {
|
||||
@@ -217,4 +211,30 @@ export class FedcmController {
|
||||
applyFedcmLoginStatus(res, true);
|
||||
return { synced: true };
|
||||
}
|
||||
|
||||
@Post('session/sync')
|
||||
@HttpCode(200)
|
||||
@ApiOperation({
|
||||
summary: 'Синхронизировать FedCM cookie',
|
||||
description: 'Устанавливает HttpOnly cookie для FedCM по Bearer access token (для уже авторизованных пользователей IdP).'
|
||||
})
|
||||
syncSessionPost(
|
||||
@Headers('authorization') authorization: string | undefined,
|
||||
@Res({ passthrough: true }) res: Response
|
||||
) {
|
||||
return this.syncFedcmSessionFromAuthorization(authorization, res);
|
||||
}
|
||||
|
||||
@Get('session/sync')
|
||||
@HttpCode(200)
|
||||
@ApiOperation({
|
||||
summary: 'Синхронизировать FedCM cookie (GET)',
|
||||
description: 'Тот же sync по Bearer token. GET нужен для совместимости с редиректами прокси и prefetch.'
|
||||
})
|
||||
syncSessionGet(
|
||||
@Headers('authorization') authorization: string | undefined,
|
||||
@Res({ passthrough: true }) res: Response
|
||||
) {
|
||||
return this.syncFedcmSessionFromAuthorization(authorization, res);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,8 @@ export class SecurityController {
|
||||
@ApiOperation({ summary: 'Активные сессии', description: 'Возвращает ACTIVE и LOCKED сессии пользователя для управления устройствами.' })
|
||||
@ApiParam({ name: 'userId', description: 'ID пользователя' })
|
||||
@ApiResponse({ status: 200, description: 'Список активных сессий получен' })
|
||||
listSessions(@Param('userId') userId: string) {
|
||||
async listSessions(@Param('userId') userId: string, @Headers('authorization') authorization?: string) {
|
||||
await resolveAuthorizedPayload(this.jwt, this.core, authorization);
|
||||
return this.core.security.ListActiveSessions({ userId });
|
||||
}
|
||||
|
||||
@@ -35,8 +36,10 @@ export class SecurityController {
|
||||
@ApiOperation({ summary: 'История входов', description: 'Показывает последние попытки входа и причины отказов.' })
|
||||
@ApiParam({ name: 'userId', description: 'ID пользователя' })
|
||||
@ApiResponse({ status: 200, description: 'История входов получена' })
|
||||
listHistory(@Param('userId') userId: string) {
|
||||
return this.core.security.ListSignInHistory({ userId });
|
||||
listHistory(@Param('userId') userId: string, @Headers('authorization') authorization?: string) {
|
||||
return resolveAuthorizedPayload(this.jwt, this.core, authorization).then(() =>
|
||||
this.core.security.ListSignInHistory({ userId })
|
||||
);
|
||||
}
|
||||
|
||||
@Get('users/:userId/totp/status')
|
||||
|
||||
Reference in New Issue
Block a user