drf-social-oauth2 icon indicating copy to clipboard operation
drf-social-oauth2 copied to clipboard

Github - convert_token

Open LearningProcesss opened this issue 4 years ago • 4 comments

Hi there, how the github implementation is supposed to work? Examples that i found all relate to Google or Facebook where those providers return an accessToken while Github returns a code. At the moment i'm stuck at the point where i have the code from Github and i would convert into a token, so i do my POST request like this:

POST http://localhost:8000/auth/convert-token HTTP/1.1 Content-Type: application/x-www-form-urlencoded

grant_type=convert_token &client_id=aIK0dQjlOlDe9UwV0pZfR2DlEBX8HrqdqhSD1iNr &client_secret=RXc9lTKqadPixO6ZoNiC8TWXPb7iLnQ5VeSMu0TXNkrnMVGynhHiDwzlNW6B1OftRpZ6nvWjpPiu2cA1aV0Iv7vgugwbXp1FOOCvnWHFSTeZbYrWxAbiX4dkTM7pVfEC &backend=github &token=35fa2fe846c3f6867e63 <- code from Github

(client_id e secret comes from django admin)

DRF response:

HTTP/1.1 400 Bad Request

{
  "error": "access_denied",
  "error_description": "Your credentials aren't allowed"
}

Below my settings

INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'rest_framework', 'oauth2_provider', 'social_django', 'drf_social_oauth2', ]

TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', 'social_django.context_processors.backends', 'social_django.context_processors.login_redirect', ], }, }, ]

REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( 'oauth2_provider.contrib.rest_framework.OAuth2Authentication', 'drf_social_oauth2.authentication.SocialAuthentication', ), }

AUTHENTICATION_BACKENDS = ( 'social_core.backends.github.GithubOAuth2', 'drf_social_oauth2.backends.DjangoOAuth2', 'django.contrib.auth.backends.ModelBackend', )

SOCIAL_AUTH_GITHUB_KEY = 'my key' SOCIAL_AUTH_GITHUB_SECRET = 'my secret'

SOCIAL_AUTH_PIPELINE = ( 'social_core.pipeline.social_auth.social_details', 'social_core.pipeline.social_auth.social_uid', 'social_core.pipeline.social_auth.auth_allowed', 'social_core.pipeline.social_auth.social_user', 'social_core.pipeline.social_auth.associate_user', 'social_core.pipeline.social_auth.load_extra_data', 'social_core.pipeline.user.user_details', )

Sure i'm missing something.

Best regards, Mattia

LearningProcesss avatar Jun 05 '21 23:06 LearningProcesss

See if this post provides any help: https://github.com/RealmTeam/django-rest-framework-social-oauth2/issues/72

sgallen avatar Jun 13 '21 19:06 sgallen

@LearningProcesss, Did you manage to get this to work?

I am sorry but my time is really limited. I didn't get much help maintaining this repo.

wagnerdelima avatar Dec 28 '21 17:12 wagnerdelima

Hi @LearningProcesss. First you need to exchange the code for access_token. This is done by sending a POST request to https://github.com/login/oauth/access_token with body:

{
    "client_id": "string",
    "client_secret": "string",
    "code": "string"
}

Client credentials are in this case from the OAuth App on GitHub.

Now the obtained GitHub access_token can be used in your case, just replace it for the code in token=code.

MilanZiaran avatar Jan 18 '22 16:01 MilanZiaran

Hey, I'm facing the very same issue. I was able to exchange the "code" for "access_token" by sending request (as mentioned by @MilanZiaran ) but I was struggling a lot on this line of code. The DrfSO library queries a database here to check for existing AccessToken objects for current user/application, if such token does not exist it will "create the request again, as a convert_token grant type". Calling such request obviously fail as the "code" was already used (already exchanged for "access_token") in previous step - and there is no way how to pass the obtained "access_token"

I've modified DrfSO library in my fork which basically reverts changes made by @wagnerdelima

Calling a convert-token now creates new token for every call which is still better than failing on an error "Your credentials aren't allowed".

