[Перевод] Аутентификация и авторизация в Ember, часть 1: библиотека ember simple auth

Подсистемы регистрации, аутентификации и авторизации пользователей нужны практически любому веб-проекту. К созданию таких подсистем можно подойти с разных сторон. Например — воспользоваться специализированными библиотеками. Сегодня мы хотим поделиться с вами переводом статьи Элвина Креспо, программиста из Echobind, который рассказывает о библиотеке ember-simple-auth.

dcb0a8e5b1bd862ec1c5d5e0e2fd326f.png

По его словам, эта библиотека, предназначенная для организации аутентификации и авторизации, занимает достойное место в арсенале инструментов, которыми пользуются в компании для разработки веб-систем, основанных на 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) и приведём его к такому виду:

 
       {{input type="email" value=email}}  
 
       {{input type="password" value=password}}  
 


Тут создана простая форма с полями 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.

Вот пример работы с только что созданной клиентской частью системы аутентификации:

25a7fc5707310755d5360d7206a0d485.gif


Итоги


В этом материале мы интегрировали в Ember-приложение клиентскую часть системы регистрации, аутентификации и авторизации пользователей. Теперь осталось лишь подключить всё это к API. Как вариант, можно сымитировать конечную точку в ember-cli-mirage. Однако, мы собираемся выйти на реально работающее веб-приложение, поэтому в следующий раз продолжим работу над проектом с применением Elixir/Phoenix и Guardian.

Уважаемые читатели! Как вы создаёте подсистемы аутентификации для веб-приложений?

© Habrahabr.ru