RealmTeam / django-rest-framework-social-oauth2

python-social-auth and oauth2 support for django-rest-framework
MIT License
1.05k stars 190 forks source link

What is the flow to convert a code to an access_token that can then be POSTed to /auth/convert-token? #72

Open sgallen opened 7 years ago

sgallen commented 7 years ago

I've worked my way through the tutorial, attempting to setup Github as the authorization backend. I'm trying to wrap my head around the proper workflow between client application and the Django powered REST API.

My understanding is that I need to do the following:

  1. Direct the user to: https://github.com/login/oauth/authorize?client_id={client_id}
  2. Assuming the user authorizes my application, the previous call will, according to the application setup in Github, callback to the specified url with a "code" as a query param.
  3. That "code" then needs to be sent in the following way to Github to obtain a third-party "access_token": https://github.com/login/oauth/access_token?client_id={client_id}&client_secret={client_secret}&code={code}
  4. The previous call returns the third-party access_token that I can then POST to http://localhost:8000/auth/convert-token, which will in turn send an access_token that I should use on all HTTP requests with DRF.

Question(s):

PhilipGarnero commented 7 years ago

The workflow you described is correct, yes.

The steps one two and three depend on your client app. These steps are often particular to your client app or the third party authorizer. If your client app is a web app using the django template engine, you could use python-social-auth to automate the step one and two.

Feel free to reopen if you have more questions.

PraveshKoirala commented 7 years ago

Is there any way we can avoid exposing the {client_secret} as sgallen asked?

PhilipGarnero commented 7 years ago

That would depend on github. If they're using a normal oauth2 flow, you should be able to specify your client type (public/confidential or server/app) and this would allow you to keep your app secret hidden.

ghost commented 7 years ago

I closed my current issue, since this one is the same. How did you solve step 3? did you create new endpoint, or is this endpint included in package somewhere?

I'm working with google sign.in for server-side apps and looking at their guide: https://developers.google.com/identity/sign-in/web/server-side-flow

The guide says that user must send one-time code to server, the server makes request to google, is this library supporting this?

PhilipGarnero commented 7 years ago

You should try to use Google Oauth2 instead of openid. It is similar to the facebook example you can find on my readme. If you are stuck implementing google oauth2 flow, you can look over here

ghost commented 7 years ago

But he flow is the same, google returns code and that code must be sent to django api and from there I need to make another request to google to get access token. I'm going through your examples and i don't see such feature...

ghost commented 7 years ago

The only thing I'm not getting is how you get from code returned by oauth2 to access token using your package... And this is bothering me for 2 days now. I don't see this in your facebook example

PhilipGarnero commented 7 years ago

You use the convert-token endpoint. It's written in the doc.

ghost commented 7 years ago

Uhm.. isn't convert-token used to convert access_token not code returned by google oauth2? I'm talking about this: https://developers.google.com/identity/sign-in/web/server-side-flow

The user clicks the sign-in button and grants your app access to the permissions that you requested. Then, the callback function that you specified in the grantOfflineAccess().then() method is passed a JSON object with an authorization code. For example:

{"code":"4/yU4cQZTMnnMtetyFcIWNItG32eKxxxgXXX-Z4yyJJJo.4qHskT-UtugceFc0ZRONyF4z7U4UmAI"}

PhilipGarnero commented 7 years ago

You don't need to worry about anything server side, it is handled on my side using python-social-auth.

Simply follow the steps over here and you'll get yourself an access token. Then all you need to do is converting it using convert-token.

aronse commented 7 years ago

I'm not quite sure about the issue you guys are experiencing, I've followed the steps listed in the readme step by step and it worked instantly.

ghost commented 7 years ago

It's working if you are using client-side flow described here: https://developers.google.com/identity/protocols/OAuth2UserAgent

but if you try to use server-side flow it's a bit more complicated: https://developers.google.com/identity/sign-in/web/server-side-flow

But I think server-side flow is more correct to use in my case. But I guess this will do ...

PhilipGarnero commented 7 years ago

This package is directed to client side flows. If you wanted a server-side flow, you'd have to reimplement it yourself.

alexandcote commented 7 years ago

Android Google SDK use server-side-flow by default. You have to use a deprecated version to work with the client-side flow. Google and Facebook recommends to use the server-side flow too. I think it would be a good feature for this package to support both.

image

Any idea how we can help you with that ? Here the information I found : https://github.com/python-social-auth/social-app-django/issues/18 Thanks for the great work by the way @PhilipGarnero

PhilipGarnero commented 7 years ago

I am not getting what you guys are trying to do with the server-side flow.
It is already handled by my library.

If you want to do something purely server-side, with no client-side involved, you can do this using python-social-auth directly; however, I don't see the point in using my package if you don't want any client side involved in the authentication process.

I may be missing something here and if that is the case, I'll need more explanations and links to documentations or example about what you are trying to do.

alexandcote commented 7 years ago

Hi @PhilipGarnero,

I don't think you understand correctly the flow that we talk. I will try to explain you my use case.

Since the new version of the Android SDK, Google prefers the Authorization Code flow over the Implicit flow. So when on my android device I click on the button Continue with Google, Google returns me a one time authorization code and not an access token. The authorization code needs to be exchanged for an access token on the server to respect the new security standard.

My suggestion is to add a new grant-type to the convert token to support this feature.

{
    "grant_type": "authorization-code",
    "backend": "<backend>",
    "code": "<social_authorization_code>"
}

