encode / django-rest-framework

Web APIs for Django. 🎸
https://www.django-rest-framework.org
Other
28.22k stars 6.82k forks source link

Add OAuth authentication/permissions. #8

Closed tomchristie closed 11 years ago

tomchristie commented 13 years ago

The plan would be to use David Larlet's OAuth plus, and keep it as an external dependency for anyone requiring OAuth support.

See: https://bitbucket.org/david/django-oauth-plus/overview

At least as a first pass this oughta be pretty easy - most of the OAuth stuff is just down to oauth-plus to deal with and you only need to tie in the very last leg.

You'll want to start off looking at examples/provider in oauth-plus. You'll see the 'outh_providers.urls' views listed in there. Those don't need to tie in at all to begin with. They'll just need to be unprotected un-api'ed views that just work exactly as they do already. (At some point it'd be really great to tie them in with the auto-documenting API - that'd really really improve the ease of use I think.)

So the part you do need to tie in is CheckOAuth. With oauth-plus you'd normally apply that check to a view as a simple decorator, and it'd ensure that:

1) The request is signed with a valid OAuth token 2) The OAuth tokens 'name' matches the view name (I think that'd right.)

We'll prob want pretty much the same thing eg.

1) A .scope attribute gets put on any Resource you want to protect with OAuth. 2) The OAuthAuthenticator gets added to the Resource's authenticators. 3) The OAuthAuthenticator.authenticate(request) method uses CheckOAuth (or some equivalent) to ensure the token is valid and the token's name matches the Resource's 'scope', or raises an HttpException (PERMISSION DENIED).

I think it's that simple. (?)

mammique commented 12 years ago

Why not using OAuth2app? : http://hiidef.github.com/oauth2app/

It should be much simpler, as it implements OAuth 2.0, which is less tricky than OAuth 1.0 provided by django-oauth-plus. I did this my own way for my API, which was done from scratch, I'm learning django-rest-framework to migrate to it now, I'll try to figure out how feasible this is.

flashingpumpkin commented 12 years ago

May I suggest https://github.com/caffeinehit/django-oauth2-provider ? ;)

mammique commented 12 years ago

Interesting! I don't really understand why there are two module levels: provider and oauth2, are you anticipating provider.oauth3? However, it seems pretty neat! Have you ever experience its integration in DRF?

tomchristie commented 12 years ago

May I suggest https://github.com/caffeinehit/django-oauth2-provider ? ;)

Yes please.

Things to note:

  1. There's no reason OAuth support needs to go into core as such. - Could be an external library that provides an OAuthAuthentication class and OAuthScopePermission class.
  2. There's a bunch of OAuth tests in there at the moment that David Larlet worked on. Plugging them never quite got finished off. Any work that does get done on this should take that into account (possibly removing them if appropriate). I think there's also something in the test settings file.
  3. My comment at the top of this issue isn't quite right - the authenticator shouldn't do any permissions checking. That should be up to a permission class.

