В этой статье мы расскажем вам о том, как добавить аутентификацию в приложение VueJS с помощью SuperTokens, используя собственный пользовательский интерфейс. Мы создадим собственные формы аутентификации и будем использовать supertokens-web-js, чтобы заставить эти формы работать.
- Что такое SuperTokens?
- Архитектура
- Фронтенд
- Вызовите функцию supertokens-web-js init.
- Создание HTML-шаблона AuthView
- Создание состояния AuthView и функций шаблона
- Настройка маршрутизации для показа форм регистрации/логина
- Интеграция бэкенда
- Управление сессией
- Поток «Забыли пароль
- Настройка ядра SuperTokens
- Заключение
Что такое SuperTokens?
SuperTokens — это проект с открытым исходным кодом, который позволяет быстро добавить аутентификацию в ваше приложение. Он предлагает различные методы аутентификации (называемые рецептами).
Наряду с готовым пользовательским интерфейсом, он также предлагает ванильный JS SDK под названием supertokens-web-js
, который вы можете использовать для создания собственного пользовательского интерфейса. В этом руководстве мы узнаем, как использовать supertokens-web-js
для добавления аутентификации в приложение VueJS. Мы сосредоточимся на пароле электронной почты и социальном логине, но вы можете выбрать и другой метод аутентификации.
Архитектура
SuperTokens состоит из трех компонентов:
-
Frontend SDK
-
Backend SDK
-
Микросервис, который взаимодействует с базой данных (называется SuperTokens Core).
Мы создадим наши собственные формы входа, регистрации и сброса пароля. Затем мы будем использовать SDK supertokens-web-js
в нашем приложении Vue, чтобы сделать эти формы функциональными, вызывая соответствующие функции для каждого действия. Эти функции будут взаимодействовать с API, открытыми через SuperTokens SDK, который интегрирован в ваш слой бэкенда.
Для бэкенда мы будем использовать SDK supertokens-node
. API, предоставляемые этим SDK, будут далее взаимодействовать с SuperTokens Core для чтения/записи информации в базу данных.
Ядро службы SuperTokens может быть размещено самостоятельно (и подключено к вашей собственной базе данных) или размещено командой, создавшей SuperTokens (зарегистрируйтесь на сайте supertokens.com). В этом блоге мы будем использовать бесплатную демо-версию ядра, размещенную на https://try.supertokens.com
.
Фронтенд
Начните с создания нового приложения Vue:
npm init vue@latest
Мы включим Vue Router и Typescript для проекта. Выберите «Да» для них в подсказке:
✔ Project name: … <your-project-name>
...
✔ Add TypeScript? … Yes
✔ Add Vue Router for Single Page Application development? … Yes
...
Scaffolding project in ./<your-project-name>...
Done.
Как только это будет сделано, зайдите в проект и установите следующие зависимости:
npm i supertokens-node supertokens-web-js cors dotenv express npm-run-all
Библиотека supertokens-web-js
будет использоваться на фронтенде для добавления функций аутентификации и сброса пароля в ваш пользовательский UI, а библиотека supertokens-node
будет использоваться на бэкенде для открытия маршрутов auth API.
Вызовите функцию supertokens-web-js
init
.
Мы инициализируем SDK supertokens-web-js
в корневом файле нашего приложения Vue, т.е. /src/main.ts
:
import ThirdPartyEmailPassword from "supertokens-web-js/recipe/thirdpartyemailpassword";
import Session from "supertokens-web-js/recipe/session";
SuperTokens.init({
appInfo: {
appName: "SuperTokens Vue ThirdPartyEmailPassword Example",
apiDomain: "http://localhost:3001",
},
recipeList: [ThirdPartyEmailPassword.init(), Session.init()],
});
В приведенном выше коде функция init
инициализирует supertokens-web-js
на фронтенде. Мы вызываем эту функцию в корневом файле нашего приложения, чтобы мы могли использовать функцию управления сессиями во всем приложении. Она также указывает тип аутентификации, который мы хотим использовать — в нашем случае это социальный логин + пароль электронной почты (рецепт ThirdPartyEmailPassword
).
Создание HTML-шаблона AuthView
Теперь мы начнем с создания HTML-шаблона, который отображает пользовательский интерфейс регистрации и входа. В качестве примера можно привести этот HTML-шаблон.
В файле шаблона вызываются следующие функции для обработки социального входа и входа/регистрации с использованием электронной почты и пароля:
-
onGithubPressed
: Эта функция позволяет пользователям проходить аутентификацию через свой аккаунт на Github. -
onGooglePressed
: Эта функция позволяет пользователям аутентифицироваться через их учетную запись Google. -
onSubmitPressed
: Эта функция запускается, когда пользователь вводит свой email и пароль для регистрации или входа.
Создание состояния AuthView и функций шаблона
Мы отобразим этот HTML-шаблон в компоненте AuthView
внутри /src/views/AuthView.vue
, который также будет содержать реализации для вышеуказанных функций:
<template src="../html/authView.html"></template>
Мы начнем с создания состояний для хранения данных для аутентификации, таких как email, пароль, сообщения об ошибках для нашего шаблона:
// ...
data() {
return {
// we allow the user to switch between sign in and sign up view
isSignIn: true,
// this will store the email and password entered by the user.
email: "",
password: "",
// any generic error states
error: false,
errorMessage: "Something went wrong",
// any error states specific to the input fields.
emailError: "",
passwordError: "",
};
}
Затем мы создадим функцию signIn
, которая будет использовать supertokens-web-js
SDK. Мы передадим в этот метод email и пароль и перенаправим пользователя на маршрут "/"
в случае успешной аутентификации. Эта функция signIn
будет вызвана из функции onSubmitPressed
, если состояние sSignIn
будет true
.
signIn: async function (_: Event) {
const response = await ThirdPartyEmailPassword.emailPasswordSignIn({
formFields: [
{
id: "email",
value: this.email,
},
{
id: "password",
value: this.password,
},
],
});
if (response.status === "WRONG_CREDENTIALS_ERROR") {
// the input email / password combination did not match,
// so we show an appropriate error message to the user
this.errorMessage = "Invalid credentials";
this.error = true;
return;
}
if (response.status === "FIELD_ERROR") {
response.formFields.forEach((item) => {
if (item.id === "email") {
// this means that something was wrong with the entered email.
// probably that it's not a valid email (from a syntax point of view)
this.emailError = item.error;
} else if (item.id === "password") {
this.passwordError = item.error;
}
});
return;
}
// login is successful, and we redirect the user to the home page.
// Note that session cookies are added automatically and nothing needs to be
// done here about them.
window.location.assign("/");
}
Если поле status
в теле ответа "FIELD_ERROR"
, а id
— "email"
, это означает, что пользователь ввел строку, которая не прошла проверку электронной почты на бэкенде (скорее всего, потому что это не действительный email). Поэтому мы сохраняем состояние ошибки и выводим сообщение об ошибке на пользовательский интерфейс. Вот пример того, как можно сделать так, чтобы сообщение об ошибке появлялось под полем email:
Аналогичным образом мы можем реализовать метод signUp
, где мы вызываем функцию emailPasswordSignUp
из supertokens-web-js
для обработки потока регистрации.
Для социального входа мы используем провайдеров аутентификации Google и Github. Когда вызываются функции onGithubPressed
или onGooglePressed
, мы вызываем метод getAuthorisationURLWithQueryParamsAndSetState
и передаем имя провайдера в параметрах. В качестве параметра authorisationURL
мы также указываем URL обратного вызова, на который провайдеры будут перенаправляться после завершения процесса аутентификации. В нашем примере мы используем http://localhost:3000/auth/callback/google
для Google и http://localhost:3000/auth/callback/github
для GitHub.
Эти URL также должны быть настроены на dashbaord провайдера.
Вот функции для Github и Google соответственно:
onGithubPressed: async function () {
const authUrl = await ThirdPartyEmailPassword.getAuthorisationURLWithQueryParamsAndSetState({
providerId: "github",
// This is where github should redirect the user back after login or error.
// This URL goes on the github dashboard as well.
authorisationURL: "http://localhost:3000/auth/callback/github",
});
window.location.assign(authUrl);
},
onGooglePressed: async function () {
const authUrl = await ThirdPartyEmailPassword.getAuthorisationURLWithQueryParamsAndSetState({
providerId: "google",
// This is where google should redirect the user back after login or error.
// This URL goes on the google dashboard as well.
authorisationURL: "http://localhost:3000/auth/callback/google",
});
window.location.assign(authUrl);
}
После того как пользователь прошел аутентификацию на сайте провайдера, он перенаправляется на маршрут http://localhost:3000/auth/callback/<provider>
. Здесь мы вызываем функцию thirdPartySignInAndUp
из supertokens-web-js
, которая потребляет код авторизации (который отправляется обратно от провайдера) для регистрации пользователя.
Вот функция, которая обрабатывает вышеуказанный поток в компоненте AuthCallbackView
внутри файла /src/views/AuthCallbackView.vue
:
mounted: async function () {
try {
const response = await ThirdPartyEmailPassword.thirdPartySignInAndUp();
if (response.status !== "OK") {
// either the user did not complete the login process, or something else went wrong.
return window.location.assign("/auth?error=signin");
}
// sign in successful.
// The session tokens are handled automatically via our SDK.
window.location.assign("/");
} catch (_) {
window.location.assign("/auth?error=signin");
}
}
Настройка маршрутизации для показа форм регистрации/логина
Vue CLI уже генерирует начальную маршрутизацию для нашего приложения внутри /src/router.index.ts.
.
Мы обновим этот файл так, чтобы маршрут /auth
загружал компонент AuthView
, а маршрут /auth/callback/:provider
загружал компонент AuthCallbackView
, который мы создали ранее:
import { createRouter, createWebHistory } from "vue-router";
import AuthView from "../views/AuthView.vue";
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: "/auth",
name: "auth",
component: () => AuthView,
},
{
path: "/auth/callback/:provider",
name: "authcallback",
component: () => AuthCallbackView,
}
],
});
export default router;
Интеграция бэкенда
Вы можете посмотреть раздел быстрой настройки бэкенда в нашей документации на supertokens.com, или даже скопировать код из нашего примера приложения. Вкратце:
-
Вам необходимо инициализировать SDK
supertokens-node
и предоставить емуrecipeList
(аналогично тому, как вы делали это на фронтенде). -
Затем вам нужно настроить
CORS
, и добавить SuperTokensmiddleware
иerrorHandler
в ваше приложение. SuperTokensmiddleware
открывает все маршруты API, связанные с авторизацией (например, вход, регистрация, выход и т.д.) для фронтенда. -
Наконец, вам нужно указать
connectionURI
(местоположение) ядра SuperTokens. Чтобы быстро начать работу, вы можете указать"https://try.supertokens.com"
(это ядро, которое мы размещаем для демонстрационных целей).
После того, как вы успешно настроили свой сервер, вы можете попробовать зарегистрироваться на фронтенде.
Управление сессией
После аутентификации мы отобразим компонент HomeView
на странице внутри /src/views/HomeView.vue
. Сначала создадим HTML-шаблон по адресу /src/html/homeView.html
:
<div v-if="session">
<div class="fill">
<div class="top-bar">
<div class="sign-out" v-on:click="signOut">SIGN OUT</div>
</div>
<div class="fill home-content">
<span class="home-emoji">🥳🎉</span>
Login successful
<div style="height: 20px" />
Your user ID is <br />
{{ `${userId}` }}
<div style="height: 40px" />
<div class="session-button" v-on:click="callAPI">CALL API</div>
<div style="height: 30px" />
------------------------------------
<div style="height: 40px" />
<a
href="https://github.com/supertokens/supertokens-web-js/tree/master/examples/vuejs/with-thirdpartyemailpassword"
target="_blank"
rel="noreferrer">
View the code on GitHub
</a>
</div>
<div class="bottom-banner">Vue Demo app. Made with ❤️ using supertokens.com</div>
</div>
</div>
Затем внутри /src/views/HomeView.vue
мы проверим, аутентифицирован ли пользователь, используя метод doesSessionExist
, открываемый рецептом Session из SDK supertokens-web-js
.
Для аутентифицированных пользователей мы отображаем кнопку выхода из системы с информацией об их сессии. Когда пользователь нажимает на эту кнопку, мы вызываем метод signOut
из supertokens-web-js
, который очищает сессию пользователя.
Для неаутентифицированных пользователей мы можем перенаправить их на страницу /auth
.
<script lang="ts">
import { defineComponent } from "vue";
import Session from "supertokens-web-js/recipe/session";
import ThirdPartyEmailPassword from "supertokens-web-js/recipe/thirdpartyemailpassword";
const apiPort = import.meta.env.VUE_APP_API_PORT || 3001;
const apiDomain = import.meta.env.VUE_APP_API_URL || `http://localhost:${apiPort}`;
export default defineComponent({
data() {
return {
// if session is false, we show a blank screen
// else we render the UI
session: false,
userId: "",
};
},
methods: {
signOut: async function () {
await ThirdPartyEmailPassword.signOut();
window.location.assign("/auth");
},
checkForSession: async function () {
if (!(await Session.doesSessionExist())) {
// since a session does not exist, we send the user to the login page.
return window.location.assign("/auth");
}
const userId = await Session.getUserId();
// this will render the UI
this.session = true;
this.userId = userId;
},
callAPI: async function () {
const response = await fetch(`${apiDomain}/sessionInfo`);
if (response.status === 401) {
// this means that the session has expired and the
// user needs to relogin.
window.location.assign("/auth");
return;
}
const json = await response.json();
window.alert("Session Information:
" + JSON.stringify(json, null, 2));
},
},
mounted() {
// this function checks if a session exists, and if not,
// it will redirect to the login screen.
this.checkForSession();
},
});
</script>
<template src="../html/homeView.html"></template>
Для маршрута/auth
мы перенаправим пользователя на главную страницу, если сессия уже существует:
checkForSession: async function () {
if (await Session.doesSessionExist()) {
// since a session already exists, we redirect the user to the HomeView.vue component
window.location.assign("/");
}
},
Наконец, для загрузки компонента HomeView
на "/"
мы обновим файл /src/router/index.ts
:
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: "/",
name: "home",
component: () => HomeView,
},
// ...
],
});
export default router;
Если теперь вы посетите сайт http://localhost:3000 после аутентификации, вы увидите следующую страницу:
Поток «Забыли пароль
В пользовательском интерфейсе входа в систему у нас есть ссылка на страницу «Забыли пароль». На этой странице пользователь может ввести свой email и получить ссылку на сброс пароля в свой почтовый ящик. Когда пользователь перейдет по этой ссылке, он сможет ввести свой новый пароль на этой странице, чтобы изменить пароль.
Сначала мы создадим HTML-шаблон внутри /src/html/forgotPassword.html
. Вот пример, который вы можете использовать.
Создадим компонент внутри файла /src/views/ForgotPassword.vue
, в котором мы будем отображать вышеуказанный шаблон:
<template src="../html/forgotPassword.html"></template>
В шаблоне HTML мы условно выводим форму, основываясь на переменной tokenPresent
, которая является переменной состояния, представляющей, был ли сгенерирован токен сброса пароля или нет. Эта переменная tokenPresent
устанавливается на основе токена, присутствующего в параметрах запроса URL страницы. В случае, когда пользователь нажал на кнопку «Забыли пароль» (на странице входа), в параметрах запроса URL страницы нет токена, поэтому переменная tokenPresent
устанавливается в значение false
.
Поскольку tokenPresent
имеет значение false
, мы выводим форму, в которой пользователь вводит свой email, чтобы получить ссылку на сброс пароля. Когда пользователь вводит свой email в этой форме и отправляет ее, мы вызываем метод sendPasswordResetEmail
из supertokens-web-js
и передаем его email. Эта функция взаимодействует с API бэкенда для отправки ссылки сброса пароля на электронную почту пользователя, если эта почта существует в SuperTokens.
Ссылка для сброса пароля имеет вид http://localhost:3000/auth/reset-password?token=....&rid=thirdpartyemailpassword
. Эта ссылка имеет тот же путь, что и страница «Забыли пароль», однако, поскольку URL имеет параметр запроса token
, она должна отобразить форму, в которой пользователь может ввести свой новый пароль.
Когда пользователь вводит новый пароль в форму, мы вызываем функцию submitNewPassword
с новым паролем. Эта функция автоматически считывает токен из URL и вызывает бэкенд API SuperTokens для изменения пароля пользователя.
В случае, если токен сброса пароля уже был использован или истек, вызов функции вернет не "OK"
статус, и тогда мы можем показать сообщение на UI, чтобы предложить пользователю вернуться на экран входа.
<script lang="ts">
import ThirdPartyEmailPassword from "supertokens-web-js/recipe/thirdpartyemailpassword";
import { defineComponent } from "vue";
export default defineComponent({
data() {
/**
* If the URL has a token query param, it means that we should show the
* "enter new password" screen, else we should ask the user for their email
* to send the password reset link.
*/
const urlParams = new URLSearchParams(window.location.search);
const token = urlParams.get("token");
return {
// the email property is used for the enter email screen
email: "",
error: false,
errorMessage: "Something Went Wrong",
didSubmit: false,
// if tokenPresent is true, it means that the user has clicked on the
// reset password link.
tokenPresent: token !== null,
password: "",
};
},
methods: {
onSubmitClicked: async function () {
if (this.tokenPresent) {
// we try and change the user's password now by consuming the token
try {
const response = await ThirdPartyEmailPassword.submitNewPassword({
formFields: [
{
id: "password",
value: this.password,
},
],
});
if (response.status === "FIELD_ERROR") {
// this indicates that the password entered by the user
// did not match the backend password validation logic.
throw new Error(response.formFields[0].error);
} else if (response.status === "RESET_PASSWORD_INVALID_TOKEN_ERROR") {
// the password reset token was consumed already, or has expired.
// in this case, the user should go back to the login screen or the
// enter email screen
throw new Error("Password reset token has expired, please go back to the sign in page");
}
// password reset successful.
window.location.assign("/auth");
} catch (e: any) {
this.errorMessage = e.message;
this.error = true;
}
} else {
// the user has entered an email for whom the password reset link
// will be sent.
try {
const response = await ThirdPartyEmailPassword.sendPasswordResetEmail({
formFields: [
{
id: "email",
value: this.email,
},
],
});
if (response.status !== "OK") {
// this means that the email validation logic failed.
throw new Error(response.formFields[0].error);
}
// a password reset email was sent successfully.
if (this.didSubmit !== true) {
// we change the UI to show that the email has been sent
this.didSubmit = true;
}
} catch (e: any) {
this.errorMessage = e.message;
this.error = true;
}
}
},
},
});
</script>
Если функция submitNewPassword
успешна, это означает, что пароль пользователя был успешно сброшен, и мы перенаправляем пользователя обратно на страницу /auth
, чтобы он мог войти в систему с новым паролем.
Чтобы загрузить компонент ForgotPassword
на маршрут /auth/reset-password
, мы сделаем следующие изменения в файле /src/router/index.ts
:
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
// ...
{
path: "/auth/reset-password",
name: "resetPassword",
component: () => ForgotPasswordView,
},
],
});
После этого, если вы сейчас зайдете на сайтhttp://localhost:3000/auth/reset-password, вы должны увидеть следующую страницу:
Если вы введете свой email и нажмете кнопку «Email Me», то на указанный email вы должны получить ссылку для сброса пароля:
После перехода по ссылке, вы можете ввести свой новый пароль и нажать кнопку «Change Password» для обновления пароля:
Настройка ядра SuperTokens
В процессе настройки бэкенда мы используем "https://try.supertokens.com"
в качестве connectionURI
для ядра. Это демонстрационный экземпляр ядра, размещенный командой SuperTokens. Вы можете использовать его столько, сколько захотите, но если вы намерены использовать SuperTokens, вам следует перейти на самостоятельный хостинг или управляемую версию ядра.
Заключение
Мы использовали SDK supertokens-web-js
для добавления пароля электронной почты и социальной аутентификации вместе с функцией «забыл пароль» в приложение Vue. Полезные ссылки:
-
Пример приложения Vue
-
Сообщество Discord (чтобы задавать вопросы)
-
Список рецептов / методов аутентификации
Написано сотрудниками SuperTokens — надеемся, вам понравилось! Мы всегда доступны на нашем сервере Discord. Присоединяйтесь к нам, если у вас есть вопросы или вам нужна помощь