Создание демонстрационной страницы SvelteKit с веб-компонентом и паролем для входа на сайт passkeys.eu

1. Введение

В этом сообщении блога мы рассмотрим процесс создания демонстрационной страницы для passkeys.eu с использованием SvelteKit. Мы расскажем, как создать многоразовый веб-компонент и реализовать функцию входа в систему с паролем для беспрепятственного взаимодействия с пользователем. В этом руководстве предполагается базовое знакомство со SvelteKit, HTML, CSS и JavaScript. Давайте погрузимся!

2. Настройка проекта SvelteKit

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

Мы будем использовать Flowbite, библиотеку компонентов, основанную на TailwindCSS, для быстрого создания адаптивного и эстетичного пользовательского интерфейса.
Давайте начнем с установки SvelteKit. Мы собираемся использовать pnpm вместо npm, так как pnpm быстрее благодаря использованию символических ссылок для повторного использования существующих зависимостей. Чтобы установить pnpm, запустите:

npm install -g pnpm

Сделав это, мы можем установить SvelteKit и инициализировать наш проект:

pnpm create svelte@latest passkeys-demo
cd passkeys-demo
pnpm install

Затем нам нужно установить TailwindCSS:

pnpx svelte-add@latest tailwindcss
pnpm i

Наконец, нам нужно установить библиотеку Flowbite svelte.

pnpm i flowbite flowbite-svelte classnames @popperjs/core

Наконец, мы обновим наш tailwind.config.cjs, включив в него Flowbite:

const config = {
    content: [
        './src/**/*.{html,js,svelte,ts}',
        './node_modules/flowbite-svelte/**/*.{html,js,svelte,ts}'
    ],
    theme: {
        extend: {}
    },
    plugins: [require('flowbite/plugin')],
    darkMode: 'class'
};
module.exports = config;

3. Настройка шрифтов и глобальных стилей с помощью Tailwind

Чтобы настроить некоторые стандартные шрифты и стили, которые должны быть глобальными для нашего приложения, мы можем использовать тему Tailwind. Во-первых, мы создадим раздел <svelte:head> в нашем корне +page.svelte. Это ведет себя как стандартный HTML <head>, который вы найдете в файле index.html, он содержит метаданные и позволяет включать сторонние скрипты. Мы собираемся использовать его, чтобы включить некоторые шрифты из Bunny Fonts и добавить некоторые метаданные для SEO. Добавьте это в свой +page.svelte:

<svelte:head>
    <link rel="preconnect" href="https://fonts.bunny.net" />
    <link href="https://fonts.bunny.net/css?family=inter:500|space-grotesk:500" rel="stylesheet" />
    <title>Passkeys demo</title>
    <meta name="description" content="Corbado Passkey passwordless authentication demo" />
</svelte:head>

4. Структура репозитория

Давайте сначала обсудим структуру нашей папки src:

├── lib
│  ├── assets
│  │  ├── apple.svg
│  │  ├── auth-sample.svg
│  │  ├── Corbado.png
│  │  ├── devices.svg
│  │  ├── europe.png
│  │  ├── github.png
│  │  ├── google.svg
│  │  ├── microsoft.svg
│  │  ├── slack.webp
│  │  └── twitter.png
│  ├── components
│  │  ├── CheckItem.svelte
│  │  └── InfoCard.svelte
│  └── sections
│     ├── Footer.svelte
│     ├── PasskeyDemo.svelte
│     ├── PasskeyExplainer.svelte
│     ├── PasskeySaas.svelte
│     ├── PasskeyStats.svelte
│     └── PasskeyTimeline.svelte
├── routes
│  ├── api
│  │  ├── logout
│  │  │  └── +server.ts
│  │  └── register
│  │     └── +server.ts
│  ├── +layout.svelte
│  ├── +page.server.ts
│  └── +page.svelte
├── app.d.ts
├── app.html
└── app.postcss
 static
└── favicon.png

lib содержит вспомогательные файлы, такие как:

  • изображение assets
  • на заказ components мы сделали
  • sections, который мы используем на нашей демо-странице

