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

Universal Router — это легковесная библиотека маршрутизации для JavaScript, обеспечивающая гибкий и декларативный способ обработки маршрутов. В контексте авторизации и аутентификации она предоставляет механизмы контроля доступа к различным маршрутам приложения, позволяя интегрировать защиту без сложных middleware.


Структура маршрутов с контролем доступа

Маршруты в Universal Router описываются в виде массива объектов, где каждый объект содержит ключи path, action и опционально дополнительные метаданные, например, authRequired. Пример базовой структуры маршрута с авторизацией:

const routes = [
  {
    path: '/',
    action: () => 'Главная страница'
  },
  {
    path: '/dashboard',
    authRequired: true,
    action: () => 'Панель управления'
  },
  {
    path: '/login',
    action: () => 'Страница входа'
  }
];

Здесь authRequired используется для указания необходимости авторизации перед доступом к маршруту. Universal Router сам по себе не проверяет права доступа, но предоставляет возможность реализовать логику проверки через функцию resolve.


Проверка авторизации через resolve

Функция resolve позволяет перехватывать маршруты и выполнять дополнительные проверки перед вызовом action. Типичная схема:

import UniversalRouter from 'universal-router';

const user = {
  isAuthenticated: false
};

const router = new UniversalRouter(routes, {
  resolve: (route, context, params) => {
    if (route.authRequired && !user.isAuthenticated) {
      return '/login'; // перенаправление на страницу входа
    }
    return route.action(context, params);
  }
});

Ключевые моменты:

  • route — объект маршрута, который проходит проверку.
  • context — произвольные данные, передаваемые при вызове router.resolve().
  • params — параметры маршрута (например, динамические сегменты /users/:id).

Такой подход позволяет централизованно обрабатывать защиту маршрутов без необходимости дублирования кода в каждом маршруте.


Динамическая авторизация

Для более сложных сценариев, например, когда права доступа зависят от роли пользователя, можно расширить маршруты метаданными:

const routes = [
  {
    path: '/admin',
    authRequired: true,
    roles: ['admin'],
    action: () => 'Административная панель'
  }
];

const router = new UniversalRouter(routes, {
  resolve: (route, context, params) => {
    if (route.authRequired && !user.isAuthenticated) {
      return '/login';
    }
    if (route.roles && !route.roles.includes(user.role)) {
      return '/unauthorized';
    }
    return route.action(context, params);
  }
});

В этом примере маршруты с roles ограничиваются конкретными ролями пользователя, а Universal Router позволяет обрабатывать их через единый resolve-колбэк.


Обработка асинхронной аутентификации

Многие приложения используют асинхронные методы проверки авторизации, например, через API-запросы. Universal Router полностью поддерживает асинхронные action и resolve:

const router = new UniversalRouter(routes, {
  resolve: async (route, context, params) => {
    if (route.authRequired) {
      const isAuth = await checkUserAuth(); // асинхронная проверка
      if (!isAuth) return '/login';
    }
    return route.action(context, params);
  }
});

Асинхронная проверка позволяет безопасно получать актуальное состояние пользователя перед рендером компонента.


Перенаправления и защищённые маршруты

Universal Router позволяет возвращать строку пути для перенаправления. Это особенно полезно для сценариев:

  • Неавторизованный доступ → /login
  • Пользователь без нужной роли → /unauthorized
  • Попытка доступа к заблокированным страницам → /forbidden

Пример с вложенными маршрутами:

const routes = [
  {
    path: '/profile',
    authRequired: true,
    children: [
      { path: '', action: () => 'Мой профиль' },
      { path: '/edit', action: () => 'Редактировать профиль' }
    ]
  }
];

Вложенные маршруты наследуют свойства родителя, включая authRequired, что упрощает контроль доступа к целым секциям приложения.


Интеграция с токенами и JWT

При использовании токенов или JWT проверка авторизации может включать декодирование токена и проверку его срока действия:

import jwtDecode from 'jwt-decode';

const router = new UniversalRouter(routes, {
  resolve: (route, context, params) => {
    const token = localStorage.getItem('token');
    if (route.authRequired) {
      if (!token) return '/login';
      const payload = jwtDecode(token);
      if (payload.exp * 1000 < Date.now()) return '/login';
    }
    return route.action(context, params);
  }
});

Такой подход позволяет интегрировать маршрутизацию с существующей системой авторизации без лишних сложностей.


Передача контекста и состояния пользователя

context в router.resolve может включать текущего пользователя, настройки приложения и любые глобальные объекты. Это упрощает доступ к данным при проверке прав:

const context = { user };

router.resolve('/dashboard', context)
  .then(result => {
    console.log(result);
  });

Использование контекста позволяет сделать маршруты полностью независимыми от глобальных переменных и облегчает тестирование логики авторизации.


Поддержка редиректов с сохранением исходного пути

Чтобы после авторизации возвращать пользователя на исходную страницу, можно сохранять путь:

const router = new UniversalRouter(routes, {
  resolve: (route, context, params) => {
    if (route.authRequired && !context.user.isAuthenticated) {
      return `/login?redirect=${encodeURIComponent(context.path)}`;
    }
    return route.action(context, params);
  }
});

Это делает UX более удобным и обеспечивает корректное поведение при входе через сторонние провайдеры или после перезагрузки страницы.


Universal Router предоставляет гибкие инструменты для реализации авторизации и аутентификации в JavaScript-приложениях. Ключевыми концепциями являются использование метаданных маршрутов, централизованная проверка через resolve, поддержка асинхронной логики и интеграция с токенами или ролями пользователей. Такой подход позволяет создавать безопасные и масштабируемые маршруты без громоздких middleware и дополнительных библиотек.