Подсистемы регистрации, аутентификации и авторизации пользователей нужны практически любому веб-проекту. К созданию таких подсистем можно подойти с разных сторон. Например — воспользоваться специализированными библиотеками. Сегодня мы хотим поделиться с вами переводом статьи Элвина Креспо, программиста из Echobind, который рассказывает о библиотеке ember-simple-auth.
По его словам, эта библиотека, предназначенная для организации аутентификации и авторизации, занимает достойное место в арсенале инструментов, которыми пользуются в компании для разработки веб-систем, основанных на Ember. В этом материале Элвин говорит о том, как интегрировать библиотеку в проект и создать подсистему регистрации пользователей сайта.
Установка ember-simple-auth
Для начала создадим новое Ember-приложение:
ember new auth-example-frontend
После выполнения этой команды вы должны увидеть следующее:
➜ ember new auth-example-frontend
installing app
create .editorconfig
create .ember-cli
create .eslintrc.js
create .travis.yml
create .watchmanconfig
create README.md
create app/app.js
create app/components/.gitkeep
create app/controllers/.gitkeep
create app/helpers/.gitkeep
create app/index.html
create app/models/.gitkeep
create app/resolver.js
create app/router.js
create app/routes/.gitkeep
create app/styles/app.css
create app/templates/application.hbs
create app/templates/components/.gitkeep
create config/environment.js
create config/targets.js
create ember-cli-build.js
create .gitignore
create package.json
create public/crossdomain.xml
create public/robots.txt
create testem.js
create tests/.eslintrc.js
create tests/helpers/destroy-app.js
create tests/helpers/module-for-acceptance.js
create tests/helpers/resolver.js
create tests/helpers/start-app.js
create tests/index.html
create tests/integration/.gitkeep
create tests/test-helper.js
create tests/unit/.gitkeep
create vendor/.gitkeep
NPM: Installed dependencies
Как видите, я пользуюсь NPM, всё работает как надо, поэтому прошу меня за это не критиковать.
Перейдём в директорию нового приложения:
cd auth-example-frontend
Установим ember-simple-auth
:
ember install ember-simple-auth
После выполнения этой команды система должна сообщить об успешной установке библиотеки:
➜ ember install ember-simple-auth
NPM: Installed ember-simple-auth
Installed addon package.
С установкой мы разобрались, как видите, тут никаких сложностей нет. Теперь займёмся настройкой приложения.
Настройка адаптера
Для начала создадим адаптер приложения:
➜ ember g adapter application
installing adapter
create app/adapters/application.js
installing adapter-test
create tests/unit/adapters/application-test.js
Здесь мы воспользовались генератором для того, чтобы создать адаптер уровня приложения, применяемый по умолчанию. В результате будет создан следующий файл:
// auth-example-frontend/app/adapters/application.js
import DS from 'ember-data';
export default DS.JSONAPIAdapter.extend({
});
Тут нам нужно внести некоторые изменения, приведя этот файл к следующему виду:
// auth-example-frontend/app/adapters/application.js
import JSONAPIAdapter from 'ember-data/adapters/json-api';
import DataAdapterMixin from 'ember-simple-auth/mixins/data-adapter-mixin';
import config from 'my-app/config/environment';
export default JSONAPIAdapter.extend(DataAdapterMixin, {
host: config.apiUrl,
namespace: config.apiNamespace,
authorizer: 'authorizer:application'
});
Здесь мы импортировали адаптер JSONAPIAdapter
, расширили его с помощью DataAdapterMixin
и добавили несколько свойств. В частности, значения, которые записываются в host
и namespace
хранятся в файле config/environment.js
, который мы тоже импортируем. В нём есть следующий код, отвечающий за интересующие нас значения:
let ENV = {
...
apiNamespace: 'api',
apiUrl: null
};
Здесь, во-первых, в качестве apiNamespace
указана строка api
, что влияет на формирование путей вида http://localhost:4000/api/users
. При этом apiURL
мы будем задавать с помощью опции proxy
в ember-cli
, например, так:
ember s --proxy http://localhost:4000
Настройка аутентификатора
Аутентификатор аутентифицирует сессию. Для этого может быть использована, например, отправка набора учётных данных серверу и получение от него токена доступа, или инициация аутентификации с использованием внешнего провайдера вроде Facebook.
Если в двух словах, то аутентификатор применяется для аутентификации сессии с помощью переданных ему учётных данных. Тут мы будем использовать OAuth2PasswordGrantAuthenticator, что даёт возможность выполнять аутентификацию с помощью идентификатора (имя пользователя, адреса электронной почты, и так далее) и пароля.
Для того, чтобы создать аутентификатор, воспользуемся следующей командой:
ember g authenticator oauth2 --base-class=oauth2
В ответ система выведет следующее:
➜ ember g authenticator oauth2 --base-class=oauth2
installing authenticator
create app/authenticators/oauth2.js
В ходе выполнения вышеописанной команды будет создан файл oauth2.js
:
// auth-example-frontend/app/authenticators/oauth2.js
import OAuth2PasswordGrant from 'ember-simple-auth/authenticators/oauth2-password-grant';
export default OAuth2PasswordGrant.extend({
});
Теперь нам нужно задать свойство serverTokenEndpoint. Какую роль оно играет?
Это — конечная точка на сервере, к которой отправляют запросы на аутентификацию и на обновление токена.
Настроим конечную точку всё в том же файле oauth2.js
:
// auth-example-frontend/app/authenticators/oauth2.js
import OAuth2PasswordGrant from 'ember-simple-auth/authenticators/oauth2-password-grant';
import config from 'my-app/config/environment';
const host = config.apiUrl || '';
const namespace = config.apiNamespace;
const serverTokenEndpoint = [ host, namespace, 'token' ];
export default OAuth2PasswordGrant.extend({
serverTokenEndpoint: serverTokenEndpoint.join('/')
});
Тут мы формируем необходимое нам свойство serverTokenEndpoint
, объединяя строковые константы host
и namespace
со строкой token
. В результате, например, если у нас есть следующее:
host = 'http://localhost:4000'
namespace = 'api'
token = 'token'
В serverTokenEndpoint
окажется такая строка:
http://localhost:4000/api/token
Настройка авторизатора
Авторизаторы используют данные сессии, полученные аутентификатором в ходе аутентификации сессии, для формирования данных авторизации, которые затем, например, могут быть встроены в последующие сетевые запросы.
Смысл этого всего заключается в том, что после завершения аутентификации авторизатор использует данные сессии для формирования данных авторизации, используемых в запросах. В нашем примере это приведёт к формированию такого заголовка:
Authorization: Bearer s0m3tok3n1
К счастью, библиотека ember-simple-auth
упрощает настройку всего этого. Мы будем использовать класс OAuth2BearerAuthorizer, так как он, без особых усилий с нашей стороны, позволяет сформировать вышеописанный заголовок.
Нам нужно лишь выполнить такую команду:
ember g authorizer application --base-class=oauth2
После выполнения команды мы увидим следующее:
➜ ember g authorizer application --base-class=oauth2
installing authorizer
create app/authorizers/application.js
В результате будет создан файл, расположенный по пути app/authorizers/application.js
:
// auth-example-frontend/app/authorizers/application.js
import OAuth2Bearer from 'ember-simple-auth/authorizers/oauth2-bearer';
export default OAuth2Bearer.extend({
});
Подключение ember simple auth к приложению
После того, как настройка подсистем аутентификации и авторизации завершена, можно воспользоваться всем этим в приложении.
▍Создание маршрута для регистрации в системе
Сначала нужно создать маршрут, который обеспечивает регистрацию в системе:
ember g route sign-up
В ответ на эту команду будет выдано следующее:
➜ ember g route sign-up
installing route
create app/routes/sign-up.js
create app/templates/sign-up.hbs
updating router
add route sign-up
installing route-test
create tests/unit/routes/sign-up-test.js
Откроем созданный шаблон формы регистрации (sign-up.hbs
) и приведём его к такому виду:
<form {{action "onSignUp" email password on="submit"}}>
<div>
<label>Email</label>
{{input type="email" value=email}}
</div>
<div>
<label>Password</label>
{{input type="password" value=password}}
</div>
<button type="submit">Sign Up</button>
</form>
Тут создана простая форма с полями email
и password
. Когда пользователь щёлкает по кнопке Sign Up —
мы вызываем действие onSignUp
, передавая ему введённые адрес электронной почты и пароль. Определим это действие:
// auth-example-frontend/routes/sign-up.js
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
const AUTHENTICATOR = 'authenticator:oauth2';
export default Route.extend({
session: service(),
actions: {
async onSignUp(email, password) {
let attrs = { email, password };
let user = this.store.createRecord('user', attrs);
await user.save();
let session = this.get('session');
await session.authenticate(AUTHENTICATOR, email, password);
}
}
});
Выше мы всего лишь добавили действие onSignUp
в маршрут. Если вы не знакомы с тем, как в Ember работают события, то, учитывайте, что действие onSignUp
, вызванное в шаблоне, задействует контроллер, а затем маршрут. Так как мы не определяем действие в контроллере, его обработает маршрут.
В действии onSignUp
мы получаем email
и password
, поэтому мы используем эти данные для создания учётной записи пользователя. Затем сохраним эту учётную запись и получим службу сессии. Служба сессии является частью ember-simple-auth
, вот её описание:
Служба сессии предоставляет доступ к текущей сессии, а также методы для её аутентификации, признания недействительной, и так далее. Это — главный интерфейс к функционалу Ember Simple Auth, которым могут пользоваться приложения.
Учитывая это, затем мы вызываем метод authenticate. Мы передаём этому методу аутентификатор, который мы хотим использовать, в нашем случае authenticator:oauth2
, а также, в виде отдельных аргументов, пароль и адрес электронной почты.
Обратите внимание на то, что если вы не пользовались конструкцией async/await
в вашем проекте, то вполне можно работать и без неё. Вы можете легко убрать async/await
из вышеприведённого кода и использовать конструкцию then/catch/finally
для разрешения промисов. Если вам интересно узнать подробности об использовании async/await
в ваших проектах, взгляните на этот материал.
Теперь нам осталось лишь определить модель user
. Если этого не сделать, мы столкнёмся со следующей неприятной ошибкой:
sourceТакое вряд ли кого порадует, поэтому займёмся моделью.
▍Определение модели user
Мы создадим довольно простую модель user
. Она будет содержать лишь адрес электронной почты и пароль. Создадим её, запустив следующий генератор:
ember g model user email:string password:string
Выполнив эту команду, увидим следующее:
➜ ember g model user email:string password:string
installing model
create app/models/user.js
installing model-test
create tests/unit/models/user-test.js
Созданный этой командой файл модели user.js
надо привести к такому виду:
// auth-example-frontend/models/user.js
import DS from 'ember-data';
export default DS.Model.extend({
email: DS.attr('string'),
password: DS.attr('string')
});
Теперь всё готово к испытаниям.
Тестирование системы аутентификации
Для того, чтобы посмотреть на то, что мы только что сделали, в действии, надо запустить приложение. Но прежде чем это сделать, уберём стандартную страницу приветствия. Откроем файл package.json
и удалим из него следующее:
"ember-welcome-page": "^3.0.0",
Теперь запустим сервер:
ember s
После этого надо перейти по адресу http://localhost:4200/sign-up
.
Вот пример работы с только что созданной клиентской частью системы аутентификации:
Итоги
В этом материале мы интегрировали в Ember-приложение клиентскую часть системы регистрации, аутентификации и авторизации пользователей. Теперь осталось лишь подключить всё это к API. Как вариант, можно сымитировать конечную точку в ember-cli-mirage. Однако, мы собираемся выйти на реально работающее веб-приложение, поэтому в следующий раз продолжим работу над проектом с применением Elixir/Phoenix и Guardian.
Уважаемые читатели! Как вы создаёте подсистемы аутентификации для веб-приложений?
Автор: ru_vds