Authentication и авторизация

Backbone.js не содержит встроенного механизма аутентификации и авторизации, что принципиально соответствует его философии: фреймворк отвечает за структуру клиентского приложения, а не за бизнес-логику безопасности. Реализация этих аспектов полностью ложится на архитектуру приложения и взаимодействие с сервером. Это позволяет гибко адаптировать решения под любые протоколы — от сессионной авторизации до JWT и OAuth.


Authentication (аутентификация) — процесс подтверждения личности пользователя. Authorization (авторизация) — процесс определения прав доступа уже аутентифицированного пользователя.

В Backbone-приложении эти процессы всегда связаны с сервером, но клиент отвечает за:

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

Архитектурное место аутентификации в Backbone

Типовая структура включает:

  • Model — представление текущего пользователя;
  • Collection — опционально, если требуется список пользователей;
  • View — формы входа, регистрации, интерфейс с учётом прав;
  • Router — защита маршрутов;
  • Sync / AJAX слой — передача токенов и обработка ответов сервера.

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


Модель текущего пользователя

Чаще всего используется отдельная модель, представляющая текущую сессию:

var CurrentUser = Backbone.Model.extend({
    url: '/api/session',

    defaults: {
        authenticated: false,
        role: null,
        permissions: []
    },

    isAuthenticated: function() {
        return this.get('authenticated') === true;
    },

    hasPermission: function(permission) {
        return this.get('permissions').includes(permission);
    }
});

Эта модель:

  • загружается при старте приложения;
  • обновляется при входе/выходе;
  • используется для проверки прав в маршрутах и представлениях.

Процесс аутентификации (логин)

Типовой сценарий:

  1. Пользователь вводит логин и пароль.
  2. View отправляет данные на сервер.
  3. Сервер возвращает токен или устанавливает сессию.
  4. Клиент обновляет модель текущего пользователя.

Пример View для логина:

var LoginView = Backbone.View.extend({
    events: {
        'submit form': 'login'
    },

    login: function(e) {
        e.preventDefault();

        var credentials = {
            email: this.$('[name=email]').val(),
            password: this.$('[name=password]').val()
        };

        $.post('/api/login', credentials)
            .done(function(data) {
                currentUser.set(data);
            })
            .fail(function() {
                // обработка ошибки
            });
    }
});

Важно, что View не хранит состояние авторизации — оно принадлежит модели.


Хранение токенов и сессий

При использовании серверных сессий:

  • браузер автоматически отправляет cookie;
  • Backbone.sync работает без изменений;
  • защита зависит от HTTP-only и SameSite параметров.

Преимущество — минимальная логика на клиенте. Недостаток — сложность масштабирования и CSRF.


JWT / Bearer Token

При использовании токенов:

  • токен сохраняется в localStorage или sessionStorage;
  • токен добавляется в каждый запрос;
  • при истечении срока требуется обновление.

Расширение Backbone.sync:

var originalSync = Backbone.sync;

Backbone.sync = function(method, model, options) {
    options = options || {};
    options.headers = options.headers || {};

    var token = localStorage.getItem('token');
    if (token) {
        options.headers.Authorization = 'Bearer ' + token;
    }

    return originalSync.call(this, method, model, options);
};

Такой подход делает авторизацию прозрачной для всех моделей и коллекций.


Проверка авторизации при инициализации приложения

При старте приложения выполняется запрос к серверу:

currentUser.fetch({
    success: function() {
        appRouter.start();
    },
    error: function() {
        appRouter.start();
    }
});

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


Защита маршрутов (Router Guard)

Backbone.Router не поддерживает guards из коробки, но они легко реализуются:

var AppRouter = Backbone.Router.extend({
    routes: {
        'admin': 'adminPanel',
        'login': 'login'
    },

    adminPanel: function() {
        if (!currentUser.isAuthenticated()) {
            this.navigate('login', { trigger: true });
            return;
        }

        if (!currentUser.hasPermission('admin')) {
            this.navigate('', { trigger: true });
            return;
        }

        // загрузка admin view
    }
});

Таким образом:

  • аутентификация проверяется на уровне маршрута;
  • авторизация зависит от ролей или разрешений.

Управление доступом в представлениях

Помимо маршрутов, контроль доступа часто требуется внутри интерфейса.

Пример условного рендера:

render: function() {
    this.$el.html(this.template(this.model.toJSON()));

    if (!currentUser.hasPermission('edit')) {
        this.$('.edit-button').remove();
    }

    return this;
}

Такой подход не заменяет серверную проверку, но улучшает UX и предотвращает ошибочные действия.


Обработка ошибок доступа

Сервер должен возвращать корректные HTTP-коды:

  • 401 Unauthorized — не аутентифицирован;
  • 403 Forbidden — нет прав.

Глобальная обработка:

$(document).ajaxError(function(e, xhr) {
    if (xhr.status === 401) {
        appRouter.navigate('login', { trigger: true });
    }
});

Это позволяет централизованно реагировать на потерю сессии или истечение токена.


Выход из системы (logout)

Процесс выхода:

  1. Удаление токена или сессии.
  2. Сброс модели пользователя.
  3. Перенаправление.
function logout() {
    localStorage.removeItem('token');
    currentUser.clear().set(currentUser.defaults);
    appRouter.navigate('login', { trigger: true });
}

Важно полностью очищать состояние, чтобы избежать утечек прав.


Авторизация на уровне моделей и коллекций

Иногда доступ зависит от конкретного ресурса:

var Document = Backbone.Model.extend({
    canEdit: function() {
        return currentUser.hasPermission('edit_document') &&
               this.get('owner_id') === currentUser.id;
    }
});

Это позволяет инкапсулировать бизнес-логику рядом с данными.


Синхронизация прав при изменении роли

При изменении прав пользователя:

  • сервер возвращает обновлённый профиль;
  • модель CurrentUser обновляется;
  • представления реагируют через события change.
currentUser.on('change', function() {
    appView.render();
});

Backbone-событийная модель делает такие сценарии предсказуемыми и управляемыми.


Безопасность и ограничения клиентской авторизации

Клиентская авторизация:

  • не является источником истины;
  • не заменяет серверные проверки;
  • используется только для управления интерфейсом и навигацией.

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


Итоговая архитектурная схема

  • Сервер — источник прав и идентичности.
  • Backbone.Model — хранение состояния пользователя.
  • Backbone.Router — контроль доступа к маршрутам.
  • Backbone.View — адаптация интерфейса.
  • Backbone.sync — передача токенов и обработка ошибок.

Такая структура остаётся масштабируемой, тестируемой и не привязанной к конкретному протоколу аутентификации.