feat(auth): add login/password authentication strategy
- KeyDescriptor now accepts optional login field alongside keyFile - when login is present, clicks login/password auth method, fills email+password fields, and submits - file-key flow unchanged; validation ensures exactly one strategy is present
This commit is contained in:
@@ -14,7 +14,8 @@ import { SessionService } from '../session/session.service';
|
|||||||
import { EnvironmentService } from '../environment/environment.service';
|
import { EnvironmentService } from '../environment/environment.service';
|
||||||
|
|
||||||
interface KeyDescriptor {
|
interface KeyDescriptor {
|
||||||
keyFile: string;
|
keyFile?: string;
|
||||||
|
login?: string;
|
||||||
password: string;
|
password: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,10 +65,17 @@ export class AuthService {
|
|||||||
throw new BadRequestException(`Cannot read key descriptor: ${keyId}`, { cause: err });
|
throw new BadRequestException(`Cannot read key descriptor: ${keyId}`, { cause: err });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const useLoginPassword = !!descriptor.login;
|
||||||
|
|
||||||
|
if (!useLoginPassword) {
|
||||||
|
if (!descriptor.keyFile) {
|
||||||
|
throw new BadRequestException(`Key descriptor for "${keyId}" must have either "login" or "keyFile"`);
|
||||||
|
}
|
||||||
const keyFilePath = path.resolve(this.keysDir, descriptor.keyFile);
|
const keyFilePath = path.resolve(this.keysDir, descriptor.keyFile);
|
||||||
if (!fs.existsSync(keyFilePath)) {
|
if (!fs.existsSync(keyFilePath)) {
|
||||||
throw new BadRequestException(`Key file not found: ${descriptor.keyFile}`);
|
throw new BadRequestException(`Key file not found: ${descriptor.keyFile}`);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const browser = await chromium.launch({ headless: true, executablePath: process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH });
|
const browser = await chromium.launch({ headless: true, executablePath: process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH });
|
||||||
try {
|
try {
|
||||||
@@ -77,6 +85,19 @@ export class AuthService {
|
|||||||
this.logger.log(`Navigating to ${loginUrl}`);
|
this.logger.log(`Navigating to ${loginUrl}`);
|
||||||
await page.goto(loginUrl, { waitUntil: 'networkidle' });
|
await page.goto(loginUrl, { waitUntil: 'networkidle' });
|
||||||
|
|
||||||
|
if (useLoginPassword) {
|
||||||
|
// Click "Логін і пароль" auth method
|
||||||
|
await page.locator('p[aria-label="Логін і пароль"]').click();
|
||||||
|
|
||||||
|
// Fill login and password
|
||||||
|
await page.getByLabel('Електронна пошта').fill(descriptor.login!);
|
||||||
|
await page.getByLabel('Пароль').fill(descriptor.password);
|
||||||
|
|
||||||
|
// Click "Увійти"
|
||||||
|
await page.locator('button:has-text("Увійти")').click();
|
||||||
|
} else {
|
||||||
|
const keyFilePath = path.resolve(this.keysDir, descriptor.keyFile!);
|
||||||
|
|
||||||
// Click "Файловий ключ" button
|
// Click "Файловий ключ" button
|
||||||
await page.getByText('Файловий ключ').click();
|
await page.getByText('Файловий ключ').click();
|
||||||
|
|
||||||
@@ -91,6 +112,7 @@ export class AuthService {
|
|||||||
|
|
||||||
// Click "Продовжити"
|
// Click "Продовжити"
|
||||||
await page.locator('#id-app-login-file-key-sign-button').click();
|
await page.locator('#id-app-login-file-key-sign-button').click();
|
||||||
|
}
|
||||||
|
|
||||||
// Wait until redirected to cabinet
|
// Wait until redirected to cabinet
|
||||||
this.logger.log(`Waiting for redirect to ${cabinetUrl}`);
|
this.logger.log(`Waiting for redirect to ${cabinetUrl}`);
|
||||||
|
|||||||
Reference in New Issue
Block a user