Create Endpoints with Mixed Auth ======================================================== The Auk release of FeathersJS includes a powerful new :doc:`../../api/authentication/server` built on top of `PassportJS `_. It can be customized to handle almost any app’s authentication requirements. In this guide, we’ll look at how to handle a fairly common auth scenario: Sometimes an endpoint needs to serve different information depending on whether the user is authenticated. An unauthenticated user might only see public records. An authenticated user might be able to see additional records. Setup the Authentication Endpoint --------------------------------- 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: 1. ``npm install -g @feathersjs/cli``\ 2. ``mkdir feathers-demo-local; cd feathers-demo-local``\ or a folder name you prefer. 3. ``feathers generate app``\ use the default prompts. 4. ``feathers generate authentication`` - Select ``Username + Password (Local)`` when prompted for a provider. - Select the defaults for the remaining prompts. **config/default.json:** .. code:: json { "host": "localhost", "port": 3030, "public": "../public/", "paginate": { "default": 10, "max": 50 }, "authentication": { "secret": "99294186737032fedad37dc2e847912e1b9393f44a28101c986f6ba8b8bc0eaab48b5b4c5178f55164973c76f8f98f2523b860674f01c16a23239a2e7d7790ae9fa00b6de5cc0565e335c6f05f2e17fbee2e8ea0e82402959f1d58b2b2dc5272d09e0c1edf1d364e9911ecad8172bdc2d41381c9ab319de4979c243925c49165a9914471be0aa647896e981da5aec6801a6dccd1511da11b696d4f6cce3a4534dab9368661458a466661b1e12170ad21a4045ce1358138caf099fbc19e05532336b5626aa376bc158cf84c6a7e0e3dbbb3af666267c08de12217c9b55aea501e5c36011779ee9dd2e061d0523ddf71cb1d68f83ea5bb1299ca06003b77f0fc69", "strategies": [ "jwt", "local" ], "path": "/authentication", "service": "users", "jwt": { "header": { "typ": "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:** .. code:: js 'use strict'; const authentication = require('@feathersjs/authentication'); const jwt = require('@feathersjs/authentication-jwt'); const local = require('@feathersjs/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 ------------------------------ Now we need to setup an endpoint to handle both unauthenticated and authenticated users. For this example, we’ll use the ``/users`` service that was already created by the authentication generator. Let’s suppose that our application requires that each ``user`` record will contain a ``public`` boolean property. Each record will look something like this: .. code:: js { id: 1, email: 'my@email.com' password: "password", public: true } If a ``user`` record contains ``public: true``, then **unauthenticated** users should be able to access it. Let’s see how to use the ``iff`` and ``else`` conditional hooks from `feathers-hooks-common `_ to make this happen. Be sure to read the `iff hook API docs `_ and `else hook API docs `_ if you haven’t, yet. We’re going to use the ``iff`` hook to authenticate users only if a token is in the request. The :doc:`../../api/authentication/jwt`. which we used in ``src/authentication.js``, includes a token extractor. If a request includes a token, it will automatically be available inside the ``context`` object at ``context.params.token``. **src/services/users/users.hooks.js**\ (This example only shows the ``find`` method’s ``before`` hooks.) .. code:: js 'use strict'; const { authenticate } = require('@feathersjs/authentication').hooks; const commonHooks = require('feathers-hooks-common'); module.exports = { before: { find: [ // If a token was included, authenticate it with the `jwt` strategy. commonHooks.iff( context => context.params.token, authenticate('jwt') // No token was found, so limit the query to include `public: true` ).else( context => Object.assign(context.params.query, { public: true }) ) ] } }; Let’s break down the above example. We setup the ``find`` method of the ``/users`` service with an ``iff`` conditional before hook: .. code:: js iff( context => context.params.token, authenticate(‘jwt’) ) For this application, the above snippet is equivalent to the snippet, below. .. code:: js context => { if (context.params.token) { return authenticate(‘jwt’) } else { return Promise.resolve(context) } } The ``iff`` hook is actually more capable than the simple demonstration, above. 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. .. code:: js .else( context => { Object.assign(context.params.query, { public: true }) return context ) 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 ``context.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.