How it is supposed to auth requests from SSR to Feathers JS API?

Aquajet Source

There is examples about how to access FeathersJS API from SSR, but they lack any info on how it is supposed to authorize such requests. Is it ok to instantiate feathers-client app for every request? Would not it be to heavy?

There is an official example of how to call feathers API from server side:

// Set up a socket connection to our remote API
const socket = io('http://api.feathersjs.com');
const api = client().configure(socketio(socket));
​
app.get('/messages', function(req, res, next){
  api.service('messages')
    .find({ query: {$sort: { updatedAt: -1 } } })
    .then(result => res.render('message-list', result.data))
    .catch(next);
});

But what if the messages service will require authenticated user? Should i just manually get token from SSR's req and add it somehow to api instance or api.service call?

Taking in mind the asynchronous nature of node it seems that durable way here is to call client() inside the app.get '/messages' handler, is it a supposed way?

It is also unclear does one of the Feathers boilerplate examples have durable SSR authentication, i've described it here.

javascriptnode.jsfeathersjs

Answers

answered 2 years ago Aquajet #1

Here is how i got it working.

On every request SSR create an API adapter before routing:

app.use('/', (req, res, next) => {
    req.api = APIClient(req);
    next();
});

APIClient constructor gets token from cookie an sets it using the set('accessToken', token) method, provided by feathers-authentication-client plugin:

'use strict';

const feathers = require('feathers');
const superagent = require('superagent');
const hooks = require('feathers-hooks')
const feathers_rest = require('feathers-rest/client');
const auth_plugin = require('feathers-authentication-client');

const config = require('../config');

const host = clientUrl => (
    __SERVER__ ? `http://${config.apiHost}:${config.apiPort}` : clientUrl
);

/*  API adaptor constructor.
*/
module.exports = function APIClient(req) {
    const api = feathers()
        // REST plugin gives ability to query services over HTTP,
        // superagent used as an isomorphic HTTPClient.
        .configure(feathers_rest(host('/api')).superagent(superagent))
        .configure(hooks())
        // Auth plugin gives ability to set accessToken
        .configure(auth_plugin())
    ;

    if (__SERVER__) {
        api.set('accessToken', req['cookies']['feathers-jwt']);
    }
    return api;
}

So, here is a page loading flow i've got:

  1. When one type 'my-app.com' in a browser, it sends a GET request to SSR, passing an access token in feathers-jwt cookie.
  2. SSR creates a feathers client, fetches access token from the cookie and gives it to the client by api.set('accessToken', token) method.
  3. SSR gets data from API using this client and gives it to the template engine (pug/react etc).
  4. SSR returns rendered page to the browser.

Also one need to set token in browser when making requests to API, because if it is on another domain there will be no cookie, and it is better to use Authorization header or token parameter when accessing API.

Discussion link.

comments powered by Disqus