I'm trying to include JWT ( http://jwt.io/ ) token authentication in the boilerplate.
Let's pretend for a second that the autentication process serverside is already there.
What I do is calling API/login passing username and password and I return a token. From that point on, I'd like the middleware to attach this to every http calls.
headers: {
'Authorization': `Bearer *TOKEN* `
}
Where _TOKEN_ is this.store.token variable defined in the redux auth.js.
How can I do that ?
@nicolabortignon i made a library for just that and am using it in combination with this boilerplate: https://github.com/bdefore/express-jwt-proxy
sorry there's very little documentation at this point.
I'm currently passing token header manually for every request that need authorization header.
First, I modified the ApiClient Class, add token parameter, and set the header when token is present
this[method] = (path, { params, data, token }
...
if (token) {
request.set('authorization', 'Bearer ' + token);
}
And pass the token value manually to the action
export function changePassword({password, verifyToken}) {
return {
types: [CHANGEPASSWORD, CHANGEPASSWORD_SUCCESS, CHANGEPASSWORD_FAIL],
promise: (client) => client.post('/user/newPassword', {
data: {
password,
},
token: verifyToken
})
};
}
Hi @xiaobuu , I did implement pretty much a similar approach.
I changed the middleware to check the store for the token, and in case is available, add in the header at any API call.
What it does puzzle me right now is how you retain the token after the user left your app.
In order to maintain the isomorphism you want to save this as a cookie (local storage wouldn't work).
Then I guess you need at bootstrap phase load the cookie, and if there is a token, save it in the newly create store.
This, that in theory sounds easy, looks to me quite hard to implement.
@bdefore : i'll have a look at your library! thanks!
@nicolabortignon part of the consideration for building my library is to store the token in redis with express-session (which identifies the client by a session cookie). i think that addresses what you're describing above.
this article is worth reading regarding why it's better not to store the token on the client: http://alexbilbie.com/2014/11/oauth-and-javascript/
I'm using react-cookie to save the JWT Token and that is a isomorphic(universal) implementation.
So you can read the token from the server
import cookie from 'react-cookie';
app.get('*', (req, res) => {
cookie.plugToRequest(req, res);
//...
}
@nicolabortignon I'm doing exactly what you said. I stored the token in the cookie, and in the App.js I fired a load action in fetchData to load the token from server, then I just implemented a reducer to keep the token somewhere in the state tree.
@nicolabortignon - check out https://github.com/GetExpert/redux-blog-example It solves that retain-token problem you've described by adding a small layer that saves the JWT to a cookie.
The basic solution they describe in this example AFAICT works as follows;
Of course this strategy means all of this needs to happen over https!
It would be great to have something around JWT and/or cookies like this included in this starter boilerplate app.
Good discussion. I am trying to figure out if there is a way to use localStorage instead of cookies; however, I am aware there is no way to get the localStorage from the client on to the server for SSR. Is there?
@Dindaleon - correct. If you have the JWT in a cookie, then as a side benefit the server will receive that cookie along with the request, which is pretty neat from a SSR perspective.
Also @Dindaleon check this article out https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage/ - TL;DR store your JWTs inside a cookie.
@nicolabortignon can you post the middleware you use to add in the token if possible ?
@feesjah
https://github.com/TracklistMe/ReactClient/blob/master/src/helpers/ApiClient.js#L23
here what I've done. Said so, I still feel really uncomfortable about this solution. I struggle in have a proper understanding of the SSR integration, although the comments above provide good 'food for thoughts'.
Would be great if someone can actually add to the boilerplate a JWT over Cookie, SSR compatible authentication model.
Hey I've been following this discussion and implemented a solution based on react-cookie (thanks @rfranco) that ends up being very simple and feels pretty solid. I'll outline the changes to files here, @nicolabortignon if you like it maybe you can incorporate it into your example. I changed some of the code below a bit from my working code to make it match the boilerplate (my project has diverged considerably now) so there might be some typos.
install react-cookie:
npm install react-cookie --save
Add to ApiClient.js:
import cookie from 'react-cookie';
...
//in the constructor method creators, load the auth_token from your cookie:
class _ApiClient {
constructor(req) {
methods.forEach((method) =>
this[method] = (path, { params, data } = {}) => new Promise((resolve, reject) => {
...
if (cookie && cookie.load('loginResult')) {
request.set('authorization', 'Bearer ' + cookie.load('loginResult').auth_token);
}
...
In your auth.js reducer:
import cookie from 'react-cookie';
...
case LOGIN_SUCCESS:
// save the auth token into the cookie - may differ depending how your api returns the auth token
// no need to save the auth token into the redux state. Instead just depend on the cookie as the single source of truth for the token. But save anything else you need from your login response into the redux state, such as the user name.
cookie.save('loginResult', action.result);
return {
...state,
loggingIn: false,
user: action.result.user
};
// Add a new action to load the authentication state from your cookie
case LOAD_AUTH_COOKIE:
const loginResult = cookie.load('loginResult');
const user = loginResult ? loginResult.user : null;
return {
...state,
user
};
...
export function loadAuthCookie() {
return {
type: LOAD_AUTH_COOKIE
};
}
In server.js, configure react-cookie to add cookies sent from the client to the server side rendering environment:
import cookie from 'react-cookie';
...
app.use((req, res) => {
cookie.plugToRequest(req, res);
...
In App.js - use fetch data to load your auth cookie. This will run both on the server for SSR or on the client if you manage to refresh the app on the client without a server side render. (I don't know if that is actually possible but maybe it is? In any case it seems a good place to trigger the check cookie logic. Maybe if your app doesn't need to check if you're logged in at all routes, you could do this fetch data in a different container and then it would be likely to get triggered on the client).
import {loadAuthCookie} from 'redux/modules/auth';
...
function fetchData(getState, dispatch) {
bindActionCreators({loadAuthCookie}, dispatch).loadAuthCookie();
}
@connectData(fetchData)
...
export default class App extends Component {
@adailey14 thanks for this!
I'm currently working on a fork of the boilerplate that unfortunately went way too far from the current mainline.
If I get your code to work I'll send over a PR to be included back in the boilerplate.
Regarding where to place the bootstrap part (the lookup of the cookie), initially I was including it in the router.js file, at the moment of the store creation. I don't see any benefit compare with your solution, so I'm happy to move it to the main container (app.js)
+1
I am also about to begin with JWT and cookies on my project. It would be great to get either a PR or the steps that worked for the both of you in this boilerplate!
has anyone read this blog post ?
http://alexbilbie.com/2014/11/oauth-and-javascript/
The author suggests using a separate proxy to encrypt/sign requests. At the moment I still don't understand fully why a proxy is needed, why cant the encryption / signing all happen on the API side ?
and @adailey14 could you perhaps post the code where you create the token too ? sorry for the trouble :)
@feesjah Actually I am using a Rails powered API so my JWT generation is in ruby, using the JWT ruby gem. I followed this blog post for how to get that set up: http://adamalbrecht.com/2015/07/20/authentication-using-json-web-tokens-using-rails-and-react/
So sorry someone else will have to share their javascript JWT generation code.
Regarding the blog post, I have found oauth to be way too complicated for my needs, and hard to reason about. For my application (and 99% of apps probably) I just need to do 'simple' user authentication (email / password => userId) so I don't think his situation applies to me. I think in the blog's case, the proxy is necessary because he is using a 'global' client_secret that provides 'keys to the kingdom', which you wouldn't want users to access, so you shouldn't store that on the client side.
Below is how I think about security with the JWT setup. I don't see why a proxy would be needed in this case (but I'm really not a security expert!):
@adailey14 What about social logins? How would you change your approach in that case?
@oyeanuj Take this with a grain of salt because I don't really know:
I think when you start talking about social logins you may have to do something more advanced, and start getting into the details of OAuth flows and such. However even in that case you are usually using OAuth as a 'consumer', rather than a 'provider'. So you just have to figure out how to fit in with the OAuth flow that facebook / linkedIn / etc provides. Sometimes this is as simple as adding some snippet to your client side code that allows the user to login to facebook, and then pulling facebook data into your client side code (so nothing fancy on your sever side). But if you want to persist their social login more permanently in your database and such, then you have to better understand the OAuth flows, and probably be careful about where you expose the various secret keys involved. I read somewhere that it would take a developer a dedicated month of study to understand OAuth well enough to implement it with confidence (and I've never dedicated a month to that!).
Of course if you are trying to be like Facebook yourself, allowing other sites to let users sign in to your site through their site... that's when you have to become an OAuth provider / expert yourself.
@adailey14 your snippets worked like a charm!
I have couple of thoughts that I'd like to pick your mind on:
1- every API call (via the apiclient) we invoke cookie.load('loginResult'). Do you know if this is a state read (memory) or a cookie file read? (just for curiosity, I'm not actually concerned about reading time).
2- how would you handle the redirect on login success? I like the approach of Redux-History-Transition (https://github.com/johanneslumpe/redux-history-transitions), but it seams in counter tendency with how the boiler plate is currently handling the redirection (in the router.js file)
@feesjah
Here you can find the server side code that i use for generating the token.
My server side is on nodejs + express. Both client (based on this boilerplate) and server are opensource, so feel free to have a look around.
@nicolabortignon glad the snippets worked. Sorry these answers may not be very satisfying:
@adailey14
regarding point 2: having the 'redirecting after login to the path you were trying to get' seams a desired feature to have so I'd probably try to implement something similar as well.
Having a wrapper component seams legit although a bit forced. In angular it would be a service, but I'm not quite sure how it does translate in the react-redux world.
@erikras thoughts?
Yeah I'm not clear yet on what is and is not forced in the react-redux world, but I think react-router seems to accomplish all of its magic using wrapper components, so it feels to me like the approach in that link fits in with that, and it allows you to specify which routes require authentication right in the same routes.js file, which seems like probably the right place for it. Let us know what you end up with and how it works out.
@nicolabortignon I just noticed on the redux-router readme it has an example of doing login redirection using redux-rx so you might want to take a look at that.
@adailey14 At the end I went with the wrapper components.
I'm not 100% confident in having redux doing the routing part, so at the end I let the route component handle that part.
The final implementation works well. I'm just concerned how little is self contained as solutions (at the end I've been editing 7 files).
@adailey14 , @nicolabortignon Thanks, I have it up and running aswell.
However there is still one problem i can't seem to get fixed, loading the auth cookie and setting the user. When I do it in App.js fetchData, I see that the action gets executed but It seems like it's not connected to the state, since it doesnt actually change
function fetchData(getState, dispatch) {
// bindActionCreators({loadAuthCookie}, dispatch).loadAuthCookie();
const promises = [];
if (!isInfoLoaded(getState())) {
promises.push(dispatch(loadInfo()));
}
if (!isAuthLoaded(getState())) {
promises.push(dispatch(loadAuth()));
promises.push(dispatch(loadAuthCookie()));
}
return Promise.all(promises);
}
@connectData(fetchData)
@connect(
state => ({user: state.auth.user, agency: state.agency.agency}),
{logout, pushState})
export default class App extends Component {
...
must be doing something wrong.
Hi @feesjah nothing obviously wrong in your code. When something doesn't work as I expect in this setup I've been doing "old school" console.log("Am I here?", someObjectToLookAt) to see where the code isn't doing what I expect. So you should probably do that, see if your loadAuthCookie() is being called, see if the dispatched action is being reduced in your reducer, etc, see where the break in the chain is.
Also I wanted to mention since I added the snippets above, I ended up moving the dispatch(loadAuthCookie()) stuff into App.js's ComponentWillMount, because fetchData was causing some infinite loops when paired with routing / redirection logic, and loadAuthCookie doesn't actually do anything asynchronous, so doesn't need to be in fetchData.
@adailey14 , I did that, and the function is being executed and the cookie loaded, but when i log the state, it shows nothing, so I think somehow it's not connected. I m gonna try and see if I get better results with ComponentWillMount.
in ComponentWillMount it works like a charm, only problem is, when I do a refresh, the app will show as not logged in for like half a second, and then rerender as logged in. So I'm thinking about reading the cookie on the server, and dispatching an action to load the user on the server.
The way it works in the snippets I pasted above is that the cookie gets
plugged in on the server-side and the code in either fetch data or
component will mount is valuable to read that cookie. It sounds to me like
you're missing that piece in your code. Fetch data will only run on the
server side, while component will mount runs on both server and client.
On Sunday, December 13, 2015, feesjah [email protected] wrote:
in ComponentWillMount it works like a charm, only problem is, when I do a
refresh, the app will show as not logged in for like half a second, and
then rerender as logged in. So I'm thinking about reading the cookie on the
server, and dispatching an action to load the user on the server.—
Reply to this email directly or view it on GitHub
https://github.com/erikras/react-redux-universal-hot-example/issues/608#issuecomment-164276206
.
Thanks for sharing your code @adailey14!
@feesjah - I ran into the same issue. To get it working I did just as @adailey14 suggested above, i.e.
App.js
import {isLoaded as isAuthLoaded, loadAuthCookie, loadAuth} from 'redux/modules/auth';
function fetchData(getState, dispatch) {
const promises = [];
if (!isAuthLoaded(getState())) {
promises.push(dispatch(loadAuthCookie()));
}
return Promise.all(promises);
}
...
componentWillMount() {
this.props.loadAuth();
}
...
And in auth.js
export function loadAuthCookie() {
return {
type: LOAD_AUTH_COOKIE
};
}
export function loadAuth() {
return (dispatch) => {
dispatch(loadAuthCookie());
};
}
Also @feesjah - if your SSR is not working, you'll notice the app 'flashes' while the client side does the correct transition, and you should also see a warning like this (client) console;
Warning: React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:
@glennr Thanks! Indeed, I didn't notice the warning in console.
I changed my code, and now the load works perfectly.
I played around with it a little bit, if you get loops an reloads, it's probably because you forgot to set 'loaded: true' where you load the cookie.
Also, my componentWillMount never seems to trigger, so my load has to happen in fetchdata.
I've just put together an example that uses the Universal Redux npm package along with express-jwt-proxy that may present a preferable JWT implementation.
There are a couple of key differences from those mentioned here:
logged-in response header to flag whether the user is logged in, which the client can tap into to show authentication status.I think this approach should be more modular, as a few people have mentioned that they have their own tech stack opinions for either the authentication service or the API, whereas the example project here runs authentication and universal duties together, and as @nicolabortignon mentions, you have to touch quite a few files in order to provide session management. In this approach, everything fits nicely inside of a relatively simple auth reducer.
Of course, this comes along with some of the other positives of the npm package, namely that when time comes to update to redux-simple-router, Babel 6, or to a different fetcher middleware, you'll just need to configure your new middleware or update universal-redux rather than upstream merge or patch on your own.
@bdefore Wow that example is great and the Universal Redux package is looking really good for the reasons you mention. I'm going to dig into it soon and see if I can migrate my project towards using it, let me know if there is anything you'd like me to help with, maybe related to that issue https://github.com/bdefore/universal-redux/issues/7 or updating to Babel 6.
@adailey14 I'd love to hear if migrating to this is helpful for you, or if anything comes up.
Either the Babel 6 update (which I envision being the 2.x version) or investigating the issue above would be very helpful, thanks!
@bdefore great work!
Just for the sake of this conversation, would be great if we can integrate a JWT login as the main login system in this boilerplate as well.
I know that the change will touch few parts of the overall boilerplate, so would be great knowing if @erikras is ok to sign this off :)
@nicolabortignon should be doable with a little work on the way the api works. but i'd propose that if we did so, it would be in a jwt branch, as the current implementation is simpler and more approachable.
@bdefore that is a great point!
I do also agree, as you did in your own Universal redux, on moving the logic on the API side. Is a good design decision.
@adailey14 Your code looks great. Attempting to implement now. However, I have a shared root '/' within my routes that checks the auth state to either load a landing page or the actual app. I'm having difficulty triggering the replace route on loginSuccess. Any advice?
The way I have done this is to dispatch loadAuth before the dispatching of a match for the router given the cookies I have in the request. Then, in ApiClient I am looking for a cookie both using req.cookies as well as with react-cookie to find if the jwt token is present and use it in the headers of requests.
I am currently clawing my way through this very same issue. @mmahalwy where is the code you are referring to? Love to see it. :)
Hello, I am using hapi instead of express and redis to store user data. I have set up refresh and access tokens. You can take a look at it if you want: http://github.com/Dindaleon/hapi-react-starter-kit
@mmahalwy do you have a code snippet?
Did any of you have any thoughts on the approach mentioned in these two repos?
@oyeanuj . I used the redux-auth example from Auth0, the example they use are still mostly using localStorage (which only works on the client side), and they only glance over certain issues in comments. But I am in middle of changing it to be used cookies (as discussed in this thread) instead so it can render on the server side.
I went with the solution proposed by @adailey14, I think it is very robust. One concern of mine, academical as it might be, is: does the reducer remain a pure function, when we use it to set the cookie?
I think you can use the "actions" to set the cookie, and keep the reducer pure so that it only deals with state.
that passed through my mind actually, I agree that it would keep the reducer pure. The problem is that the promise is handled by another middleware, it does not seem straight forward to add the logic to the action creator.
We want to keep our reducers pure. We ended up adding a method to ApiClient called updateToken that is called from an action creator. We also updated the constructor of the ApiClient to check for the JWT cookie when it is created. We felt that since the ApiClient is dealing with the API, it was the right place to stick the logic.
Here is our copy of ApiClient.js. And Here is the entire commit. Note: we ended up using auth0 on our project.
I'm fairly new to this this boilerplate, but was looking at this issue with protected routes.
I approached this problem differently, and wanted to see what you guys think. I was trying to protect the API route /loadInfo (as it comes by default on this boilerplate) for testing purposes.
So if I try to load /loadInfo on the API without a successful login, I get an auth error.
The main goal here is to protect the routes selectively not all. This is what the checkAuth() helps with.
/api/actions/login.js
import { UserModel } from './models/UserModel';
import config from '../config';
import cookie from 'react-cookie';
var jwt = require('jsonwebtoken');
export default function login(req) {
var userAuth = {
email: req.body.email,
password: req.body.password
};
if(userAuth.email && userAuth.password) {
UserModel.validateUser(userAuth.email, userAuth.password, function(err, user) {
if(user) {
var token = jwt.sign(user, config.secret, {
expiresIn: 864000
});
cookie.save('token', token, { path: '/' });
userAuth.token = token;
req.session.user = user.email;
}
else {
console.log('not logged in!');
}
});
}
return Promise.resolve(userAuth);
}
/api/utils/checkAuth.js
import config from '../config';
import cookie from 'react-cookie';
var jwt = require('jsonwebtoken');
export default function checkAuth(req, params) {
var token = cookie.load('token') || req.body.token || req.query.token || req.headers['x-access-token'];
if(token) {
try {
req.decoded = jwt.verify(token, config.secret);
} catch(err) {
return false;
}
return true;
} else {
return false;
}
};
/api/actions/loadInfo.js
This is where the route checks for the token and ensures that we are indeed logged in.
import checkAuth from '../utils/checkAuth';
export default function loadInfo(req, params) {
if(checkAuth(req, params)) {
console.log('authenticated!');
return new Promise((resolve) => {
resolve({
message: 'This came from the api server',
time: Date.now()
});
});
} else {
return Promise.reject('Auth error!');
}
}
Maybe there is a cleaner way?
any thoughts if this could be improved?
Another idea is to have "magic" routes that according to a special naming convention they would be protected (but would require to hack the route logic)
@pbreah That approach makes sense to me and seems pretty straight-forward. I'm not sure exactly how, but I'd love to pull the boilerplate out of the authenticated routes. Perhaps having an actions and "authed actions" folder as well, rather than route hacking, just apply a convention that way. I would like have a way to set the default error case to a 401 or 302 or whatever, centralizing makes that simpler. Perhaps as a decorator might work as well.
Edit - I was hoping to use a @auth or some similar concept infront of functions that needed to be authenticated, but I guess the ES6(7?) syntax is only for classes. Anyway, haven't used too many decorators, but here is what I did to streamline it some.
I rewrote checkAuth as:
import cookie from 'react-cookie';
import jwt from 'jsonwebtoken';
export default function checkAuth(req) {
const token = cookie.load('token') || req.body.token || req.query.token || req.headers['x-access-token'];
return function(action) {
return new Promise((resolve, reject) => {
if(token) {
try {
req.decoded = jwt.verify(token, 'mySecret');
} catch(err) {
return reject('No Auth');
}
resolve(action);
} else {
reject('No Auth');
}
});
}
}
In widget/load (which I used just as a test case) I utilize it like this:
import checkAuth as auth from '../../utils/checkAuth'; // call it whatever
...
export default function load(req) {
return auth(req)(new Promise((resolve, reject) => {
// make async call to database
setTimeout(() => {
if (Math.random() < 0.33) {
reject('Widget load fails 33% of the time. You were unlucky.');
} else {
resolve(getWidgets(req));
}
}, 1000); // simulate async load
}));
}
@glennr , @adailey14 : What's the benefit of copying the token into the Authorization header, rather than just allowing the server to read it from the cookie?
@jonathan-stone
I believe sending the JWT in the Authorization header is the way you're supposed to use JWTs according to the examples I've seen (for example: https://auth0.com/learn/json-web-tokens/). So writing your API so it works the way will probably make it more likely to work with other JWT client implementations. Also I believe there are some environments (like within a mobile app) that might want to consume your API where cookies are difficult / impossible to use but Authorization headers are always present for anything that talks http.
@jonathan-stone Also some other benefits of not storing the JWT in a cookie are listed here: https://jwt.io/introduction/. Mainly that by not storing it in a cookie you can use it cross domain without having to worry about CORS issues. (With cookies you would typically have trouble going cross-domain)
Thanks @adailey14 . Good point about consuming my API with a mobile app - cookies would not make sense there. That's reason enough for me, don't want to have to modify the server if/when we add a mobile app!
@adailey14 Thank you so much for posting your code. I did make one modification though. The auth.js Reducer is no more a pure function if cookie.save is used on LOGIN_SUCCESS. So, instead of saving the cookie in the reducer, I did it in the actions creator on LOGIN_SUCCESS. Like in this SO: http://stackoverflow.com/questions/34821742/where-to-set-cookie-in-isomorphic-redux-application
@adailey14 I know it's been a long time since this was answered by you. But I was just curious and had a simpler implementation on the front-end side(I have SSR disabled as some packages I am using are not Isomorphic) and wanted to pick someone's brain if I am not making things too vulnerable.
In the ApiClient.js I have just added request.withCredentials();
`
this[method] = (path, { params, data } = {}) => new Promise((resolve, reject) => {
const request = superagentmethod;
...
request.withCredentials();
...
`
And from the server on correct login request I am sending a Set-Cookie: "Token" response. So the appropriate cookie automatically is set and things just work.
Advice from anyone would be much appreciated.
Most helpful comment
@nicolabortignon - check out https://github.com/GetExpert/redux-blog-example It solves that retain-token problem you've described by adding a small layer that saves the JWT to a cookie.
The basic solution they describe in this example AFAICT works as follows;
( so no cookie action, just standard redux store )
Of course this strategy means all of this needs to happen over https!
It would be great to have something around JWT and/or cookies like this included in this starter boilerplate app.