routes содержит все маршруты нашего приложения, такие как

  • api, они эквивалентны конечным точкам API, которые вы бы имели на классическом сервере/бэкенде. Путь к конечным точкам соответствует их пути к папке.
  • Корневой путь к / стройным файлам:
  • +layout.svelte определяет общий макет и применяет наш глобальный .postcss
  • +page.server.ts обрабатывает логику на стороне сервера для этого конкретного маршрута (в данном случае /), например функции загрузки или действия формы.
  • +page.svelte содержит фактический клиентский код для отображения нашей страницы.

У нас есть корневые файлы .html и .css, которые мы можем игнорировать для целей нашего руководства. И папка static для ресурсов, таких как фавиконы.

5. Настройка веб-компонента Corbado для аутентификации по паролю

Наша демонстрационная страница содержит различные разделы, определенные в нашем файле +page.svelte:

<script lang="ts">
    import Footer from '$lib/sections/Footer.svelte';
    import PasskeyDemo from '$lib/sections/PasskeyDemo.svelte';
    import PasskeyExplainer from '$lib/sections/PasskeyExplainer.svelte';
    import PasskeySaas from '$lib/sections/PasskeySaas.svelte';
    import PasskeyStats from '$lib/sections/PasskeyStats.svelte';
    import PasskeyTimeline from '$lib/sections/PasskeyTimeline.svelte';
    import type { PageData } from './$types';
    export let data: PageData;
</script>

<svelte:head>
    <link rel="preconnect" href="https://fonts.bunny.net" />
    <link href="https://fonts.bunny.net/css?family=inter:500|space-grotesk:500" rel="stylesheet" />
    <title>Corbado Passkey demo</title>
    <meta name="description" content="Corbado Passkey passwordless authentication demo" />
</svelte:head>
<PasskeyDemo {data} />
<PasskeyExplainer />
<PasskeyTimeline />
<PasskeyStats />
<PasskeySaas />
<footer />

В этой демонстрации мы сосредоточимся на интеграции Corbado, поэтому я не буду рассказывать о том, как я оформлял и создавал эти страницы. Если вам интересно, проверьте папки src/lib/sections и src/lib/components в репозитории проекта, чтобы увидеть, как они оформлены.

5.1 Настройка вашей учетной записи и проекта Corbado

Посетите Панель разработчика Corbado, чтобы зарегистрироваться и создать свою учетную запись (вы увидите пароли при регистрации в действии!).
Когда вы окажетесь в панели разработчика, перейдите в Настройки > Учетные данные:

  • Нажмите Add New.

Здесь добавьте новый авторизованный источник, чтобы ваше локальное приложение Svelte могло отправлять запросы к Corbado API с помощью веб-компонента:

  • Введите описание имени, например «Демонстрация пароля»
  • Введите URL-адрес источника, в нашем случае локальный URL-адрес http://localhost:5137.

Когда вы закончите, вам нужно будет получить идентификатор вашего проекта и создать секрет API для аутентификации с помощью бэкэнда Corbado. Перейдите на вкладку API secrets.

Скопируйте имя пользователя, щелкнув по нему, и нажмите «Добавить новый», чтобы получить новый секрет API, который вы можете скопировать. Мы добавим их в файл .env в корне нашего проекта SvelteKit:

CORBADO_API_SECRET=****
PUBLIC_CORBADO_PROJECT_ID=***

SvelteKit позволяет нам легко получить к ним доступ, используя пространство имен $env для их импорта. Переменные Env с префиксом PUBLIC_ доступны как в клиентских, так и в серверных маршрутах, в то время как те, которые по умолчанию не являются секретными, доступны только в серверных маршрутах SvelteKit.

5.2 Включение веб-компонента в наш интерфейс

Первый раздел в нашем корне +page.svelte — это PasskeysDemo. Этот раздел содержит демонстрацию нашего веб-компонента. Чтобы импортировать веб-компонент Corbado в наше приложение, все, что нам нужно сделать, это добавить svelte:head для загрузки скрипта в src/lib/sections/PasskeyDemo.svelte:

