Table of Contents

Окно авторизации из библиотеки @ascon/polynom-login

Библиотека @ascon/polynom-login предоставляет готовое окно авторизации в ПОЛИНОМ:MDM. Окно выполняет полный сценарий входа: выбор зарегистрированного хранилища, вход по имени входа и паролю, вход от системной учётной записи, вход по внешнему токену доступа и через общий сервис авторизации (ОСА). Последние использованные данные сохраняются в локальном хранилище браузера.

См. пример SDK/Web/Samples/Client/polynom-select-demo — в нём окно подключено на отдельном маршруте sign-in и используется для входа перед открытием диалогов выбора.

Состав библиотеки

Библиотека экспортирует:

  • AuthenticationPageComponent — готовая страница авторизации (selector: mdm-authentication-page), размещается на маршруте;
  • AuthenticationViewComponent — встраиваемая форма авторизации (selector: mdm-authentication-view) с входными параметрами accessToken и storageId;
  • AuthenticationLocalStorage — сервис сохранения последнего выбранного хранилища и режима входа;
  • loginValidator, passwordValidator, newPasswordValidator и specialSymbolsLabel — валидаторы и подпись допустимых символов для форм;
  • polynomLoginLocalizationScope — область локализации polynom/polynom-login.

Подключение библиотек

Рекомендуемые зависимости:

{
  "dependencies": {
    "@ascon/core": "24.1.126",
    "@ascon/polynom-api": "~24.1.14404",
    "@ascon/polynom-api-client": "~24.1.14404",
    "@ascon/polynom-integration": "~24.1.14404",
    "@ascon/polynom-login": "~24.1.14404",
    "@ascon/polynom-ui": "~24.1.14404",
    "@ascon/primeng": "24.0.2",
    "@ascon/qwebchannel-bridge": "^6.8.3",
    "@ascon/ui": "24.1.126"
  }
}

Библиотеки @ascon/* можно получить из каталога поставки SDK/Web/Samples/Client/libs/@ascon или из npm-регистра.

Подключение активов и стилей

В файл project.json добавьте активы и стили библиотеки (аналогично примеру SDK/Web/Samples/Client/polynom-select-demo):

{
  "targets": {
    "build": {
      "options": {
        "assets": [
          {
            "glob": "**/*",
            "input": "node_modules/@ascon/polynom-login/assets",
            "output": "./assets"
          },
          {
            "glob": "**/*",
            "input": "node_modules/@ascon/polynom-ui/assets",
            "output": "./assets"
          }
        ],
        "styles": [
          {
            "input": "node_modules/@ascon/theme/theme.css",
            "inject": false,
            "bundleName": "theme"
          },
          {
            "input": "node_modules/@ascon/polynom-login/styles/polynom-login.css",
            "inject": true,
            "bundleName": "app-styles"
          },
          {
            "input": "node_modules/@ascon/polynom-ui/styles/polynom-ui.css",
            "inject": true,
            "bundleName": "app-styles"
          }
        ]
      }
    }
  }
}

Полный пример подключения активов приведён в статье Выбор объекта из ПОЛИНОМ:MDM.

В файл index.html добавьте ссылки на собранные стили:

<html lang="ru">
  <head>
    <link
      href="theme.css"
      id="theme-link"
      media="all"
      onload="this.media='all'"
      rel="stylesheet"
      type="text/css"
    />
    <link
      href="app-styles.css"
      id="app-link"
      media="all"
      onload="this.media='all'"
      rel="stylesheet"
      type="text/css"
    />
  </head>
</html>

Настройка приложения

В app.config подключите HTTP-клиент с интерцептором, локализацию (включая область polynom/polynom-login), начальное обновление токена и маршрутизацию:

