Добавление потока аутентификации с использованием SuperToken в приложение React и Hasura GraphQL

Добавление потока аутентификации с использованием SuperToken в приложение React и Hasura GraphQL

30 марта 2022 г.

В этом руководстве показано, как разработать безопасный магазин электронной коммерции с использованием аутентификации SuperTokens в приложении React.js.


Мы будем использовать современный стек, включающий React, Hasura GraphQL и SuperTokens.


Исходный код приложения, над которым мы работаем, доступен для просмотра [здесь] (https://github.com/tyaga001/SuperTokens-Hasura-Demo).


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


Использование SuperToken для аутентификации конечной точки Hasura


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


Вы будете интегрировать SuperTokens с Hasura. Токены, сгенерированные из SuperToken, будут отправлены со стороны пользовательского интерфейса в заголовках запросов в Hasura, где они будут проверены.


Что такое супертокены?


SuperTokens — это альтернатива AuthO с открытым исходным кодом, которая позволяет настроить аутентификацию менее чем за 30 минут.


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


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


Зачем использовать SuperToken?


SuperTokens — это альтернатива с открытым исходным кодом со следующими функциями:


  • SuperTokens имеет открытый исходный код, что означает, что его можно использовать бесплатно, неограниченное количество раз, без ограничений по количеству пользователей.

  • Локальное развертывание, которое дает вам полный контроль над вашими пользовательскими данными за счет использования вашей базы данных.

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

  • Простота использования и повышенная безопасность.

  • Настраиваемый: любой может внести свой вклад в улучшение SuperTokens!

Что такое Хасура?


  • Hasura позволяет создавать GraphQL API в режиме реального времени для вашего приложения без написания какого-либо внутреннего кода.

  • Hasura — это механизм GraphQL, который преобразует вашу базу данных в мгновенный GraphQL API в режиме реального времени.

  • Вы также можете использовать удаленные схемы и действия для интеграции собственных API-интерфейсов GraphQL в Hasura.

  • Hasura — это система, основанная на разрешениях.

TL;DR


Вот ссылки для быстрого доступа к исходному коду или получения дополнительной информации об обоих продуктах.


  • [Исходный код] (https://github.com/tyaga001/SuperTokens-Hasura-Demo)


  • [Хасура] (https://hasura.io/)

  • [ngrok] (https://ngrok.com/download)

Давайте начнем


Для начала создайте новое приложение React.js:


```javascript


npx создать-реагировать-приложение мое-приложение


cd мое приложение


запуск нпм


Для реализации аутентификации SuperTokens у нас есть два варианта.


  • Неограниченное количество пользователей, собственный хостинг и бесплатно на всю жизнь

  • Бесплатно до 5 тысяч активных пользователей в месяц на SaaS (размещенных SuperTokens). После этого 29 долларов в месяц за каждые 5 тыс. пользователей (до 50 тыс. MAU)

Pricing.JPG


Создайте управляемую службу с помощью SuperTokens


Чтобы создать управляемую службу SuperTokens, нажмите синюю кнопку «Создать приложение», которая приведет вас на страницу создания учетной записи. Затем, следуя инструкциям, вы сможете выбрать регион доступности для управляемой службы.


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


изображение(2).png


Настройка облака Hasura


Хасуру можно использовать двумя способами. Вы можете использовать его локально с Docker или в облаке Hasura. Облако Hasura используется на протяжении всего руководства.


Если вы новичок в Hasura, вам необходимо создать учетную запись и проект. Если вы будете следовать этому [руководству] (https://hasura.io/docs/latest/graphql/cloud/getting-started/), вы сможете начать работу в кратчайшие сроки.


Вы должны быть на панели управления проектом после настройки учетной записи и проекта.


изображение(3).png


изображение(4).png


Создание/импорт базы данных в Hasura


Первым шагом является подключение базы данных к Hasura. Затем выберите параметр «Подключить базу данных», как показано на изображении ниже. Это приведет вас на страницу базы данных, где вы сможете подключиться к существующей базе данных или создать ее с нуля.


изображение(5).png


изображение(6).png


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


После подключения к базе данных вы можете получить к ней доступ, нажав «общедоступная», как показано на рисунке ниже.


изображение(7).png


В этом блоге я подключаю Hasura к облачной базе данных SuperTokens, но в рабочей среде SuperTokens не позволяет использовать базу данных, поэтому база данных должна быть отдельным объектом, что является рекомендуемым подходом. Я использую его только для этого демонстрационного приложения.


Использование Hasura для создания таблиц


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


На этом шаге вы создадите еще несколько таблиц:


  • пользовательская_корзина

  • продукты

  • список желаний пользователя

  • торговцы

  • заказы

  • категории

drawSQL-export-2022-03-15_23_20.png


Управление разрешениями в Hasura


Hasura позволяет вам определять правила контроля доступа на трех разных уровнях:


Примерами уровней являются уровень таблицы, уровень действия и уровень роли.


В этом приложении используются примеры на уровне ролей.


Подробные инструкции можно найти в документации ссылка


SuperTokens Frontend.init()


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


Мы будем использовать готовый рецепт **EmailPassword ** для доступа к демонстрационному приложению SuperTokens.


Давайте добавим следующий блок кода в начало файла index.tsx, чтобы инициализировать клиент Supertokens в приложении React.


```машинопись


импортировать React из «реагировать»;


импортировать ReactDOM из 'react-dom';


импортировать './index.css';


импортировать SuperTokens из 'supertokens-auth-react';


импортировать Session, { SessionAuth } из 'supertokens-auth-react/recipe/session';


импортировать {BrowserRouter} из 'react-router-dom';


импортировать EmailPassword из 'supertokens-auth-react/recipe/emailpassword';


импортировать {getApiDomain, getWebsiteDomain} из './utils/utils';


импортировать приложение из './App';


импортировать reportWebVitals из './reportWebVitals';


SuperTokens.init({


информация о приложении: {


appName: 'Приложение для покупок',


apiDomain: получитьApiDomain(),


веб-сайтДомен: getWebsiteDomain(),


Список рецептов: [


EmailPassword.init({


getRedirectionURL: асинхронный (контекст) => {


если (context.action === 'УСПЕХ') {


вернуться домой';


вернуть неопределенное значение;


emailVerificationFeature: {


режим: 'ОБЯЗАТЕЛЬНЫЙ',


Сессия.init(),


Доступ к нашему контексту сеанса можно получить после обертывания компонента App() оболочкой EmailPaswordAuth.


Супертокены Backend.init()


SuperTokens заботится о многих вещах для вас и абстрагирует их. При вызове supertokens.init мы должны указать значение переопределения конфигурации, чтобы переопределить реализацию по умолчанию. Каждый рецепт в recipeList имеет переопределяющую конфигурацию, которая может использоваться для изменения его поведения.


```машинопись


supertokens.init({


рамки: «экспресс»,


супертокены: {


URI соединения: process.env.API_TOKENS_URL,


APIKey: процесс.env.API_KEY,


информация о приложении: {


appName: 'Демонстрационное приложение SuperTokens',


апидомен,


сайтДомен,


recipeList: [EmailPassword.init({}), Session.init({


джвт: {


включить: правда,


  • Это пример URL-адреса, который ngrok генерирует, когда

  • вы открываете локальный хост в Интернете

эмитент: process.env.API_JWT_URL,


Узнайте, как настроить API SuperTokens здесь.


Архитектура управляемых сервисов SuperToken


Схема архитектуры версии управляемых сервисов SuperTokens 👇


image.png


Пример взаимодействия трех компонентов для процесса входа и выхода (с использованием электронной почты и пароля) показан ниже.


image.png


Интеграция SuperTokens с Hasura


URL-адрес эмитента токена необходимо добавить в переменные окружения Hasura, чтобы интегрировать SuperTokens с Hasura. Поскольку мы будем звонить на конечную точку Hasura из нашей локальной сети, нам нужно будет открыть ее для Интернета. Для этого мы будем использовать ng-rock, и нам также потребуется включить JWT в SuperTokens.


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


ng rock.JPG


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


[Настройте переменные среды Hasura] (https://supertokens.com/docs/ ThirdPartyemailpassword/hasura-integration/with-jwt#4-configure-hasura-environment-variables)


Переменные env Hasura в облаке настраиваются с помощью URL-адресов JWT SuperTokens, как показано на диаграмме ниже.


```машинопись


recipeList: [EmailPassword.init({}), Session.init({


джвт: {


включить: правда,


  • Это пример URL-адреса, который ngrok генерирует, когда

  • вы открываете локальный хост в Интернете

эмитент: process.env.API_JWT_URL,


```машинопись


REACT_APP_API_PORT=3002


REACT_APP_API_GRAPHQL_URL=https://supertokens.hasura.app/v1/graphql


API_KEY=SSugiN8EMGZv=fL33=yJbycgI7UmSd


API_TOKENS_URL=https://0def13719ed411ecb83cf5e5275e2536-ap-southeast-1.aws.supertokens.io:3568


API_JWT_URL=http://ec87-223-185-12-185.ngrok.io/auth


env.JPG


update.JPG


Для отправки утверждений Hasura JWT в токене, сгенерированном SuperTokens


Нам нужно поделиться информацией, связанной с ролью пользователя, с Hasura для разрешения на основе роли. Это можно сделать в SuperTokens, переопределив существующий токен, как показано в приведенном ниже фрагменте кода.


```машинопись


переопределить: {


функции (исходная реализация) {


вернуть {


...оригинальная реализация,


асинхронный createNewSession (sessionInput) {


константный ввод = сеансовый ввод;


input.accessTokenPayload = {


... input.accessTokenPayload,


'https://hasura.io/jwt/claims': {


'x-hasura-user-id': input.userId,


'x-hasura-default-role': 'пользователь',


'x-hasura-allowed-roles': ['пользователь', 'анонимный', 'администратор'],


вернуть originalImplementation.createNewSession (вход);


Hasura проверит авторизацию, используя заголовки, перечисленные ниже.


x-hasura-идентификатор пользователя


x-hasura-роль по умолчанию


x-hasura-разрешенные роли


Как вы используете конечную точку Hasura в пользовательском интерфейсе?


Мы используем пакет npm apollo/client для доступа к конечной точке Hasura GrpahQL.


Добавление аполлона/клиента в наше приложение:


```машинопись


импортировать React из «реагировать»;


импортировать './App.scss';


импортировать { useSessionContext } из 'supertokens-auth-react/recipe/session';


импорт {


АполлонКлиент,


InMemoryCache,


АполлоПровайдер,


} из '@apollo/client';


импортировать AppRoutes из './shared/components/routes/AppRoutes';


приложение функции () {


const { accessTokenPayload } = useSessionContext();


постоянный клиент = новый ApolloClient({


URI: ${process.env.REACT_APP_API_GRAPHQL_URL},


кеш: новый InMemoryCache(),


заголовки: {


Авторизация: Bearer ${accessTokenPayload?.jwt},


«Тип контента»: «приложение/json»,


вернуть (







экспортировать приложение по умолчанию;


Мы отправляем токен, сгенерированный SuperTokens при авторизации: Bearer $accessTokenPayload?.jwt


Давайте посмотрим на все зависимости проекта, которые использовались при разработке этого приложения.


```машинопись


"зависимости": {


"@аполло/клиент": "^3.5.9",


"@эмоция/реагировать": "^11.8.1",


"@emotion/styled": "^11.8.1",


"@material-ui/icons": "^4.11.2",


"@mui/icons-material": "^5.4.4",


"@mui/lab": "^5.0.0-альфа.72",


"@mui/материал": "^5.4.3",


"@mui/styles": "^5.4.4",


"@testing-library/jest-dom": "^5.16.2",


"@testing-library/реагировать": "^12.1.3",


"@testing-library/user-event": "^13.5.0",


"@типы/экспресс": "^4.17.13",


"@types/jest": "^27.4.0",


"@типы/узел": "^16.11.25",


"@types/реагировать": "^17.0.39",


"@types/react-dom": "^17.0.11",


"аксиос": "^0.26.0",


"парсер тела": "^1.19.2",


"Корс": "^ 2.8.5",


"дотенв": "^16.0.0",


"graphql": "^16.3.0",


"шлем": "^5.0.2",


"морган": "^1.10.0",


"узел": "^2.0.15",


"npm-run-all": "^4.1.5",


"красивее": "^2.5.1",


"реагировать": "^17.0.2",


"реагировать-дом": "^ 17.0.2",


"реакция-маршрутизатор-дом": "^ 6.2.1",


"реагировать-скрипты": "5.0.0",


"дерзкий": "^ 1.49.8",


"супертокены-аутентификация-реагировать": "^0.18.7",


"supertokens-node": "^9.0.0",


"машинопись": "^4.5.5",


"веб-виталы": "^2.1.4"


Давайте поговорим о компонентах React, которые мы создали для приложения электронной коммерции.


Структура папок в Visual Studio Code выглядит так:


папка.JPG


Создайте компонент списка товаров (ProductList.tsx)


Этот компонент отображает список всех продуктов.


```машинопись


импортировать React из «реагировать»;


импортировать коробку из '@mui/material/Box';


импортировать сетку из '@mui/material/Grid';


импорт {


использование запроса,


gql,


} из '@apollo/client';


импортировать скелет из '@mui/material/Skeleton';


импортировать Карту из '@mui/material/Card';


импортировать ProductItem из '../product-item/ProductItem';


импортировать {Product} из '../models/Product';


импортировать useToast из '../../hooks/useToast';


const PRODUCT_LIST = gql`query{товары {идентификатор категории_идентификатор продавца_идентификатор цены product_img_url статус}user_whishlist {


идантификационный номер продукта


функция Список Продуктов () {


const { загрузка, ошибка, данные } = useQuery (PRODUCT_LIST);


const { addToast } = useToast();


если (ошибка) {


addToast('Невозможно загрузить.....');


вернуть ноль;


вернуть (



<Интервал контейнера сетки={6}>


!загрузка ? data?.products?.map((product: Product) => (


<Элемент сетки xs={3}>


<ТоварЭлемент


productData={продукт}


Wishlisted={данные?.user_whishlist


.some((item: any) => item.product_id === product.id)}



)) :(


<Элемент сетки xs={3}>










экспортировать ProductList по умолчанию;


Создайте компонент сведений о продукте (ProductDetails.tsx)


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


```машинопись


/ eslint-disable no-unused-vars /


импортировать React, {useEffect, useRef, useState} из 'реагировать';


импортировать коробку из '@mui/material/Box';


импортировать сетку из '@mui/material/Grid';


импортировать Карту из '@mui/material/Card';


импортировать CardHeader из '@mui/material/CardHeader';


импортировать CardMedia из '@mui/material/CardMedia';


импортировать { makeStyles } из '@mui/styles';


импортировать значок AddShoppingCart из '@mui/icons-material/AddShoppingCart';


импорт {


использование запроса,


gql,


использованиеМутация,


} из '@apollo/client';


импортировать CardActions из '@mui/material/CardActions';


импортировать LoadingButton из '@mui/lab/LoadingButton';


импортировать скелет из '@mui/material/Skeleton';


импортировать CardContent из '@mui/material/CardContent';


импортировать типографику из '@mui/material/Typography';


импортировать CurrencyRupeeIcon из '@mui/icons-material/CurrencyRupee';


import {useParams, useNavigate} из 'react-router-dom';


импортировать ProductSpecifications из '../product-specifications/ProductSpecifications';


const FETCH_PRODUCT = gql`запрос getProduct($pid: Int!) {


продукты (где: {id: {_eq: $pid}}) {


ид_категории


я бы


идентификатор продавца


имя


цена


product_img_url


положение дел


описания


user_cart(где: {product_id: {_eq: $pid}}) {


идантификационный номер продукта


const ADD_TO_CART = gql`mutation addToCart($pid: Int!, $price: Int!) {


insert_user_cart_one (объект: {product_id: $pid, цена: $price}) {


идантификационный номер продукта


const useStyles: any = makeStyles(() => ({


Изображение продукта: {


высота: «416 пикселей»,


ширина: «200 пикселей»,


marginLeft: 'авто',


marginRight: 'авто',


отступ: '10px',


addtoCartBtn: {


backgroundColor: '#ff9f00',


fontWeight: 'жирный',


размер шрифта: '16px',


купитьNowBtn: {


backgroundColor: '#fb641b',


fontWeight: 'жирный',


размер шрифта: '16px',


текстСлева: {


textAlign: 'левый',


предложение Заголовок: {


размер шрифта: '16px',


fontWeight: '500',


цвет: '# 212121',


textAlign: 'левый',


список предложений: {


textAlign: 'левый',


высота строки: '1.43',


paddingLeft: '0',


спецзаголовок: {


размер шрифта: '24px',


fontWeight: '500',


высота линии: '1.14',


textAlign: 'левый',


цвет: '# 212121',


картаВраппер: {


отступ: '20px',


валютаTxt: {


размер шрифта: '28px',


textAlign: 'левый',


fontWeight: 'жирный',


предложениеИзображение: {


высота: '18px',


ширина: «18 пикселей»,


должность: «родственник»,


сверху: '6px',


marginRight: '10px',


предложениеListWrapper: {


стиль списка: 'нет',


pb0: {


paddingBottom: '0',


иконка: {


должность: «родственник»,


сверху: '5px',


fontWeight: 'жирный',


размер шрифта: '28px',


действия карты: {


дисплей: «гибкий»,


justifyContent: 'центр',


карточка товара: {


курсор: 'указатель',


функция экспорта по умолчанию ProductDetails() {


const {pid} = useParams();


const { загрузка, данные, ошибка } = useQuery (FETCH_PRODUCT, {


переменные: {


пид,


константа [добавить в корзину, {


загрузка: AddLoader,


данные: AddData, ошибка: AddError,


}] = useMutation (ADD_TO_CART);


const product = data?.products[0];


const [addToCartLoader, setAddToCartLoader] = useState (false);


константные классы = useStyles();


const [cartBtnTxt, setCartBtnTxt] = useState('ДОБАВИТЬ В КОРЗИНУ');


константная навигация = useNavigate();


использоватьЭффект(() => {


setCartBtnTxt (данные?.user_cart.length > 0? «ПЕРЕХОДИТЬ В КОРЗИНУ»: «ДОБАВИТЬ В КОРЗИНУ»);


}, [данные]);


константа addToCartHandler = асинхронная () => {


если (данные?.user_cart.length > 0) {


перейти('/корзина');


} еще {


setCartBtnTxt('ИДЕТ В КОРЗИНУ');


установитьAddToCartLoader (истина);


ждать addToCart({


переменные: {


пид,


цена: продукт.цена,


перейти('/корзина');


вернуть (



<Интервал контейнера сетки={6}>


<Элемент сетки xs={4}>



{!загружается ? (


<CardMedia


className={классы.productImg}


компонент = "изображение"


изображение={product.product_img_url}


alt="Блюдо паэлья"


) : <Скелетная анимация="волна" вариант="прямоугольная" высота="416px" />}



{!загружается ? (


<Кнопка загрузки


вариант = "содержится"


отключитьвысоту


размер = "большой"


загрузка={addToCartLoader}


ЗагрузкаПозиция = "старт"


className={классы.addtoCartBtn}


startIcon={}


onClick={addToCartHandler}


{cartBtnTxt}



<Кнопка загрузки


вариант = "содержится"


отключитьвысоту


размер = "большой"


className={классы.buyNowBtn}


КУПИ СЕЙЧАС



<Скелетная анимация="волна" вариант="прямоугольная" высота="43px" ширина="190px" />


<Скелетная анимация="волна" вариант="прямоугольная" высота="43px" ширина="190px" />





<Элемент сетки xs={8}>


<Карта>


{!загружается ? ${classes.textLeft} ${classes.pb0}} title={product.name} /> : }



{!загружается ? (


<Типография color="text.primary" className={classes.currencyTxt}>



{цена продукта}



{продукт?.описание?.предложения?.длина > 0 && (



Доступные предложения



    product?.descriptions?.offers.map((item: string) => (




  • {пункт}





<дел>


Технические характеристики





) : <Скелетная анимация="волна" вариант="прямоугольная" высота="43px" ширина="190px" />}







Создайте компонент списка корзины (CartList.tsx)


Этот компонент отображает список товаров, которые вы добавили в корзину.


```машинопись


/ eslint-disable no-unused-vars /


импортировать React из «реагировать»;


импортировать коробку из '@mui/material/Box';


импортировать сетку из '@mui/material/Grid';


импортировать Карту из '@mui/material/Card';


импортировать CardHeader из '@mui/material/CardHeader';


импортировать CardContent из '@mui/material/CardContent';


импорт {


использование запроса,


gql,


} из '@apollo/client';


импортировать скелет из '@mui/material/Skeleton';


импортировать кнопку из '@mui/material/Button';


import {useNavigate} из 'react-router-dom';


импортировать CartItem из '../cart-item/CartItem';


импортировать PriceDetails из '../price-details/PriceDetails';


// импортируем CardMedia из '@mui/material/CardMedia';


const PRODUCTS_IN_CART = gql`запрос getProductsInCart {


user_cart {


корзинаТовары {


ид_категории


имя


цена


product_img_url


я бы


цена


скидка


функция экспорта по умолчанию CartList() {


константа {


данные, загрузка, ошибка, обновление,


} = использовать запрос (PRODUCTS_IN_CART);


константная навигация = useNavigate();


const refereshCart = () => {


обновить();


если (! загрузка && data.user_cart.length === 0) {


вернуть (


<Ящик>


<Карта>



<Содержимое карты>



Ваша корзина пуста






вернуть (



<Интервал контейнера сетки={6}>


<Элемент сетки xs={7}>


<Карта>


{!загружается ? (


Моя корзина (${data.user_cart.length})} />



{data.user_cart.map((элемент: любой) => (


<Корзина


refereshCart={refereshCart}


продукт={item.cartProducts}



) : <Скелетная анимация="волна" вариант="прямоугольная" высота="416px" />}




<Элемент сетки xs={5}>


<Карта>


{!загружается ? (





) : <Скелетная анимация="волна" вариант="прямоугольная" высота="416px" />}






Создайте компонент сведений о цене (PriceDetails.tsx)


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


```машинопись


импортировать React из «реагировать»;


импортировать { makeStyles } из '@mui/styles';


const useStyles = makeStyles({


подробностиЗаголовок: {


размер шрифта: '24px',


fontWeight: '500',


textAlign: 'левый',


цвет: '#878787',


borderBottom: '1px сплошной #efefef',


отступ: '16px',


prcieWrapper: {


дисплей: «гибкий»,


ценаКонтент: {


ширина: «50%»,


отступ: '16px',


textAlign: 'левый',


размер шрифта: '22px',


функция экспорта по умолчанию PriceDetails({priceDetails}: {priceDetails: any}) {


константные классы = useStyles();


const total = priceDetails.reduce((предыдущая: любая, текущая: любая) => ({


цена: пред.цена + тек.цена,


скидка: пред.скидка + текущая.скидка,


вернуть (


<дел>



ЦЕНА ДЕТАЛИ




Цена

{total.price}



Скидка


{всего.скидка}





Стоимость доставки

-



Общая сумма


{Число(общая.цена)


  • Количество(общая.скидка)}




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


image.png


Если у вас возникнут проблемы с реализацией рабочего процесса после прочтения этой статьи, не стесняйтесь обращаться ко мне в Twitter или отправлять свои вопросы в SuperTokens Discord.


Заключение


На этом этот блог закончился.


Большое спасибо команде SuperTokens за руководство этим прекрасным проектом аутентификации с открытым исходным кодом и за разработку этой функции интеграции с Hasura.


Окончательный код этого проекта можно найти здесь.


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


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


Дайте мне знать, как вы планируете использовать SuperToken в своем следующем проекте.


Для получения самой актуальной информации следите за SuperTokens в Твиттере.


Также опубликовано здесь



Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE