Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make authenticated API calls #46

Open
amaury1093 opened this issue Jan 26, 2018 · 3 comments
Open

Make authenticated API calls #46

amaury1093 opened this issue Jan 26, 2018 · 3 comments

Comments

@amaury1093
Copy link
Contributor

Steps to reproduce

After authentication, I store the accessToken in my redux state (and after that redux-persist automatically stores it in localStorage).

What I want to achieve is: Make every feathers API call with the accessToken, if it's present in the redux state.

What I'm doing now:

export const app = feathers(); // + configure rest, hooks...

// Append accessToken in header if present.
// It would be better to take the accessToken from redux state,
// but for now we keep this solution (i.e. take directly from
// localStorage)
app.hooks({
  before: hook => {
    const item = window.localStorage.getItem('persist:myApp'); // This is where redux-persist saves the accessToken
    if (item) {
      const { auth } = JSON.parse(item);
      const { accessToken } = JSON.parse(auth);
      if (!accessToken) {
        return;
      }
      hook.params.headers = {
        Authorization: `Bearer ${accessToken}`,
        ...hook.params.headers
      };
      return hook;
    }
  }
});

So what I'm doing is, before each API request, retrieve the accessToken from the localStorage (and not from the in-memory store!).

Expected behavior

There should surely be a way to do this without retrieving localStorage and parsing JSON before each api request.

Actual behavior

Didn't find a clean way to pass state variables to the feathers app.

The only way I found is, on every api request, do

componentWillMount() {
  this.props.findUsers({ // findUsers dispatches feathersRedux.users.find
    headers: { Authorization: `Bearer ${this.props.auth.accessToken}` }
  })
}

But doing this on every method is not viable.

System configuration

Tell us about the applicable parts of your setup.

Module versions 2.1.0

NodeJS version: Not relevant

Operating System: Not relevant

Browser Version: Not relevant

React Native Version: Not relevant

Module Loader: Not relevant

@chris-garrett
Copy link

I just double checked my project(s) and I am using @feathersjs/authentication-client for this.

https://docs.feathersjs.com/api/authentication/client.html
https://github.com/feathersjs/authentication-client

My app looks like:

...
import authentication from 'feathers-authentication-client';
...
export const app = feathers()
  .configure(feathers.socketio(socket))
  .configure(feathers.hooks())
  .configure(authentication({
    storage: window.localStorage, // store the token in localStorage and initially sign in with that
  }));

In my oauth callback page I end up calling:

app.authenticate({strategy: 'jwt', accessToken: Cookies.get('feathers-jwt')}));

at this point the client will exchange the cookie for an auth token and stuff it in local storge. After this all subsequent calls should be authenticated.

@crypto-titan
Copy link

crypto-titan commented Mar 19, 2018

Hey @chris-garrett,

I'm a beginner and I'm trying to implement this for the past 6 hours or so, I am having difficulties because app.authenticate() is a promise and not sure how to wait it out.
I have tried something like this:

    import React from 'react';
    import Dashboard from './dashboard';
    import PropTypes from 'prop-types';
    import feathers from '@feathersjs/feathers';
    import auth from '@feathersjs/authentication-client';
    import socketio from '@feathersjs/socketio-client';
    import io from 'socket.io-client';
    import { Provider } from 'react-redux';
    import Realtime from 'feathers-offline-realtime';
    import reduxifyServices, { getServicesStatus } from 'feathers-redux';
    import configureStore from '../../store';

    const socket = io();
    const app = feathers().configure(socketio(socket)).configure(auth({
        storage: window.localStorage
    }));

    class Layout extends React.Component {
        async componentDidMount(){
            await app.authenticate();
        }

        render() {
            const services = reduxifyServices(app, ['users', 'customers']);
            const store = configureStore(services);
            store.dispatch(services.customers.create({ text: 'message 1' }));
            
            return (
                <div>
                <Provider store={store}>
                <Dashboard services={services} getServicesStatus={getServicesStatus}>
                    { this.props.children }
                </Dashboard>
                </Provider>
                </div>
                );
        }
    }

    export { Layout };

But it does not seem to work, can you give me some advice? :)

@eddyystop
Copy link
Collaborator

eddyystop commented Mar 19, 2018

First you can look at eddyystop/feathers-reduxify-authentication. Its a wrapper over app.authenticate() while maintaining an 'authorized' prop in the state.

Your client will have to be in a 'waiting for auth' state until the promise resolves using app.authenticate() or until the authication state is set using feathers-reduxify-authentication. That can be awkward. You can look at (the by now quite outdated)
eddyystop/feathers-starter-react-redux-login-roles for a redux example.

You can always use a pollyfill for async/await in the client to make it sync, but your frontend will likely be 'hung' until auth is done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants