TL;DR
- Добавить аутентификацию в приложение Vue.js с Auth0
- Авторизация с использованием разрешений JWT и Hasura GraphQL
- Пример приложения Vue, защищенного логином для получения статей, написанных авторизованным пользователем.
- Исходный код для образца приложения
Стек технологий
Приложение использует следующий стек, который необходимо настроить и настроить для его работы:
- Vue.js с vue-cli-plugin-apollo и vue-router
- Auth0 для аутентификации
- Hasura GraphQL Engine для мгновенных API GraphQL
Давайте развернем Hasura вместе с postgres, чтобы подготовить наши API GraphQL.
Развернуть Хасуру
Hasura - это движок с открытым исходным кодом, который предоставляет вам API-интерфейсы GraphQL в реальном времени для новых или существующих баз данных Postgres со встроенной поддержкой для сшивания пользовательских API-интерфейсов GraphQL и запуска веб-перехватчиков при изменении базы данных.
Следуйте инструкциям в документации, чтобы развернуть Hasura. Обратите внимание на URL-адрес Heroku для конечной точки GraphQL. Вы будете настраивать это в приложении позже.
Примените миграции, следуя инструкциям в этом разделе, чтобы создать необходимую схему базы данных и разрешения.
Теперь бэкэнд готов! Вы сможете мгновенно делать запросы с помощью API Hasura GraphQL. Конечная точка будет выглядеть так (https://myapp.herokuapp.com/v1alpha1/graphql). Мы вернемся к этому во время интеграции с приложением Vue.
Создать приложение в Auth0
- Перейдите в Панель управления Auth0 и создайте приложение в виде одностраничного веб-приложения.
2. В настройках приложения добавьте http://localhost:3000/callback
как «Разрешенные URL-адреса обратного вызова» и http://localhost:3000
как «Разрешенные веб-источники», чтобы разрешить локальную разработку приложения.
Добавить правила для пользовательских утверждений JWT
На панели инструментов Auth0 перейдите к «Правилам». Добавьте следующие правила, чтобы добавить наши собственные утверждения JWT:
function (user, context, callback) {
const namespace = "https://hasura.io/jwt/claims";
context.idToken[namespace] =
{
'x-hasura-default-role': 'user',
// do some custom logic to decide allowed roles
'x-hasura-allowed-roles': user.email === '[email protected]' ? ['user', 'admin'] : ['user'],
'x-hasura-user-id': user.user_id
};
callback(null, user, context);
}
Получите сертификат подписи JWT
Перейдите на https://hasura.io/jwt-config и сгенерируйте конфигурацию для своего домена Auth0.
Скопируйте конфигурацию JWT, созданную для приложения Auth0.
Включить режим JWT на Hasura
Сгенерированную выше конфигурацию необходимо использовать в переменной среды HASURA_GRAPHQL_JWT_SECRET
. Нам также нужно установить ключ HASURA_GRAPHQL_ADMIN_SECRET
для работы режима JWT.
После того, как вы добавили это, конечные точки GraphQL можно будет запрашивать только с использованием заголовка Authorization
или X-Hasura-Admin-Secret
.
Создать правило Auth0
Каждый раз, когда пользователь регистрируется на Auth0, нам нужно синхронизировать этого пользователя с нашей базой данных postgres. Это делается с использованием правил Auth0. Создайте еще одно правило и вставьте следующий код:
function (user, context, callback) {
const userId = user.user_id;
const nickname = user.nickname;
request.post({
headers: {'content-type' : 'application/json', 'x-hasura-admin-secret': '<your-admin-secret>'},
url: 'http://myapp.herokuapp.com/v1alpha1/graphql',
body: `{\"query\":\"mutation($userId: String!, $nickname: String) {\\n insert_users(\\n objects: [{ auth0_id: $userId, name: $nickname }]\\n on_conflict: {\\n constraint: users_pkey\\n update_columns: [last_seen, name]\\n }\\n ) {\\n affected_rows\\n }\\n }\",\"variables\":{\"userId\":\"${userId}\",\"nickname\":\"${nickname}\"}}`
}, function(error, response, body){
console.log(body);
callback(null, user, context);
});
}
Замените admin secret
и url
соответствующим образом.
Наконец-то у нас есть готовая полная бэкэнд и настройка аутентификации. Давайте настроим интерфейс Vue.js для выполнения запроса GraphQL с правильными заголовками.
Настроить Vue-CLI-Apollo-Plugin
Мы будем использовать пример приложения Auth0, чтобы начать работу со стандартным кодом.
Следующая команда генерирует настройку клиента apollo для приложения Vue.
vue add apollo
Это создаст файл с именем vue-apollo.js
в src
. В этом файле мы будем настраивать объект options
, getAuth
, определяя следующее:
getAuth: tokenName => {
// get the authentication token from local storage if it exists
// return the headers to the context so httpLink can read them
const token = localStorage.getItem('apollo-token')
if (token) {
return 'Bearer ' + token
} else {
return ''
}
},
Эта конфигурация гарантирует, что ApolloClient использует токен, возвращаемый Auth0 для заголовка Authorization
, при выполнении запроса или подписки.
Аутентифицированный запрос
Клиент Apollo был настроен с правильными заголовками в приведенной выше настройке. Итак, давайте добавим простой запрос для получения списка статей, написанных пользователем, который вошел в систему.
export default {
apollo: {
// Simple query that will update the 'article' vue property
article: gql`query {
article {
id
title
}
}`,
},
}
Теперь мы хотели бы показать это, только если пользователь вошел в приложение.
Итак, в нашем теге <template>
для Home.vue
мы будем использовать следующий фрагмент кода для перечисления статей.
<template>
...
...
<div v-if="isAuthenticated">
<h1 class="mb-4">
Articles written by me
</h1>
<div v-for="a in article" :key="a.id">
{{a.id}}. {{ a.title }}
</div>
</div>
...
...
</template>
Обратите внимание, что мы гарантируем, что эта разметка должна отображаться только в том случае, если isAuthenticated
возвращает true. Чтобы реализовать это, мы генерируем событие после каждого успешного входа в систему.
Перейдите к src/auth/authService.js
, чтобы увидеть детали реализации входа в систему Auth0 и генерации событий.
В этом файле событие генерируется после успешного входа в систему.
this.emit(loginEvent, {
loggedIn: true,
profile: authResult.idTokenPayload,
state: authResult.appState || {}
});
plugin
был зарегистрирован для обработки этого события в src/plugins
import authService from "../auth/authService";
export default {
install(Vue) {
Vue.prototype.$auth = authService;
Vue.mixin({
created() {
if (this.handleLoginEvent) {
authService.addListener("loginEvent", this.handleLoginEvent);
}
},
destroyed() {
if (this.handleLoginEvent) {
authService.removeListener("loginEvent", this.handleLoginEvent);
}
}
});
}
};
Итак, как только возникает loginEvent
, вызывается метод handleLoginEvent
.
И в нашем Home.vue
компоненте мы обрабатываем этот метод для обновления значения isAuthenticated
. По умолчанию это false
, и после успешного входа в систему обновляется до true
.
methods: {
handleLoginEvent(data) {
this.isAuthenticated = data.loggedIn;
this.isLoading = false;
}
},
Вышеупомянутый запрос GraphQL отправляется с использованием заголовка токена, возвращаемого Auth0, и это обеспечивает аутентификацию.
Авторизация с помощью JWT
Хотя пользователь вошел в систему, мы хотим отображать только статьи, написанные этим же пользователем. Разрешения были настроены таким образом, что только пользователь, написавший статью, сможет получить данные.
Перейдите по URL-адресу приложения Heroku, чтобы открыть консоль Hasura, и перейдите к Данные- ›статья-› Разрешения, чтобы просмотреть разрешения, определенные для роли user
.
Проверка разрешений выглядит так:
{ "user_id": {"_eq": "X-Hasura-User-Id"}}
Это означает, что когда запрос отправляется с Authorization: Bearer <token>
от клиента, он будет искать значение X-Hasura-User-Id
из полезных данных токена и фильтровать его по столбцу user_id
, гарантируя, что только зарегистрированные пользователи получают данные, а также получают только их данные. У пользователя есть права доступа ко всем столбцам.
Защищенные маршруты с использованием Vue Router
Поскольку мы используем Vue Router, мы можем добавить Navigation Guards
, используя Global Before Guard
. Это вызывается всякий раз, когда запускается переход, и переход считается отложенным, пока не будет разрешен.
В src/router.js
мы определяем beforeEach
охранник, который проверяет логическое значение auth.isAuthenticated()
перед разрешением.
router.beforeEach((to, from, next) => {
if (to.path === "/" || to.path === "/callback" || auth.isAuthenticated()){
return next();
}
auth.login({ target: to.path });
});
В случае, если страница не /
, /callback
или пользователь не аутентифицирован, то пользователь перенаправляется на страницу входа с использованием метода auth.login
.
Запуск приложения
Нам нужно настроить конечную точку Hasura GraphQL в приложении Vue.js. Перейдите к src/vue-apollo.js
и соответствующим образом измените значения httpEndpoint
и wsEndpoint
.
Запустите образец приложения, выполнив следующие команды:
npm install
npm run serve
Вы должны увидеть такой экран:
Я собрал шаблон, чтобы вы могли быстро приступить к работе!
Проверьте это на гитхабе.
Попробуйте и дайте нам знать, что вы думаете. Если у вас есть какие-либо вопросы или возникнут какие-либо проблемы, не стесняйтесь обращаться к нам в twitter, github или на нашем сервере Discord.
Первоначально опубликовано на https://blog.hasura.io 1 апреля 2019 г.