Аутентификация и авторизация

SkateJS — это современная библиотека для создания веб-компонентов с использованием стандарта Custom Elements, которая позволяет строить модульные и повторно используемые интерфейсы. В контексте аутентификации и авторизации важна организация состояния пользователя и безопасное управление доступом к функциональности приложения.

Управление состоянием пользователя

Для реализации аутентификации ключевым элементом является хранение состояния текущего пользователя. В SkateJS это удобно делать с использованием props и observables.

import { Component, h, prop } from 'skatejs';

class UserSession extends Component {
  static props = {
    user: prop.object(),
    token: prop.string()
  };

  constructor() {
    super();
    this.user = null;
    this.token = null;
  }

  isAuthenticated() {
    return !!this.token;
  }
}

customElements.define('user-session', UserSession);

Ключевой момент: состояние user и token должно храниться централизованно и передаваться дочерним компонентам через свойства или контекст, чтобы избежать дублирования и возможных рассинхронизаций.

Реализация формы логина

Форма логина в SkateJS строится как обычный компонент с реактивными свойствами для хранения полей ввода и методов для обработки событий.

class LoginForm extends Component {
  static props = {
    username: prop.string(),
    password: prop.string(),
    onLogin: prop.func()
  };

  constructor() {
    super();
    this.username = '';
    this.password = '';
  }

  handleSubmit(event) {
    event.preventDefault();
    if(this.onLogin) {
      this.onLogin({ username: this.username, password: this.password });
    }
  }

  render() {
    return h('form', { onSubmit: this.handleSubmit.bind(this) },
      h('input', { type: 'text', value: this.username, onInput: e => this.username = e.target.value }),
      h('input', { type: 'password', value: this.password, onInput: e => this.password = e.target.value }),
      h('button', null, 'Войти')
    );
  }
}

customElements.define('login-form', LoginForm);

Выделение ключевого: обработка формы привязана к реактивным свойствам компонента, что обеспечивает мгновенное обновление состояния при вводе данных пользователем.

Авторизация компонентов

Контроль доступа к отдельным частям интерфейса реализуется через условный рендеринг на основе состояния пользователя. SkateJS поддерживает динамическое обновление DOM, что позволяет скрывать или показывать элементы без перезагрузки страницы.

class ProtectedContent extends Component {
  static props = {
    session: prop.object()
  };

  render() {
    if (!this.session?.isAuthenticated()) {
      return h('p', null, 'Доступ запрещен. Пожалуйста, войдите в систему.');
    }
    return h('div', null, 'Секретная информация для авторизованных пользователей');
  }
}

customElements.define('protected-content', ProtectedContent);

Особенность: использование метода isAuthenticated() из объекта сессии позволяет централизованно управлять проверкой авторизации, что повышает безопасность и упрощает сопровождение кода.

Взаимодействие с сервером

Аутентификация в реальных приложениях требует обмена данными с сервером через REST API или GraphQL. SkateJS компоненты могут использовать fetch или сторонние библиотеки для работы с HTTP-запросами:

async function loginUser(credentials) {
  const response = await fetch('/api/login', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(credentials)
  });
  if (!response.ok) {
    throw new Error('Ошибка авторизации');
  }
  return await response.json();
}

Ключевой момент: полученный токен необходимо безопасно хранить (например, в памяти или secure cookie) и передавать в заголовках Authorization при последующих запросах для проверки доступа.

Реактивная интеграция с приложением

SkateJS позволяет строить реактивные цепочки, связывая компоненты с состоянием пользователя. Например, можно создать главный компонент приложения, который управляет сессией и передает её в дочерние компоненты:

class AppRoot extends Component {
  static props = {
    session: prop.object()
  };

  constructor() {
    super();
    this.session = new UserSession();
  }

  handleLogin = async (credentials) => {
    try {
      const data = await loginUser(credentials);
      this.session.user = data.user;
      this.session.token = data.token;
    } catch (err) {
      console.error(err);
    }
  }

  render() {
    return h('div', null,
      h('login-form', { onLogin: this.handleLogin }),
      h('protected-content', { session: this.session })
    );
  }
}

customElements.define('app-root', AppRoot);

Суть подхода: централизованное управление сессией и реактивное обновление дочерних компонентов обеспечивает безопасный и масштабируемый механизм аутентификации и авторизации без сложных глобальных хранилищ.

Рекомендации по безопасности

  • Никогда не хранить токен в localStorage без шифрования, чтобы избежать XSS-уязвимостей.
  • Минимизировать доступ к состоянию пользователя только необходимыми компонентами.
  • Использовать HTTPS для всех запросов аутентификации.
  • Обновлять токены при каждом критическом действии пользователя для предотвращения повторного использования украденного токена.

С таким подходом SkateJS позволяет создавать модульные и безопасные интерфейсы, где аутентификация и авторизация управляются декларативно через компоненты и реактивное состояние.