Also this is more a future thing, but worth bearing in mind:

  1. It's occurred to me that the current system whereby authenticators return a single 'auth' context (which is usually the user) doesn't quite work - they should probably return a two-tuple of (user, auth). user is the User instance that's been authenticated, and auth is any additional authentication information (eg. scope, expiry or other tokening information) Session authentication, and basic authentication would just return (user, None), OAuth and other API tokening schemes would return (user, auth).
  2. In the same way that .DATA is moving to the request.DATA instead of view.DATA (See #141), we should also have request.user and request.auth. The authenticators should move to the request in the same way that the parsers have.

/cc @sebpiq

mammique commented 12 years ago

OK, correct me if I'm wrong, but I think that David's stuff is OAuth 1.0 related. I'm not sure that 1.0 worth the challenge nowadays regarding its tedious design and that 2.0 is ramping up… Indeed, authenticators will probably need (user, auth), I was thinking about that as well. Thanks for the warnings about the next evolution of DRF :-) I keep browsing around the code until I find where to start hacking from :-)

Camille.

tomchristie commented 12 years ago

OK, correct me if I'm wrong, but I think that David's stuff is OAuth 1.0 related.

As far as I know, yup. It was last summer, and I don't think the OAuth 2.0 was very finalized.

I'm not sure that 1.0 worth the challenge nowadays regarding its tedious design and that 2.0 is ramping up…

Yup. Makes sense to me. Thought I'd better just point out that there are vestiges of 1.0 stuff in there that might need ripping out if we get an OAuth 2.0 implementation.

reinout commented 12 years ago

I've been looking at oauth for two days now and I'd personally say that it is just fine to use 1.0. 1.0 works fine and is pretty clear, 2.0 seems to be quite enterprisy and hard to implement right. So if there's a proper 1.0 library...

As a data point: dropbox is using oauth 1.0.

tomchristie commented 12 years ago

Yup I'd somewhat come to the same conclusion.

The confusingly named python-oauth2 library (It actually implements 1.0) seems very well used.

There's an example service that uses it here: https://github.com/honza/oauth-service

And a plugin to Kenneth Reitz'a requests that gives you an oauth client here, which'd be useful for testing: https://github.com/maraujop/requests-oauth

tomchristie commented 12 years ago

@flashingpumpkin's django-oauth2-provider looks like the best option to me.

He's given me a sketch of an auth class: https://gist.github.com/877c9ffb14568031609c Would need updating slightly for 2.0, plus some docs. (Although leave the bulk of documentation to the django-oauth2-provider project - I don't really want to get into maintaining anything beyond a stub plugin.

Anyone up for taking this on?

flashingpumpkin commented 12 years ago

I've got no time currently. :/

tomchristie commented 12 years ago

@flashingpumpkin shame. D'ya think it'd be worth me pulling together an Authentication class for django-oauth2-provider? How likely is it to get a little bit of "getting started" style docs love sometime? I don't think it needs much, but at the moment I'd be a bit lost around things like say, why client_type is relevant, and what effect, if any it actually has. I think my only other quibble is that I think the bitwise scopes are a bit awkward - it'd be nice to just have a proper Scope table with name and description. I guess it's possible I could put a little bit of time into django-oauth2-provider if it looks like it's going to remain well maintained.

flashingpumpkin commented 11 years ago

@tomchristie When I get some time spare I'm definitely up for it. We're using it quite heavily in our projects. Re bitwise scopes: It's easy to swap them out for a table with name and description, but again, time. :(

tomchristie commented 11 years ago

When I get some time spare I'm definitely up for it.

@flashingpumpkin - That's good enough for me. If I get around to it I might tick off the Authentication class myself. I don't really care about that side of things too much myself, since it's a pretty small stub. It's mostly about being confident that the provider library used is something that's going to get a bit of support & maintenance now and again.

but again, time. :(

Yup. As ever. Feeling pretty lucky that I've managed to get some real quality on-the-clock hours for REST framework 2, would have been a pretty impossible slog otherwise.

michaelmior commented 11 years ago

Curious if anyone has made any progress on this. Wondering if there's any way I can lend a hand. If people are happy with the approach of django-oauth2-provider as an approach but no one has the time to implement right now, I can give that a go.

tomchristie commented 11 years ago

@michaelmior - Nope it's still kicking around. As you can see there's not loads needed in terms of implementation, so I think a big chunk of the work needed is simply around adequate documentation and testing. Also I don't know if there's any python oauth2 client at the moment, but it'd def be helpful if we could provide examples to implementers of how to access with eg. a python client.

I'm also not really sure if it'd make sense to implement this as a third party package, or include it directly in core.

michaelmior commented 11 years ago

I haven't tried it, but there is python-oauth2 which seems promising. Personally I'd like to see this end up in the core since OAuth is becoming pretty standard as an auth method for any API.

My experience with OAuth in Python has strictly been on the server side (currently using Piston, which uses about the same OAuth code as django-oauth-plus).

tomchristie commented 11 years ago

Note that python-oauth2 is actually (somewhat confusingly) support for oauth1. It's the underlying library that django-oauth-plus uses.

On 7 January 2013 16:43, Michael Mior notifications@github.com wrote:

I haven't tried it, but there is python-oauth2https://github.com/simplegeo/python-oauth2which seems promising. Personally I'd like to see this end up in the core since OAuth is becoming pretty standard as an auth method for any API.

My experience with OAuth in Python has strictly been on the server side (currently using Piston, which uses about the same OAuth code as django-oauth-plus).

— Reply to this email directly or view it on GitHubhttps://github.com/tomchristie/django-rest-framework/issues/8#issuecomment-11959115.

matthewlmcclure commented 11 years ago

I believe oauthlib implements OAuth 2 client behavior. Server support was still in the works as of a few weeks ago.

tomchristie commented 11 years ago

Assuming you mean this oauthlib: https://github.com/idan/oauthlib

There isn't a well-documented, maintained Django binding for that yet.

This is the closest I've seen, https://github.com/craigbruce/django-oauth

Have toyed with the idea of putting together a Django binding for oauthlib, which might be doable, but only if we could get some community support behind the effort.

On 7 January 2013 17:23, matthewlmcclure notifications@github.com wrote:

I believe oauthlib implements OAuth 2 client behavior. Server support was still in the works as of a few weeks ago.

— Reply to this email directly or view it on GitHubhttps://github.com/tomchristie/django-rest-framework/issues/8#issuecomment-11961019.

matthewlmcclure commented 11 years ago

Yes, that's the oauthlib I meant.

michaelmior commented 11 years ago

I took a look at https://github.com/craigbruce/django-oauth. The project seems pretty far from complete, but perhaps a good starting point. However, I'm inclined to say that your initial suggestion of OAuth plus seems like the best option so far.

I'm a little biased since as mentioned, I'm using almost the same code in one of my current projects via Piston. I've found it to be quite stable.

dulacp commented 11 years ago

Hi everyone,

I've read the whole issue and I'm willing to give a hand on adding the OAuth and OAuth2 support.

If I correctly understand the work @tomchristie is suggesting, the goal is to provide just an Authentication class that returns a tuple (user, token) just like the TokenAuthentication.authentication is working but for that to work with any OAuth2 backend we want, we need to let the developer implement the method that validate the token and retrieve the user from the token submitted, usually called authenticate_credentials, right ?

This way, we can build a perfectly decoupled OAuth2 authentication support. And guide the user in the documentation on how to use it of course.

I will add a pull request to translate those clumsy words into a full of sense code :)

tomchristie commented 11 years ago

I've read the whole issue and I'm willing to give a hand on adding the OAuth and OAuth2 support.

@dulaccc - That'd be great!

but for that to work with any OAuth2 backend we want, we need to let the developer implement the method

Actually, not quite. At this point we don't really need to provide any extra layers of abstraction, we just need a concrete implementation of OAuth (using django-oauth-plus) and/or OAuth2 (using django-oauth2-provider).

Having good documentation for them is also really important. At the moment it's often quite hard to know where to start with OAuth, so we need to make sure we provide a really simple, easy guide.

swistakm commented 11 years ago

I think this could be done the same way it's done in django-tastypie. Their OAuthAuthentication class verifies only OAuth signature and checks if provided access token is valid. Obtaining of request token and access token still should be left outside rest framework (oauth dance). This will leave for devs choice of suitable oauth flow (three-legged, two-legged, xAuth etc.)

I don't see the point in making developer to implement his own validation methods for tokens and signatures. Maybe I'm wrong because I know know only OAuth 1.0a specification and perhaps 2.0 is more flexible (and needs such approach).

I recommend looking at tastypie code as a reference and starting point but AFAIK this solution is quite incomplete (e.g. it lacks validation of nonces).

tomchristie commented 11 years ago

@swistakm Agreed on all counts, they use django-oauth-plus and seems good to me.

Any more info about eg validation of nonces? Doesn't mention anything about that in the django-oauth-plus docs.

swistakm commented 11 years ago

@tomchristie there is already everything needed in django-oauth-plus (including nonces). I was only mentioning tastypie implementation which lacks this feature (what makes it vulnerable to reply attacks).

I have even working OAuth code for django-rest-framework but I hadn't time to clean it and add to lib. I hope I will find time this week to make pull request.

BTW oauth-plus wasn't very developed over last year but I already contacted its author and I will take lead of this project (because it still needs some fixes IMO)

dulacp commented 11 years ago

@tomchristie I agree, if we choose a specific backend (like django-oauth2-provider for OAuth2) then we can implement the validation method ourself.

Having good documentation for them is also really important. At the moment it's often quite hard to know where to start with OAuth, so we need to make sure we provide a really simple, easy guide.

Can't agree more. The issue is that django-oauth2-provider doesn't have a decent documentation currently. I've been through all the source code and the good news is that it is really well commented and brilliantly executed, so I think it's just a matter of time before they'd generate a complete documentation. I'll fill an issue on their project to ask them to update the online doc.

I need to implement an OAuth2 Authentication class for a current project of mine. The OAuth2 dance is working right now using the django-oauth2-provider backend. So I'll make a little PR with it and some documentation, and @swistakm you can handle the OAuth1 part :)

dbrgn commented 11 years ago

By the way, there's also the rauth library which apparently supports OAuth1.0/a, 2.0 and Ofly. Its only requirement is requests.

It's on readthedocs and well tested. But I've never used it yet and don't know exactly how well it covers everything we need.

michaelmior commented 11 years ago

@dbrgn It looks like rauth is for client-side only?

dbrgn commented 11 years ago

@michaelmior D'oh, I'm sorry, you're right :(