<svelte:head>
    <script defer src="https://auth.Corbado.com/auth.js"></script>
</svelte:head>

Затем мы можем включить его:

<Corbado-auth
    style="border: none"
    project_id="{PUBLIC_CORBADO_PROJECT_ID}"
    conditional="yes"
    login_title="Try passkey login"
    login_btn="Passkey login"
    register_title="Try passkey signup"
    register_btn="Passkey signup"
    auto_detect_language="no"
    fallback_language="en"
    page="register"
>
    <input name="username" id="Corbado-username" value="" required autocomplete="webauthn" />
</Corbado-auth>

Веб-компонент имеет различные служебные атрибуты для настройки, например login_title или login_btn. Что важно для вас, чтобы заставить его работать, так это атрибут project_id, куда мы передадим наш PUBLIC_CORBADO_PROJECT_ID из среды. Если вы используете редактор, такой как VSCode, с расширением Svelte, он автоматически добавит этот импорт в ваш тег .svelte <script>:

import { PUBLIC_CORBADO_PROJECT_ID } from '$env/static/public';

5.3 Настройка логики перенаправления

Веб-компонент Corbado работает, перенаправляя вас на указанную вами страницу после того, как пользователь зарегистрируется или войдет в систему. Этот URL-адрес перенаправления может быть маршрутом на стороне сервера или маршрутом клиента. В нашем случае это будет конечная точка сервера SvelteKit. Чтобы настроить наш URL-адрес перенаправления, давайте зайдем в Панель разработчика Corbado, перейдем на вкладку URL-адрес в Настройки> Общие и введите следующее:

Redirect URL: введите URL-адрес, на который должно перенаправляться ваше приложение после регистрации в веб-компоненте. В нашем случае это маршрут API SvelteKit http://localhost:5137/api/register.

Application URL: введите URL-адрес, по которому работает ваше приложение, в нашем случае это localhost:5137 локально.

«Идентификатор проверяющей стороны»: введите домен (без протокола, порта или пути), к которому должны быть привязаны пароли. В нашем случае это «localhost».

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

Давайте теперь добавим маршрут нашего сервера в SvelteKit. Под папкой routes/ создадим папки api/register и создадим в них файл api/register/+server.ts. Это будет содержать наш маршрут API:

import { CORBADO_API_SECRET } from '$env/static/private';
import { PUBLIC_CORBADO_PROJECT_ID } from '$env/static/public';
import { error, redirect, type RequestHandler } from '@sveltejs/kit';

export const GET = (async ({ url, request, getClientAddress, cookies }) => {
    const Corbado = {
        sessionToken: url.searchParams.get('sessionToken') || '',
        userAgent: request.headers.get('user-agent'),
        remoteAddress: getClientAddress()
    };
    const response = await fetch('https://api.Corbado.com/v1/sessions/verify', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
            Authorization: `Basic ${btoa(`${PUBLIC_CORBADO_PROJECT_ID}:${CORBADO_API_SECRET}`)}`
        },
        body: JSON.stringify({
            token: Corbado.sessionToken,
            clientInfo: {
                userAgent: Corbado.userAgent,
                remoteAddress: Corbado.remoteAddress
            }
        })
    });
    if (response.status !== 200) {
        throw error(401, 'Invalid session token');
    }
    cookies.set('jwt', crypto.randomUUID(), {
       httpOnly: true,
       path: '/',
       sameSite: 'lax',
       secure: true,
       maxAge: 60 * 60 * 24 // 1 day
    });
    throw redirect(303, '/');
}) satisfies RequestHandler;

Давайте переварим это один за другим. Этот маршрут GET будет автоматически вызываться Corbado API, как только пользователь нажмет кнопку регистрации или входа в систему. Затем нам нужно проверить эту информацию с помощью API Corbado, а затем мы можем аутентифицировать нашего пользователя локально.

