Skip to content
This repository was archived by the owner on Dec 6, 2024. It is now read-only.
This repository was archived by the owner on Dec 6, 2024. It is now read-only.

Potential security issue when using "Authorization Code" flow #14

@jdmwood

Description

@jdmwood

Hi there,

I think I have found a security problem.

In views.py in _get_access_token_jwt() it allows embedding the username in the JWT payload:

if request.POST.get('username'):
    extra_data['username'] = request.POST.get('username')

This username is then used in authentication.py to figure out who the user is.

But, when using the "Authorization Code" flow, I can't see how the username is verified in views.py to check that it actually is the same as the user who is requesting the access code.

I experimented and I found that I was able to get an access code with user A, but then enter a different user in the POST request to get the token and it just accepted this.

Here is a test case to prove this issue. Add the following code to test_views.py:

    def test_get_token_authorization_code_wrong_user(self):
        """
        Request an access token using Authorization Code Flow
        """
        Application.objects.create(
            client_id='user_app_id',
            client_secret='user_app_secret',
            client_type=Application.CLIENT_CONFIDENTIAL,
            authorization_grant_type=Application.GRANT_AUTHORIZATION_CODE,
            name='user app',
            skip_authorization=True,
            redirect_uris='http://localhost:8002/callback',
        )

        self.client.force_login(self.test_user)

        response = self.client.get(reverse("oauth2_provider_jwt:authorize") +
                                   '?response_type=code&client_id=user_app_id')

        self.assertEqual(302, response.status_code)
        match = re.match(r'http://localhost:8002/callback\?code=(\w+)',
                         response.url)
        self.assertIsNotNone(match)
        code = match.group(1)

         # To simulate that the token call is normally made unauthenticated
        self.client.logout()
        data = {
            'client_id': 'user_app_id',
            'client_secret': 'user_app_secret',
            'code': code,
            'grant_type': 'authorization_code',
            'redirect_uri': 'http://localhost:8002/callback',
            'username': 'some_fake_user',  # Pass in wrong user
        }
        response = self.client.post(reverse("oauth2_provider_jwt:token"), data)
        self.assertNotEqual(200, response.status_code)

Near the end, we pass in the wrong username. This is bad because this username is baked into the JWT payload.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions