FeathersJS Auth Recipe: Create Endpoints with Mixed Auth
Auk релиз FeathersJS содержит новый мощный набор authentication suite построенный на основе PassportJS. Он может быть настроен для обработки большинства потребностей аутентификации приложения. В этом руководстве мы рассмотрим наиболее частый сценарий: иногда конечная точка (роут) должна предоставлять разную информацию на основе данных аутентификации пользователя. Например неавторизованный пользователь может просматривать только публичные записи. А авторизованный пользователь может видеть какую-либо дополнительную информацию.
Установка конечной точки Аутентифкации
Для начала создадим рабочую конфигурацию.
To get started, we need a working authentication setup. Below is a default configuration and authentication.js
. They contain the same code generated by the feathers-cli. You can create the below setup using the following terminal commands:
npm install -g feathers-cli@pre
or
yarn global feathers-cli@pre
mkdir feathers-demo-local; cd feathers-demo-local
or a folder name you prefer.feathers generate app
use the default prompts.feathers generate authentication
- Select
Username + Password (Local)
when prompted for a provider. - Select the defaults for the remaining prompts.
- Select
config/default.json:
{
"host": "localhost",
"port": 3030,
"public": "../public/",
"paginate": {
"default": 10,
"max": 50
},
"authentication": {
"secret": "99294186737032fedad37dc2e847912e1b9393f44a28101c986f6ba8b8bc0eaab48b5b4c5178f55164973c76f8f98f2523b860674f01c16a23239a2e7d7790ae9fa00b6de5cc0565e335c6f05f2e17fbee2e8ea0e82402959f1d58b2b2dc5272d09e0c1edf1d364e9911ecad8172bdc2d41381c9ab319de4979c243925c49165a9914471be0aa647896e981da5aec6801a6dccd1511da11b696d4f6cce3a4534dab9368661458a466661b1e12170ad21a4045ce1358138caf099fbc19e05532336b5626aa376bc158cf84c6a7e0e3dbbb3af666267c08de12217c9b55aea501e5c36011779ee9dd2e061d0523ddf71cb1d68f83ea5bb1299ca06003b77f0fc69",
"strategies": [
"jwt",
"local"
],
"path": "/authentication",
"service": "users",
"jwt": {
"header": {
"type": "access"
},
"audience": "https://yourdomain.com",
"subject": "anonymous",
"issuer": "feathers",
"algorithm": "HS256",
"expiresIn": "1d"
},
"local": {
"entity": "user",
"service": "users",
"usernameField": "email",
"passwordField": "password"
}
},
"nedb": "../data"
}
src/authentication.js:
'use strict';
const authentication = require('feathers-authentication');
const jwt = require('feathers-authentication-jwt');
const local = require('feathers-authentication-local');
module.exports = function () {
const app = this;
const config = app.get('authentication');
app.configure(authentication(config));
app.configure(jwt());
app.configure(local(config.local));
app.service('authentication').hooks({
before: {
create: [
authentication.hooks.authenticate(config.strategies)
],
remove: [
authentication.hooks.authenticate('jwt')
]
}
});
};
Set up a “Mixed Auth” Endpoint
Установка конечной точки для "Смешанной авторизации"
Теперь нужно создать конечную точку, которая сможет обрабатывать смешанные запросы обычных и аутентифицированных пользователей. Для этого примера мы используем сервис /users
так как он создается на этапе генерации сервиса аутентификации. Предположим, что в нашем приложении каждая запись пользователя содержит логическое свойство public
и каждая запись выглядит примерно так:
{
id: 1,
email: '[email protected]'
password: "password",
public: true
}
Если user
содержит public: true
, то неаутентифицированые пользователи могут получить доступ к этой записи (просматривать её). Посмотрим как это сделать используя условия iff
и else
(из feathers-hooks-common
) в хуках. Не забудьте познакомится с iff hook API docs
и else hook API docs
если еще не сделали этого.
Будем использовать iff
хук чтобы аутентифицировать только пользователей в запросах которых есть токен. Плагин feathers-authentication-jwt
, который мы используем, содержит возможность получить токен из запроса. Если запрос содержит токен, он будет автоматически доступен в объекте hook
по ключу hook.params.token
.
src/services/users/users.hooks.js
(Это пример показывает работу только с хуками метода find
)
'use strict';
const { authenticate } = require('feathers-authentication').hooks;
const commonHooks = require('feathers-hooks-common');
module.exports = {
before: {
find: [
// Если токен включен, аутентифицируем его со стратегией `jwt`
commonHooks.iff(
hook => hook.params.token,
authenticate('jwt')
// Если токена нет, устанавливаем ограничение в запросе `public: true`
).else( hook => Object.assign(hook.params.query, { public: true }) )
]
}
};
Разберем пример подробнее.
Мы настроили метод find
сервиса /users
c использованием условия iff
в хуке before
:
iff(
hook => hook.params.token,
authenticate(‘jwt’)
)
Для данного приложения, код примера вверху равнозначен коду ниже
hook => {
if (hook.params.token) {
return authenticate(‘jwt’)
} else {
return Promise.resolve(hook)
}
}
На самом деле хук iff
более эффективен, чем пример выше.
It can handle an async predicate expression. This would be equivalent to being able to pass a promise
inside the if
statement's parentheses. It also allows us to chain an .else()
statement, which will run if the predicate evaluates to false.
.else( hook => Object.assign(hook.params.query, { public: true }) )
The above statement simply adds public: true
to the query parameters. This limits the query to only find user
records that have the public
property set to true
.
Wrapping Up
With the above code, we’ve successfully setup a /users
service that responds differently to unauthenticated and authenticated users. We used the hook.params.token
attribute to either authenticate a user or to limit the search query to only public users. If you become familiar with the Common Hooks API, you’ll be able to solve almost any authentication puzzle.