This will call google and convert the authorization code to an access token.

If you want to test this flow with Facebook, here is a screenshot of my application configuration: image

If you want more information on facebook, here is a video that explains the authorization code flow: https://www.facebook.com/FacebookforDevelopers/videos/10152795636318553/ If you want more information on google: https://developers.google.com/actions/develop/identity/oauth2-code-flow

If you have any questions or if we can help you implement this feature, let me know. Can you re-open this issue ?

Hi @omab, do you know if this flow is supported in you library ?

Thanks guys

P.S Je parle Français si jamais 😃

PhilipGarnero commented 7 years ago

Oh, now I get what you meant.

I've looked into that and will do as soon as I get some time.

Note for future self (or anyone willing to try) : The goal is to use the oauth_complete method of the backend (completely or partially) over here instead of do_auth (there will also be some modifications on the rest of this method to allow both tokens and codes). In order to use oauth_complete correctly, we'll need to modify the backend's data property to introduce the authorization code because of this and find a way around the state property because of this leading to there leading to there where we'll probably have to monkey patch our way.

Currently the api is :

curl -X POST -d "grant_type=convert_token&client_id=<client_id>&client_secret=<client_secret>&backend=<backend>&token=<backend_token>" http://localhost:8000/auth/convert-token

I don't want to change this too much so I'm thinking of using the exact same route and use code instead of token if you're using an authorization code like so :

curl -X POST -d "grant_type=convert_token&client_id=<client_id>&client_secret=<client_secret>&backend=<backend>&code=<backend_authorization_code>" http://localhost:8000/auth/convert-token

You might say the route name isn't matching anymore because this is now a code but you are actually still converting a token. The only difference is that you won't see the token at all this time but it will still be created and converted on my side.

hovi commented 7 years ago

Hello, I am dealing with the exact problem. Took me a few days to figure out what's wrong and what is it exactly that I need. I'd be happy to provide full tutorial for google-oauth2 (and in combination with Google sign in for android) afterwards if it helps someone.

veseln commented 7 years ago

I would love to hear the tutorial for google-oauth2 as I am facing the same problems.

hovi commented 7 years ago

Well I am still missing that last piece grant_type=convert_token. For now I am using dirty (and probably insecure) workaround for testing purposes when calling from android.

erik-sn commented 7 years ago

I got stuck using github oauth2 translating the code -> github access_token -> django authenticated token.

/convert-token/ seems to take care of github token -> django token, but I couldn't find anything in the package that did the same for code -> github token. So I wrote my own - it is rough and needs error handling but maybe it will help someone get started. Basically I just POST the code string from JavaScript to an API endpoint and respond with the token/user:

https://gist.github.com/erik-sn/923843ee673df47b8ca2e4a830f1f985

The confusion between client_id and client_secret tripped me up pretty badly. There are multiple - the ones you configure in the github applications page (or twitter/google, w/e), as well as the ones you make by yourself in your admin / Django OAuth Toolkit / Applications interface.

Is there an implementation to do this through python-social-auth directly?

moonwolf-github commented 7 years ago

Any progress with this (or workaround)? i was just kicked by this issue (with google-oauth2) and trying to work this out.

ashishpahwa7 commented 7 years ago

Is it okay to embed client id and secret (for third party authentication) in mobile application ? Is there any alternate option ?

alexandcote commented 7 years ago

@ashishpahwa7 No... You should use the Authorization Code flow. The main reason is that you can decompile your mobile application and find the secret code. With the Authorization Code flow, you only need the client id on the mobile. The server will transform the authorization code provided by the mobile into a access token.

ashishpahwa7 commented 7 years ago

@alexandcote Thank you

geokinski commented 7 years ago

It is a major security flaw to store server client secret on the mobile app. Hence, the access token must be generated on the server, never on the client app.

jturmel commented 6 years ago

Something to keep in mind on this too is Facebook's flow is a bit wonky for their code flow. If you have native apps utilizing their SDK (which more than likely is what this project targets), they will be handing an access token to you (no way to get a code using their SDK, which is the best user experience and recommended). It's what Facebook calls their "long-lived" token, which means it's good for 60 days. If you want to get ahold of the expires time for this, you need to then take that access token, make their code call with that access token, which will give you a code to then use to make another call to get the access token with expires, you can then store this server side and know reliably when you'll need to refresh that access token before it expires... once it expires you'll need to have the user do the auth flow again, which isn't ideal. To get around that, and I realize this part is outside the scope of this project, you'll need a background process/job (ie cron, etc) that checks for tokens that are near their EOL and refresh them before they expire.

anoopksh commented 5 years ago

IS this fixed and available to use?

DanishKhakwani commented 5 years ago

Any update on this?

omushpapa commented 4 years ago

@PhilipGarnero any update on this?

GabriellBP commented 4 years ago

@PhilipGarnero any update?

knowmelife commented 4 years ago

@PhilipGarnero any update?

wagnerdelima commented 4 years ago

Hi all.

My team and I are constantly using this framework and it seems it has died out there. I contacted the owner by email asking if he would add some of us as maintainers so we could continue to improve it. However we didn't get a response.

I am publishing the project under my profile and we are going to continue to invest time in it.

So I would like to gently ask you to contribute to this project on: https://github.com/wagnerdelima/drf-social-oauth2

Thank you for understanding.

mayankwadhwani commented 4 years ago

@knowmelife @GabriellBP Did you guys find any workaround for Google Oauth2 based authentication. I have been following for days now without any leads.