// app.config.ts
import { APP_BASE_HREF } from "@angular/common";
import { provideHttpClient, withInterceptors } from "@angular/common/http";
import { APP_INITIALIZER, ApplicationConfig, isDevMode } from "@angular/core";
import { provideAnimations } from "@angular/platform-browser/animations";
import { provideRouter } from "@angular/router";
import { provideLocalization, provideStorages } from "@ascon/core";
import { AuthStore, LoginService, PolynomApiConfig } from "@ascon/polynom-api";
import { IntegrationHostService } from "@ascon/polynom-integration";
import { appInitializer } from "./helpers/app.initializer";
import { jwtInterceptor } from "./helpers/jwt.interceptor";
import { appRoutes } from "./app.routes";

export const appConfig: ApplicationConfig = {
  providers: [
    provideAnimations(),
    provideHttpClient(withInterceptors([jwtInterceptor])),
    provideStorages(),
    provideLocalization({
      availableLangs: ["ru", "en"],
      defaultLang: "ru",
      prodMode: !isDevMode(),
      scopes: [
        "polynom/polynom-select-demo",
        "polynom/polynom-api",
        "polynom/polynom-login",
        "polynom/polynom-ui",
        "polynom/polynom-core"
      ]
    }),
    { provide: APP_BASE_HREF, useValue: "/select-demo" },
    {
      provide: APP_INITIALIZER,
      useFactory: appInitializer,
      deps: [LoginService, PolynomApiConfig, AuthStore],
      multi: true
    },
    provideRouter(appRoutes)
  ]
};
// helpers/app.initializer.ts
import { LoginService, PolynomApiConfig } from "@ascon/polynom-api";
import { ClientTypes } from "@ascon/polynom-api-client";
import { delay, lastValueFrom, take, TeardownLogic } from "rxjs";
import { environment } from "../environments/environment";

export async function initializeClient(loginService: LoginService): Promise<TeardownLogic> {
  await lastValueFrom(loginService.updateToken().pipe(take(1), delay(500)));
}

export function appInitializer(loginService: LoginService, config: PolynomApiConfig): () => Promise<TeardownLogic> {
  config.setClientType(ClientTypes.Client);
  config.setHost(environment.apiRoute);

  return async () => {
    try {
      return await initializeClient(loginService);
    } catch (error) {
      console.error("App initialization failed:", error);
    }
  };
}
// helpers/jwt.interceptor.ts
import { HttpHeaders, HttpInterceptorFn } from "@angular/common/http";
import { inject } from "@angular/core";
import { LoginService } from "@ascon/polynom-api";

export const jwtInterceptor: HttpInterceptorFn = (request, next) => {
  const loginService = inject(LoginService);

  const polynomAccessToken = loginService.accessToken;
  if (polynomAccessToken) {
    const clonedRequest = request.clone({
      headers: new HttpHeaders({
        Authorization: `Bearer ${polynomAccessToken}`
      })
    });

    return next(clonedRequest);
  }

  return next(request);
};

Адрес web-сервера задаётся сервисом PolynomApiConfig (см. Авторизация в TypeScript/Angular).

Размещение окна авторизации

AuthenticationPageComponent — standalone-компонент. Разместите его на отдельном маршруте:

// app.routes.ts
import { Route } from "@angular/router";
import { AuthenticationPageComponent } from "@ascon/polynom-login";
import { MainComponent } from "./main/main.component";

export const appRoutes: Route[] = [
  { path: "", redirectTo: "select-example", pathMatch: "full" },
  {
    path: "",
    component: MainComponent,
    children: [
      { path: "sign-in", component: AuthenticationPageComponent },
      {
        path: "select-example",
        loadComponent: async () =>
          import("./select-example/select-example.component").then(module => module.SelectExampleComponent)
      }
    ]
  },
  { path: "**", redirectTo: "select-example", pathMatch: "full" }
];

Переход на окно авторизации выполняется по маршруту sign-in с параметром returnUrl, чтобы после входа вернуться на исходную страницу:

import { Router } from "@angular/router";

export class AppRouterTools {
  public static subRoot(route: string): string {
    return `/${route}`;
  }

  public static navigateTo(router: Router, path: string): void {
    router.navigate([AppRouterTools.subRoot(path)], { queryParams: { returnUrl: router.url } }).then();
  }
}

Режимы входа