В константе Corbado мы сохраняем всю информацию, относящуюся к проверке, такую как sessionToken и clientinfo, такую как userAgent и remoteAddress. SvelteKit предоставляет полезных помощников, которые мы можем передать маршруту, чтобы получить всю эту информацию.

Получив эту информацию, мы хотим вызвать конечную точку Corbado API sessionVerify. Мы используем наш идентификатор проекта и наш секрет, который мы сохранили в нашем .env ранее, для аутентификации с помощью обычной аутентификации, а в теле мы передаем sessionToken и clientinfo, которые мы собрали.

Если наш ответ не удался, мы позволим нашей конечной точке выдать ошибку. Если мы успешно прошли проверку, теперь мы можем установить файл cookie, используя новый токен jwt, и перенаправить нашего клиента обратно в корень. Обратите внимание, что в рабочей среде вы, вероятно, использовали бы информацию о пользователе, возвращенную вам в ответ на вызов sessionVerify, для создания пользователя в вашей базе данных, но для наших целей мы просто создадим файл cookie локально.

5.4 Использование файлов cookie для управления состоянием аутентификации

Теперь, когда мы можем нажать «Зарегистрироваться» в нашем веб-компоненте и получить обратно файл cookie, нам нужно загрузить этот файл cookie в наш клиент и использовать его для отображения статуса входа. Для этого мы можем использовать функции load SvelteKit. В корне нашей папки routes мы можем создать +page.server.ts рядом с существующим +page.server.ts:

import type { PageServerLoad } from './$types';

export const load = (async ({ cookies }) => {
    const jwt = cookies.get('jwt');
    return {
        jwt
    };
}) satisfies PageServerLoad;

Что это делает, так это получает файл cookie jwt, который мы установили в перенаправлении, и передает его всем страницам на том же уровне routes или ниже.

Ранее, в нашем корне +page.svelte, вы могли заметить, что в нашем скрипте была дополнительная переменная:

export let data: PageData;

Эти данные заполняются тем, что возвращает функция load нашей страницы, в данном случае содержимым нашего файла cookie jwt. Мы хотим передать этот файл cookie в наш раздел PasskeyDemo:

<PasskeyDemo {data} />

В этом разделе нам также нужно объявить свойство data, чтобы получать его с родительской страницы. Это должно быть в нашем разделе скриптов:

export let data: PageData;

Теперь мы можем использовать это для условной визуализации нашего пользовательского интерфейса:

<!--- other code --->
{#if data && data.jwt}
<Card class="w-full">
    <Heading tag="h4">That’s it. You’re logged in.​</Heading>
    <button href="/api/logout" pill class="bg-primary text-white mt-32 mb-8">Log out</button>
</Card>
{:else}
<Card class="w-full">
    <Corbado-auth
        style="border: none"
        project_id="{PUBLIC_CORBADO_PROJECT_ID}"
        conditional="yes"
        login_title="Try passkey login"
        login_btn="Passkey login"
        register_title="Try passkey signup"
        register_btn="Passkey signup"
        page="register"
    >
        <input name="username" id="Corbado-username" value="" required autocomplete="webauthn" />
    </Corbado-auth>
</Card>
{/if}
<!--- other code --->

Это отобразит либо наш веб-компонент, если мы не вошли в систему, либо кнопку выхода, если мы уже вошли в систему. Кнопка выхода использует другое действие. Мы можем создать это действие, просто создав файл api/logout/+server.ts со следующим содержимым:

import { redirect } from '@sveltejs/kit';
import type { RequestHandler } from './$types';

export const GET = (({ cookies }) => {
    cookies.set('jwt', '', {
        path: '/',
        expires: new Date(0)
    });
    throw redirect(303, '/');
}) satisfies RequestHandler;

Это действие будет вызвано при нажатии кнопки и удалит токен jwt из браузера и перенаправит обратно в корень страницы.

6. Заключение

Я надеюсь, что эта демонстрация показала, как легко быстро добавить аутентификацию без пароля с использованием ключей доступа в ваше приложение SvelteKit с помощью Corbado и использовать все функции SvelteKit, чтобы облегчить нашу жизнь в этом процессе.