-
Notifications
You must be signed in to change notification settings - Fork 47
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
Switch from cookies to bearer tokens #288
Switch from cookies to bearer tokens #288
Conversation
I could use some advice about
|
btw, the way I implemented this now, no OAuth dialog is necessary. The app just gets to see the user's real password and then calls |
Will this be an integrated protection against CSRF? |
@Acconut there is a separate hapi module for CSRF protection https://github.com/spumko/crumb |
@svnlto Yes, but you're only vulnerable to CSRF if you use cookies. And if this PR removes all cookies between the browser and hoodie-server, we won't be vulnerable by default so no protection is needed. |
@svnlto The question is, if this PR removes cookie usage. :) But if not we should totally do! 👍 |
@Acconut yes, it tries to strip all outgoing as well as incoming cookies, but I'm not sure whether it does so successfully, there may be edge cases I forgot. If there are no bugs in the cookie-stripping then your point about CSRF being solved by that sounds plausible. But IANASE. :) And this PR introduces a new attack surface: the It's not a trivial change. But it makes |
@michielbdejong It's still possible for XSS attacks to get the token but before your PR, using cookies, XSS could also make own request, so I see this as an improvement 👍. |
Let’s keep the XSS/CSRF question out of this PR as the scope and intent are entirely different. |
It seems same-origin signup may be broken; i get a 409 and it displays "undefined: undefined" in the dialog. Will investigate further once I fix my Dockerfile for this branch. |
The code is working now, but before this gets merged I could use some help with:
would someone be available to pair on that, maybe? Also, Michiel Leenaars from nlnet pointed out I could offer to do a guest post on the Hoodie blog about "per-user Hoodie" once it's merged - for my part I think that would be a great idea! Let me know if that would work for you guys as well. |
@michielbdejong great progress. I’m meaning to look into this, but realistically, it won’t be before two weeks from now, so maybe someone can beat me to it :) Also, yes, we would totally blog about this, if you want to supply a blog draft, let us know :) |
@michielbdejong I would enjoy helping with this PR but I'm currently busy and I need some time to get used to your changes but I'll try to contribute. |
resp.headers = res.headers; | ||
|
||
resp.headers['Access-Control-Allow-Origin'] = request.headers.origin || '*'; | ||
resp.headers['Access-Control-Allow-Headers'] = 'Authorization, Content-Length, Content-Type, If-Match, If-None-Match, Origin, X-Requested-With'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you please make Access-Control-Allow-Headers
dynamic, based on the request coming in, just like you do for Access-Control-Allow-Origin
? Otherwise plugins with hooks depending on own headers won't work with CORS
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good idea! although I guess that will only work if the headers are already sent in the OPTIONS request
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this still seems unresolved?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess that will only work if the headers are already sent in the OPTIONS request
Shouldn't be a problem. When browsers need custom headers for CORS requests, they send the headers in the OPTIONS request as far as I know
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah ok, I could make it add all headers that are on the request, plus all headers from the Access-Control-Request-Headers request header. OK, I'll add that functionality. Anything else?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks good to me, I gave it another test with the latest version of your hoodie-server/hoodie.js pull requests.
Just one thought. Is there a reason to call the token "bearer token" and "auth token" at different places? Maybe it would reduce potential confusion to call it bearer token all over? Or do I miss something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agreed, I'll make that change as well.
If someone wants to pair on this in Berlin, I'm here until Monday evening |
@michielbdejong I just rebased it on master, there were a few conflicts. I know get an error when starting up, you also get it when running Any chance you could help me get this fixed? I want to get this merged, everything else is set from my side |
i'll have a look after lunch! |
@svnlto helped out me to get hoodie-server running again, it was a hapi problem. But I know see some of the |
I fear I broke something with your hoodie-server work, the response to |
OK, should be easy to fix. I lost some time getting couchdb running again on my devbox, but I'll continue tomorrow. |
e86d489
to
45beb1c
Compare
This PR takes auth token from the couchdb cookie and exposes it as a bearer token at the HAPI level: * When couchdb sets a cookie, it is stripped, and the authToken is added to the JSON body. * When a request comes in, the 'Bearer ...' token is read from the Authorization header, and converted to a cookie for couchdb. This happens at the hapi level. The solution is based on hapijs/hapi#1637 and https://github.com/spumko/hapi/blob/master/docs/Reference.md TODO: * [ ] create a couchdb mock in the test framework * [ ] use this to create a test for the `mapProxyPath` function * [ ] use this to create a test for the `addCorsAndBearerToken` method * [ ] add tests for the `extractToken` function add basic tests for api plugin tests for mapProxyPath add test for extractToken first draft of addCorsAndBearerToken tests use done function in tests use port number that is different from the one used in other integration tests fix hoodiehq#288 (comment) fix hoodiehq#288 (comment) jshint and typo fix merge errors from rebase try to fix https://travis-ci.org/hoodiehq/hoodie.js/builds/34471428 fix typo in test
The latest changes from my branch are missing in your rebase, for instance the addAllowedHeader function. I just rebased my branch on master, can you maybe test with my one? I'm having a hard time getting hoodie-server and hoodie.js working on my machine, I have been trying for hours, but it just fails to install in different ways. I'm going to start from scratch using the Ubuntu install instructions from hood.ie, instead of using the two git repos as a starting point. |
@michielbdejong Try to remove all karma packages from hoodie.js package json. Then it should run the npm install command at least. Sorry for the trouble, got bit a lot by this myself in the past. It's fixed in the new version that is currently in the works, but we have to get this merged into the old one. Another problem I've found: the bearer token gets sent after you sign out. It gets removed form localStorage, but is still set on |
@gr2m ok, that should be fixed in michielbdejong/hoodie.js@893f999. Is everything else working now, if you use my rebase? |
Unfortunately not, when running the integration tests, the server crashes repeatedly with this error:
Have you seen that? Maybe we can pair on this later today or tomorrow? To setup the integration tests locally, checkout https://github.com/hoodiehq/hoodie-integration-test/, do I also found a problem in hoodie.js that I fixed (https://gist.github.com/c109b87f27dacdd90a11), can you add me as collaborator to your fork? |
@michielbdejong Here is a script you can run against your hoodie test app using the // make sure localStorage is empty. If not, clear an reload page
var username = 'user' + Date.now();
var password = 'secret';
window.events = [];
[
'signin',
'signup',
'signout',
'changeusername',
'changepassword',
'passwordreset',
'reauthenticated',
'error:unauthenticated',
'error:passwordreset'
].forEach(function(eventName) {
hoodie.account.on(eventName, function() {
window.events.push(eventName);
});
});
hoodie.account.signIn('foo', 'bar').fail(function() {
console.log('✓ sign in error message');
hoodie.account.signUp(username, password).done(function() {
console.log('✓ sign up');
var oldPassword = password;
password += '2';
hoodie.account.changePassword(oldPassword, password).done(function() {
console.log('✓ change password');
username += '2';
hoodie.account.changeUsername(password, username).done(function() {
console.log('✓ change username');
hoodie.account.signOut().done(function() {
console.log('✓ sign out');
hoodie.account.signIn(username, password).done(function() {
console.log('✓ sign in');
hoodie.account.destroy().done(function() {
console.log('✓ destroy');
var allEvents = events.join(',');
var allEventsExpected = 'signup,changepassword,changeusername,signout,signin,signout';
console.log('checking for events with 2s timeout to catch unwanted error:unauthenticated events');
setTimeout(function() {
if (allEvents === allEventsExpected) {
console.log('✓ events');
} else {
console.log('Events failed. Expected ' + allEventsExpected + ' but was ' + allEvents);
}
}, 2000);
});
});
});
});
});
});
}); When running this code in latest Firefox stable on Mac, I see the hapi error above. I don't see it in Chrome. But even if wrap the line with a try / catch and fallback data to {}, the script above does not succeed, there is an error:unauthenticated triggered somewhere around the account.destroy call Hope that helps. |
I've |
see hoodiehq#288 (comment) @svnlto could you review please?
Okay, all integration tests pass now, but I had to hack the I'd like someone review this before we merge. |
This PR does two simple things: * look for an 'authToken' field in the sign-in response, and store this as hoodie.account.authToken * send an 'Authorization' header with 'Bearer ' + that token in each request to the hoodie-server. This PR goes together with a PR on the hoodie-server repo, which at the HAPI level, will: * strip the couchdb cookie and add it into the JSON response of signIn requests. * read the Authorization header to look for a token of the form 'Bearer ...', and create a cookie header from the '...', to send to the couchdb layer. set authToken from config at page load fix hoodiehq/hoodie-server#288 (comment)
84a402f
to
bf4f7c2
Compare
This PR takes auth token from the couchdb cookie and exposes it as a bearer token at the HAPI level: * When couchdb sets a cookie, it is stripped, and the authToken is added to the JSON body. * When a request comes in, the 'Bearer ...' token is read from the Authorization header, and converted to a cookie for couchdb. This happens at the hapi level. The solution is based on hapijs/hapi#1637 and https://github.com/spumko/hapi/blob/master/docs/Reference.md TODO: * [ ] create a couchdb mock in the test framework * [ ] use this to create a test for the `mapProxyPath` function * [ ] use this to create a test for the `addCorsAndBearerToken` method * [ ] add tests for the `extractToken` function add basic tests for api plugin tests for mapProxyPath add test for extractToken first draft of addCorsAndBearerToken tests use done function in tests use port number that is different from the one used in other integration tests fix hoodiehq#288 (comment) fix hoodiehq#288 (comment) jshint and typo fix merge errors from rebase try to fix https://travis-ci.org/hoodiehq/hoodie.js/builds/34471428 fix typo in test
see hoodiehq#288 (comment) @svnlto could you review please?
Switch from cookies to bearer tokens This PR takes auth token from the couchdb cookie and exposes it as a bearer token at the HAPI level: * When couchdb sets a cookie, it is stripped, and the authToken is added to the JSON body. * When a request comes in, the 'Bearer ...' token is read from the Authorization header, and converted to a cookie for couchdb. This happens at the hapi level. The solution is based on hapijs/hapi#1637 and https://github.com/spumko/hapi/blob/master/docs/Reference.md BREAKING CHANGE: Hoodie is not using cookies for authentication anymore and relies on hoodie.js@^1.0.0
bf4f7c2
to
1f50385
Compare
@michielbdejong are you around by chance? We have 4 of the unit tests failing now and are quite puzzled |
Sorry, was out of town for a few days. Will be on irc! |
any update? Sorry for pushing, I just need to coordinate with @boennemann when we both have time to finish the merge and release the new versions, and I'll be gone Friday - Monday. Would be great to get it done by tomorrow evening. |
This PR takes the auth token from the couchdb cookie and exposes it as a bearer
token at the HAPI level:
the JSON body.
header, and converted to a cookie for couchdb.
This happens at the hapi level.
The solution is based on hapijs/hapi#1637
and https://github.com/spumko/hapi/blob/master/docs/Reference.md
TODO:
create a couchdb mock in the test framework(added test/unit/api-plugin-test.js)mapProxyPath
functionaddCorsAndBearerToken
methodextractToken
function