-
-
Notifications
You must be signed in to change notification settings - Fork 835
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
Develop user authentication strategy #5
Comments
Maybe related: It would be helpful for large-scale usage if the user account system can be overridden by extensions so that it can be integrated with existing account systems. For example, a tech company may want to add support forums for their product, but doesn't want users to have to make a separate account in addition to the main site. I believe WordPress does something like this. |
@AlexanderOMara Yep, this is definitely something we'll want to look at in the future. I've added it to the Features page. For now, though, this issue is about a basic user authentication strategy. |
I've done a good bit of research on this for an API I develop and this is almost exactly the solution we came up with. Pass your username and password via HTTPS one time and get back a token that you can store for later usage. It's a pretty solid way of doing things and as long as it's only passable by HTTPS, you're secure. |
@cwramsey The problem with this is that it requires HTTPS — and since one of Flarum's goals is ease-of-installation, ideally we don't want to require HTTPS to be set up. Is sending a username/password to an API and subsequently authenticating requests with a token without HTTPS any less secure than the corresponding workflow in a normal PHP app (username/password in login form, subsequently authenticating requests with a session cookie)? |
How about something like this? http://www.thebuzzmedia.com/designing-a-secure-rest-api-without-oauth-authentication/ Essentially, it uses a public key and a hash (usually a private key and other data) on the client side, then generates the same hash on the server side and compares the two. |
JWT could be implemented http://jwt.io/ |
Thanks for the links. Looks like JWT is a nice simple way to go. |
As I understand it, the JWT approach without HTTPS would still be susceptible to session hijacking in the same way that a standard PHP session-cookie authentication flow is? And @UTAlan's link (i.e. 2-legged OAuth) would solve this, by authenticating every request using an encrypted private key only known to the client and server. However, I'm pretty keen to keep things simple (at least to start with) and forgo the complexity of OAuth, instead just encouraging the use of HTTPS. What do you all think? |
I'm not wanting to buy a SSL certificate just to be able to use Flarum, however. |
I have built a laravel jwt auth package that may be of some use - https://github.com/tymondesigns/jwt-auth I am currently working on big release on the develop branch, which will be much more flexible and extensible. |
Not using https will get you lower positions on search engines like Google. And as certificates will be free soon and are cheap you should give your community (forum) some kind of security :-) I vote for https |
relying on every host being able to use HTTPS should be avoided in my opinion. think about shared hosts with no SSH access and server hosters who are slow to implement Let's Encript's free HTTPS (or decide not to do it altogether) I mean let's be real here. server hosts are quite slow to adapt to new changes - how many hosts are still running ancient versions of PHP for example? |
@qrokodial Right, well said. Definitely in the mid-to-long-term we will want to implement something like OAuth. But in the short-term, just to kickstart development, I'm going to set up something really simple. |
OK, doing some more reading/thinking:
So: I'm going to implement JWT using @tymondesigns package (excited about 0.4!) and this will at least be a good way to validate claims. Flarum will run insecurely without HTTPS just like esoTalk or any other PHP forum software, and the recommendation will be to set up HTTPS, use something like CloudFlare Universal SSL, and/or offer SSO with external providers (Facebook, etc.) for which at least username/password isn't sent in plain text. Does that sound reasonable? Any thoughts? |
@tobscure Firstly, thanks for taking the time to check out my package 😄 I hope to release 0.4 hopefully within the next couple of weeks, and I am currently writing up the wiki. Also note that 0.4 will be for Laravel 4, and I plan to release 0.5 for Laravel 5 shortly after. Just out of interest, do you plan to keep flarum on Laravel 4 long term, or can you see an upgrade path at some point? Thanks! |
Plan to upgrade as soon as L5 is released. See #2 |
I think that's will be enough for the core. All kinds, more secure solutions can be implemented with extensions in the future. |
@tymondesigns Quick question, there's no way to manually invalidate JWTs is there? i.e. if someone sniffs and steals the token, there's no way to invalidate their claim. Now I'm thinking it might be better to just use Laravel's built-in remember_token system, something like this:
|
@tobscure on the current 0.3 release there is no feasible way to invalidate tokens individually. The token TTL can be kept very short in this case, and refreshed transparently; until, for example, the user becomes inactive (or a longer period has passed) and they must re-login again. I expect you've done a fair bit of reading on the subject already, but here are some links that highlight some issues with using cookies and jwt's. (CORS and session scalability are big ones for me) https://auth0.com/blog/2014/01/07/angularjs-authentication-with-cookies-vs-token/ Also I assume both appraoaches are vulnerable when not in a ssl environment? |
Yes, I think this is OK though (no less secure than a standard PHP app in a non-SSL environment). We will strongly recommend users to set up HTTPS or use something like CloudFlare Universal SSL.
Thanks for the links! I should clarify: I'm not comparing cookies vs. JWTs, but rather standard tokens vs. JWTs. With normal tokens the API is still completely stateless and will work with CORS — it just stores a randomly-generated token in the database for each user and uses that to authenticate each request (a whitelist approach) rather than containing the user's identity within the token and then having to store blacklisted tokens. I've implemented this in Flarum's API (ad269fd). |
Sorry to have discovered this thread a bit later. Here are my 2 cents. JWT will give portability (session info can be stored in token itself) but not security as such. Any traffic over HTTP can be sniffed, whether its username+password, cookies, oauth tokens, or JWT. Once sniffed, backend won't be able to make any distinction and will gladly accept the stolen token/credentials. Keeping it simple might be the way out. If a forum owner is concerned about security, he better get HTTPS. With SNI and all, its not that difficult and costly anymore. Thats what move from OAuth1 to OAuth2 essentially was. It made things much simpler. OAuth2 is not complex and is a very good candidate for being the default authentication. |
@designgrill thanks for the thoughts. I've come to realise that plain ol' tokens aren't flexible/powerful enough (e.g. can't set an expiry date so "remember me" functionality is implied, which would be bad if using Flarum on a public computer.) Very likely I will implement OAuth2 soon. |
'remember me' can be done regardless of the token type. For conventional session IDs, you store the expiry in the session storage. For JWT that can be stored in the token itself. And in both cases the cookie can have corresponding life time. Looking forward to your OAuth2 implementation. That might also enforce an API first design which will go a long way! |
OK, I've been playing around with OAuth2 and I've come to the conclusion that it's overkill for Flarum.
Ultimately, it seems to me that we're left with a simple endpoint which accepts a username-password combo and spits out an access_token which doesn't expire. That's not really OAuth2. So I'm thinking of a really dumbed-down, simple solution instead:
How does that sound? |
Simple. Extensible. Awesome :) |
This should be flexible and extensible for possible integration on any third party apps in the future. |
You are right about OAuth implementation in this case be without any client credentials and Resource Owner Password credentials grant type. This would be exactly similar to your proposed implementation of having a api/token endpoint which can generate token. However, using the OAuth semantics will help a lot. Think about extensibility. Say tomorrow you start to support other social login mechanisms. In fact, what if I want to use my existing OAuth compliant identity server. I can just create a new grant_type in system and done with authentication. You will get good support from existing client libraries as well. Ignoring the refresh tokens part will work, they are not needed for now. You can be implementing just 20-25% of the OAuth standard and still get support from ecosystem in terms of code and developer understanding. For the session thing, you can use the same token as the session ID in the cookie. You need not inject the token through JS anywhere as it will automatically be sent with each request by browser. Some consideration for CSRF/XSS may be needed but can be figured out. For remember me, you can have thing controlled through cookie itself. If remember me is selected, give an explicit expiry time to the cookie else don't give it. The session will be lost as soon as the browser is closed. You can also store the generation time of the token with in token table itself and then you can choose to keep extending the token life as API hits are received or have the hard deadline of 30 days or so from time of creation. Also, store token in hashed format in DB for security purposes. Some fast hash though. It can't be as slow as the password hashing. |
Thanks for bringing up single sign-on stuff — I hadn't thought about that, but it is quite important. That said, I don't understand how implementing an OAuth2 server would work as an authentication flow using external providers. Here's how I envisioned it would work (without OAuth2):
I'm just under the assumption that Laravel has to handle the whole external provider connection process because the client_secret needs to be kept secret. Unless I'm missing something?
The API is stateless — it should not read any cookies. Therefore, the access_token must be sent in an Authorisation header.
I've implemented it so that the contents of the remember cookie is simply an access_token, then this is looked up by the Laravel app and any matching user is logged in. With regards to having expiry times on access_tokens, won't this cause problems with the remember cookie and third-party apps where you don't access your account in over a month?
Is this definitely necessary? If an access_tokens table was compromised, wouldn't the recommended thing to do be to just empty it? https://github.com/lucadegasperi/oauth2-server-laravel doesn't hash its access_tokens in the database, for example. |
Will first go through the easier things.
At HTTP header level, both are not much different. You can write your API Server/Gateway to fall back to Cookie header if the Authorization is missing. There might be some check for CSRF needed here though. If CSRF proofing makes it complex, it can be dropped.
Thats more of a product call rather than technical one. Third party apps, I assume, would be used to this fact that tokens may have expiry. They would just ask user to login again and get a new token. Fine in the name of security once a month or so.
Say the access_tokens never expire. Now as these tokens are as powerful as passwords, getting them compromised is a big security risk as they will be usable immediately. Even if you identify the breach in a day, highly unlikely with the audience of flarum, the damage would already be done. If you use long tokens with a salt and store only the MD5, you avoid that risk. Sadly these tokens can't get same treatment as passwords because their hashing needs to be fast. Long token strings and a common salt may go a long way here. Now for the whole OAuth thing. Feel free to discard this whole thing if looks overkill for flarum. This is based on some framework I plan to create in not so distant future. :) Think how flarum mobile client is going to accept FB login. Its better that there is an API endpoint exposed (api.myflarum.org/token) which takes different type of credentials, does its verification, and returns the token. Now www.myflarum.org may just be a CLIENT of that API which will handle the browser redirects but ultimately pass on the stuff it got from Facebook to api.myflarum.org and gets the token which it can then decide to set in the cookie for instance. |
They are very different because of the security issues associated with cookie authentication. Not having any fallback that reads credentials from cookies is the whole point :).
@tobscure I would suggest going with your strategy and just adding appropriate hooks that make authentication extensible for integrating flarum with OAuth or any custom provider. |
Users will need to be able to authenticate (log in) through the API. It needs to be reasonably secure over both HTTP and HTTPS, just like a standard PHP form login.
I'm unsure of the best way to approach this — presumably just have an endpoint that takes a username/password and gives back a token?
Discuss. Does anyone want to lead this?
The text was updated successfully, but these errors were encountered: