Я не тратил на это много времени, потому что мне это было не нужно 👍

А если серьезно, Svelte — одна из тех интерфейсных сред, которая делает создание одностраничных приложений безумно простым, и мне очень понравилось работать с ней — осмелюсь сказать, больше, чем с React, в основном с учетом того, что по сравнению со Svelte, у React крутая кривая обучения.

А теперь послушай, прежде чем ты сожжешь меня на костре, проверь это. Маршрутизация в Svelte очень проста, и защита ваших маршрутов так же проста. Я покажу вам, создав самый простой в мире краулер подземелий «Легенда о… Нечто».

Настройка — проект

Начните с запуска проекта Svelte, запустив его и заменив ‹your_project_name› на название вашей дурацкой игры про поиск подземелий.

npx degit sveltejs/template <your_project_name>

Svelte поставляется с rollup из коробки, но я предпочитаю использовать webpack, так как это то, что я знаю, и я уже знаю, как заставить его работать с Typescript. Если вы хотите настроить, как я, вам нужно будет сделать дополнительную домашнюю работу. TLDR — Сделайте свой проект похожим на мой с помощью .tsconfig webpack.config.ts .babelrc и добавьте необходимые пакеты.

Ознакомьтесь с примером проекта, который я разместил для получения более подробной информации, так как это выходит за рамки этой статьи (и если вам нужно подробное пошаговое руководство по настройке Svelte + Webpack + Typescript, дайте мне знать).

Как только ваш проект настроен и запущен, мы можем начать вносить изменения. Сначала вам нужно установить несколько пакетов npm:

npm install svelte-routing dotenv @onelogin/sdk

svelte-routing для наших маршрутов.

dotenv позволяет нам использовать переменные среды.

@onelogin/sdk отвечает за процесс входа в систему и управление пользователями (подробнее об этом позже).

Прежде чем мы начнем «настоящую работу», мы создадим несколько папок/файлов. /src это место, где происходит большая часть действий. В /src давайте сделаем следующее:

cd src
mkdir ui_components pages concerns
touch ui_components/Button.svelte
touch pages/Login.svelte pages/Home.svelte pages/SecretStuff.svelte
touch concerns/stores.ts concerns/ProtectedRoute.svelte

Структура вашего проекта должна выглядеть как на картинке. Если вы используете Typescript, преобразуйте все ваши файлы .js в файлы .ts.

Папка ui_components говорит сама за себя, но интересны папки pages и concerns. В папке pages живет каждая «страница» или «экран». Папка concerns предназначена для сквозных компонентов и другого вспомогательного кода. Здесь мы добавим компонент ProtectedRoute и компонент store.

Настройка — Сервер авторизации (OneLogin)

Я использую OneLogin в качестве своего бэкенда для авторизации, так как он бесплатный, простой в использовании, и у них отличное сообщество разработчиков (и я там работаю, но хватит об этом 😶).

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

Предполагая, что вы уже получили свои учетные данные API из своей учетной записи разработчика OneLogin, вы можете использовать их для получения токена доступа к API, который вам понадобится для вызовов создания приложений и пользователей. Я сохранил свой как ONELOGIN_CLIENT_ID и ONELOGIN_CLIENT_SECRET в своей среде.

Если вы используете Mac и у вас установлен jq, вы можете передать это pbcopy, чтобы вставить позже, или опустить | jq '."access_token"' | pbcopy и просто скопировать и вставить в ответ.

curl 'https://api.us.onelogin.com/auth/oauth2/v2/token' \
-X POST \
-H "Authorization: client_id:$ONELOGIN_CLIENT_ID, client_secret:$ONELOGIN_CLIENT_SECRET" \
-H "Content-Type: application/json" \
-d '{
  "grant_type":"client_credentials"
}' | jq '."access_token"' | pbcopy

Создайте приложение — так ваше приложение Svelte будет представлено на сервере аутентификации OneLogin для аутентификации. Обратите внимание на redirect_uri . Прежде чем перейти к производству, убедитесь, что вы обновили их в своем приложении, чтобы они отражали рабочий домен, в противном случае для локальной разработки это будет работать.

Для приложений OIDC, которые необходимы для этого процесса, убедитесь, что connector_id равно 108419, а token_endpoint_auth_method установлено на 2. для PKCE, как показано в cURL.

curl --location --request POST 'https://api.us.onelogin.com/api/2/apps' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <access_token>' \
--data-raw '{
  "connector_id": 108419,
  "name": "Legend of Something",
  "description": "Svelte Demo",
  "visible": true,
  "configuration": {
    "redirect_uri": "http://localhost:3000\nhttps://localhost:3000",
    "token_endpoint_auth_method": "2",
    "oidc_application_type": "0"
  }
}'

Создайте пользователя — мы не делаем регистрацию частью этого, поэтому мы предварительно заполним ее сейчас.

curl --location --request POST 'https://api.us.onelogin.com/api/2/users' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <access_token>' \
--data-raw '{
  "email": "[email protected]", 
  "password": "123asdf!", 
  "password_confirmation": "123asdf!"
}'

Найдите нового пользователя на портале — внесите любые последние изменения в его имя пользователя, пароль и т. д., которые вам нравятся, а затем под Applications на правой панели добавьте приложение Legend of Something к доступным приложениям пользователя, нажав кнопку +, чтобы открыть меню.

Настройка — Маршруты

