django-rest-framework-social-oauth2 icon indicating copy to clipboard operation
django-rest-framework-social-oauth2 copied to clipboard

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

Open sgallen opened this issue 9 years ago • 34 comments

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):

  • Is this general workflow correct or am I misunderstanding something?
  • Does this library provide an endpoint that I can POST the third-party "code" to from Step 2, that will internally make the call to Github to obtain the access token (e.g. "auth/github/")? i.e. keeping the Github {client_secret} hidden from the client side. Or would I need to implement that piece of the puzzle?

sgallen avatar Oct 22 '16 23:10 sgallen

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.

PhilipGarnero avatar Oct 23 '16 11:10 PhilipGarnero

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

PraveshKoirala avatar Oct 24 '16 03:10 PraveshKoirala

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.

PhilipGarnero avatar Oct 25 '16 13:10 PhilipGarnero

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?

ghost avatar Jan 10 '17 09:01 ghost

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

PhilipGarnero avatar Jan 10 '17 10:01 PhilipGarnero

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 avatar Jan 10 '17 11:01 ghost

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

ghost avatar Jan 10 '17 13:01 ghost

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

PhilipGarnero avatar Jan 10 '17 16:01 PhilipGarnero

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"}

ghost avatar Jan 10 '17 16:01 ghost

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.

PhilipGarnero avatar Jan 10 '17 16:01 PhilipGarnero

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.

aronse avatar Jan 10 '17 17:01 aronse

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 ...

ghost avatar Jan 10 '17 17:01 ghost

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

PhilipGarnero avatar Jan 10 '17 17:01 PhilipGarnero

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

alexandcote avatar Jan 23 '17 01:01 alexandcote

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.

PhilipGarnero avatar Jan 23 '17 10:01 PhilipGarnero

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 the user doesn't exist in the database, we need to create it. After that, we have to return the token of the Django Application
  • If the user exists in the database, we only need to return the token of the Django Application

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 😃

alexandcote avatar Jan 23 '17 18:01 alexandcote

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.

PhilipGarnero avatar Jan 24 '17 09:01 PhilipGarnero

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.

hovi avatar Feb 04 '17 05:02 hovi

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

veseln avatar Feb 24 '17 20:02 veseln

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.

hovi avatar Feb 28 '17 18:02 hovi

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?

erik-sn avatar Mar 22 '17 02:03 erik-sn

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

moonwolf-github avatar Mar 29 '17 16:03 moonwolf-github

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

ashishpahwa7 avatar May 22 '17 16:05 ashishpahwa7

@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.

alexandcote avatar May 22 '17 17:05 alexandcote

@alexandcote Thank you

ashishpahwa7 avatar May 24 '17 07:05 ashishpahwa7

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.

geokinski avatar Jun 06 '17 12:06 geokinski

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.

jturmel avatar Aug 02 '17 04:08 jturmel

IS this fixed and available to use?

anoopksh avatar Jul 26 '18 13:07 anoopksh

Any update on this?

DanishKhakwani avatar Nov 20 '18 21:11 DanishKhakwani

@PhilipGarnero any update on this?

omushpapa avatar Jul 28 '19 13:07 omushpapa