I would like to open a PR if I would be sure reverting such changes is a way to go - maybe the token recreation should be handled by underlying library but IMO "recreating request" is something that is not a part of OAuth. Correct me if I'm wrong.

One more thing - I've modified GithubOAuth2 backend accordingly

class GitHubOAuth2(GithubOAuth2):
    @handle_http_errors
    def do_auth(self, access_token, *args, **kwargs):
        access_token = self.exchange_code_for_access_token(access_token)
        return super(GitHubOAuth2, self).do_auth(access_token, *args, **kwargs)

    @handle_http_errors
    def exchange_code_for_access_token(self, access_token):
        response = self.request_access_token(
            self.ACCESS_TOKEN_URL,
            data={
                "client_id": self.setting("SOCIAL_AUTH_GITHUB_KEY"),
                "client_secret": self.setting("SOCIAL_AUTH_GITHUB_SECRET"),
                "code": access_token,
                "scope": "user,user:email",
            },
            headers=self.auth_headers(),
            method=self.ACCESS_TOKEN_METHOD,
        )
        self.process_error(response)
        return response["access_token"]

EDIT: I¨m investigating what happened at jazzband/django-oauth-toolkit#1058

SukiCZ avatar Sep 11 '22 15:09 SukiCZ

@LearningProcesss @SukiCZ @MilanZiaran

Adding GitHub Sign In is easy. Please refer to the GitHub section in the README file.

wagnerdelima avatar Mar 26 '23 20:03 wagnerdelima

@wagnerdelima Yes, it works as described in documentation where developer generates an Access Token that can be used multiple times. It doesn't work in production run, when GitHub issues a code (that can only be used once) which needs to be converted to an Access Token.

SukiCZ avatar Apr 08 '23 21:04 SukiCZ

https://github.com/login/oauth/access_token

@SukiCZ thanks for your reply. Unfortunately I could not authenticate and get the token from the first step: https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps#1-request-a-users-github-identity.

I keep getting an 422Unprocessable Entity execption. I have been trying for hours.

Let me know if you can help.

wagnerdelima avatar Apr 21 '23 11:04 wagnerdelima

@wagnerdelima I would like to help but I wasn't able to reproduce 422 error. I've made a small Django app that demonstrates working GitHub OAuth. Feel free to have a look https://github.com/SukiCZ/tmp_django

SukiCZ avatar Apr 26 '23 17:04 SukiCZ

I will take a look at it, @SukiCZ

wagnerdelima avatar Apr 28 '23 11:04 wagnerdelima

@SukiCZ thanks for your demo project. It helped me understand the flow from /authorize to /convert-token.

I updated the documentation of GitHub. You can find it here https://drf-social-oauth2.readthedocs.io/en/latest/integration.html#github-integration.

I did not understand why you needed to create a custom github baseauth. drf-social-oauth2 handled it without any other setup other than those listed in the GitHub settings. Let me know if I am missing something.

@LearningProcesss hope this helps.

wagnerdelima avatar Apr 28 '23 15:04 wagnerdelima

sorry @wagnerdelima if I wasn't clear but the demo project is using modified version of drf-social-oauth2 + modfied BaseAuth.

Without any modification the OAuth flow starts with GET request to https://api.github.com/user with headers: {"Authorization": "token 35fa2fe846c3f6867e63", "User-Agent": "social-auth-4.4.2"} which fails and the flow ends with

{
  "error": "access_denied",
  "error_description": "Your credentials aren't allowed"
}

The modified version of drf-social-oauth2 basically reverts changes from one of your commits where the flow was triggered twice. The modified BaseAuth sends a POST request to https://github.com/login/oauth/access_token with data

{
    "client_id": "<github client ID>",
    "client_secret": "<github client secret>",
    "code": "35fa2fe846c3f6867e63",
    "scope": "user,user:email"
}

which returns access_token (Bearer) that can be used to finish the flow.

SukiCZ avatar Apr 28 '23 17:04 SukiCZ