в App.Svelte мы добавим наши маршруты. Вы также можете определить routes.js и импортировать его, но для проекта с двумя маршрутами это работает совершенно нормально.

App.Svelte очень прост и лишен логики — это просто Router и тупая панель навигации. У каждого маршрута есть path, который является путем, по которому вы попадаете на component, то есть страницу, на которую вы смотрите. У нас есть страница Home и страница SecretStuff. Последний маршрут к /* должен вернуть нас домой, когда мы попытаемся перейти к /asdf или чему-то еще в строке поиска нашего браузера. Обратите внимание, что компонент SecretStuff находится в определяемом нами пользовательском компоненте ProtectedRoute.

Защищенные маршруты с PKCE

ProtectedRoute звезда нашего сегодняшнего шоу — сквозной компонент, который «оборачивает» маршрут и либо позволяет перейти на защищенную страницу, либо возвращает обратно на Login страницу.

При этом используется поток аутентификации PKCE — поток OAuth, который теперь используется для одностраничных приложений и заменяет неявный поток. Преимущество PKCE заключается в том, что приложение выполняет дополнительный шаг по установлению значения code_verification и механизма с сервером аутентификации, поэтому после перенаправления (которое добавляет code в качестве параметра запроса, который можно изменить) приложение может отправить значение code_verification и как пока он хэширует значение, отправленное на сервер аутентификации перед перенаправлением, вы готовы к работе. Для подробного объяснения PKCE и того, как его реализует OneLogin SDK, есть отличный пост здесь.

Компонент ProtectedRoute работает следующим образом:

  1. Установите соединение с клиентом OneLogin с вашими приложениями OIDC clientID и redirectURL.
  2. Создайте authURL, который является URL-адресом, по которому пользователь переходит на страницу входа.
  3. Если мы возвращаемся с сервера авторизации (мы только что завершили вход), в нашем URL должно быть ?code=something. Когда это происходит, мы берем его и используем для запроса access_token, что указывает на то, что мы правильно ввели свои учетные данные.

Все, что связано с PKCE code_verifier, выполняется «под капотом», поэтому об этом не нужно беспокоиться, но опять же, это просто добавляет дополнительную уверенность в том, что приложение, отправляющее code для получения access_token, является тем же приложением, которое запустило процесс аутентификации.

С ProtectedRoute настройкой и обработкой нашего состояния аутентификации добавление компонента Login невероятно просто.

// src/pages/Login.svelte
<script lang="ts">
  import { authURL } from '../concerns/stores';
  import Button from '../ui_components/Button.svelte';
  import { navigate } from 'svelte-routing';
</script>
<h2>This door is locked</h2>
<Button on:message={() => navigate($authURL)} label="🔐 unlock"/>

Мы используем svelte-stores для обработки токена доступа и URL-адреса авторизации. Когда приложение загружается, мы сначала пытаемся извлечь данные из localStorage, в противном случае мы обрабатываем такие данные в памяти и «сохраняем» их в localStorage, чтобы выдержать обновления.

// src/concerns/store.ts
import { writable } from 'svelte/store';
export const accessToken = writable(localStorage.getItem('accessToken'));
export const authURL = writable(localStorage.getItem('auth-url'));

⚠️ Если вы решите использовать localStorage, убедитесь, что ваше приложение защищено от атак межсайтового скриптинга. Любой, кто может запускать произвольный javascript на вашей странице, может добавить код, который сбрасывает localStorage и отправляет его на какой-либо другой сервер, поэтому санируйте ваши входные данные, и функция javascript eval должна вас напугать ⚠️

"Готовый продукт

Если все пойдет хорошо, вы должны увидеть, как ваши страницы начинают появляться. Я не тратил на это много времени, поэтому мы просто получили внешний вид Svelte по умолчанию — я же говорил вам, что это было несложно…

Дом довольно прост — это начало нашего приключения. Обратите внимание на дерьмовую панель навигации вверху.

Если вы хотите увидеть секретную комнату, вам понадобится ключ (пароль)

Кнопка 🔐 Разблокировать на самом деле просто перенаправление на сервер авторизации. Щелчок по ней отправляет нас на страницу входа OneLogin, где мы вводим свои учетные данные. Изучив URL-адрес, вы увидите, что он содержит наш хешированный code_verifier и алгоритм хеширования, поэтому вы знаете, что PKCE SDK выполняет свою работу.

Наконец, если вам удастся войти в систему, вы наткнетесь на секретную комнату со всеми «деньгами» (это моя игровая валюта). Вы можете обновить или перейти домой сколько угодно, и вы все равно сможете попасть в секретную комнату, потому что access_token кэшируется.

Нажав кнопку «Выйти», вы сбросите access_token, и вы попадете домой, и вам придется снова войти в систему.

Подведение итогов

И вуаля! Быстрые и грязные защищенные маршруты в Svelte, которые были реализованы менее чем в 50 строках кода благодаря PKCE и OneLogin. Я буду первым, кто столкнется с тем фактом, что он не идеален, поскольку и Svelte, и OneLogin — относительно новые дети в этом блоке (Svelte — новый фреймворк, а OneLogin — прорыв в фокусе разработчиков), и мы улучшаем все время.

Если вы видите что-то, что могло бы быть лучше в этом, я буду рад вашим советам и предложениям, а OneLogin SDK является открытым исходным кодом, поэтому приветствуются PR и вопросы.