Окно автоматически предоставляет доступные режимы:

  • Имя входа и пароль — основной режим для пользователей ПОЛИНОМ:MDM.
  • Системная учётная запись — вход без ввода логина и пароля.
  • Внешний токен доступа — вход по ранее полученному accessToken. Режим включается, если компоненту передан входной параметр accessToken; при этом выбор хранилища фиксируется.
  • ОСА — вход через общий сервис авторизации. Режим отображается, если web-сервер сообщает о доступности ОСА (см. Авторизация с помощью общего сервиса авторизации).

Встраивание формы авторизации

Если окно на отдельном маршруте не подходит, используйте AuthenticationViewComponent и передайте ему фиксированное хранилище и токен доступа:

import { Component } from "@angular/core";
import { AuthenticationViewComponent } from "@ascon/polynom-login";

@Component({
  selector: "mdm-embedded-sign-in",
  standalone: true,
  imports: [AuthenticationViewComponent],
  templateUrl: "./embedded-sign-in.component.html"
})
export class EmbeddedSignInComponent {
  // Идентификатор хранилища, к которому выполняется подключение.
  storageId = "f552d8ff-50b1-468e-9577-2069cbaed16e";

  // Токен доступа, полученный извне (например, от ОСА). Может быть пустым.
  accessToken = "";
}
<mdm-authentication-view [storageId]="storageId" [accessToken]="accessToken"></mdm-authentication-view>

Интеграция в веб-клиент ПОЛИНОМ:MDM

При запуске окна внутри веб-клиента ПОЛИНОМ:MDM идентификатор хранилища и токен доступа передаются автоматически через IntegrationHostService из @ascon/polynom-integration. В этом случае AuthenticationPageComponent сам получает значения storageId и accessToken.

Сохранённые данные авторизации

Сервис AuthenticationLocalStorage хранит последнее выбранное хранилище и режим входа:

import { inject } from "@angular/core";
import { AuthenticationLocalStorage } from "@ascon/polynom-login";

private readonly _authDataStorage: AuthenticationLocalStorage = inject(AuthenticationLocalStorage);

// Идентификатор последнего выбранного хранилища.
const lastStorageId: string = this._authDataStorage.lastStorageId;

// Признак входа от системной учётной записи.
const useSystemAccount: boolean = this._authDataStorage.useSystemAccount;

// Признак входа через ОСА.
const useSsoAccount: boolean = this._authDataStorage.useSsoAccount;

// Поток изменения данных авторизации.
this._authDataStorage.authData$.subscribe(data => {
  // ...
});

// Сброс сохранённых данных.
this._authDataStorage.reset();

Проверка состояния авторизации и выход

Состояние авторизации и выход выполняются сервисом LoginService:

import { Observable } from "rxjs";
import { LoginService } from "@ascon/polynom-api";

private readonly _loginService: LoginService = inject(LoginService);

get authorized$(): Observable<boolean> {
  return this._loginService.authenticated$;
}

signOutHandler(): void {
  this._loginService.signOut().pipe(take(1)).subscribe();
}

Возможные ошибки

Ошибка Причина Решение
Окно авторизации отображается без стилей Не подключены стили polynom-login.css, polynom-ui.css и theme.css Добавьте активы и стили библиотеки в project.json, а ссылки на собранные стили — в index.html
Не отображается список хранилищ Не задан адрес web-сервера или он недоступен Укажите адрес через PolynomApiConfig.setHost() и проверьте доступность web-сервера
Отсутствуют подписи элементов интерфейса Не подключена область локализации polynom/polynom-login Добавьте polynom/polynom-login в массив scopes функции provideLocalization()
Не отображается режим входа через ОСА Web-сервер не сообщает о доступности ОСА Проверьте настройки ОСА на web-сервере (см. Авторизация с помощью общего сервиса авторизации)
Ошибка инициализации приложения при запуске Не выполнен вход и отсутствует сохранённый токен обновления Убедитесь, что пользователь перенаправляется на маршрут sign-in, а APP_INITIALIZER обрабатывает ошибку updateToken()

Связанные статьи