aliev/aioauth

Implementation of implicit grant type is not fully compliant with RFC 6749

PrieJos opened this issue · 2 comments

Dear aioauth Team,

I would like to bring into your attention that the implementation of the Implicit Grant Type in aioauth is not fully compliant with the specification described in RFC 6749. In particular, this spec in section 4.2.2 says:

The authorization server MUST NOT issue a refresh token.

However, your implementation is returning a refresh token as part of the Access Token Response described in this section. In particular, the fragments attached to the client's redirect URI is including a refresh token like this (I am omitting the real tokens in the next output):

'http://localhost/0123456789/token#state=mYegmEQQMgQ&expires_in=3600&refresh_token_expires_in=86400&access_token=xyz&refresh_token=abc&scope=&token_type=Bearer'

Expected Result

The redirection URL should not include the refresh token fields as stated by the RFC 6749 like this:

'http://localhost/0123456789/token#state=mYegmEQQMgQ&expires_in=3600&access_token=xyz&scope=&token_type=Bearer'

Actual Result

See the example above that was taken out of one test execution.

Reproduction Steps

The next code excerpt was taken from my pytest cases:

class TestImplicitGrant:
    """Test cases for implicit grant flow."""

    def test_complete_flow(
        self,
        fastplay_client_factory: t.Callable[..., "TestClient"],
        client_info: ClientInfoTuple,
        request_state: str,
        http_basic: "HTTPBasicFixture",
        token_type: str,
    ) -> None:
        """Test a successful complete authorization code flow."""
        # Create test client
        fastplay_client = fastplay_client_factory(
            FASTPLAY_ACCESS_TOKEN_TYPE=token_type,
            FASTPLAY_RFRESH_TOKEN_TYPE=token_type,
        )

        # Authorization request
        authorization_response = fastplay_client.get(
            "/auth/oauth2/authorize",
            params={
                "response_type": "token",
                "client_id": client_info.client_id,
                "redirect_uri": client_info.redirect_uri,
                "scope": "",
                "state": request_state,
            },
            auth=(http_basic.username, http_basic.password),
        )

        # Token response check
        assert authorization_response.status_code == HTTPStatus.FOUND
        assert authorization_response.next_request is not None
        redirect_url = authorization_response.next_request.url
        assert str(redirect_url).startswith(client_info.redirect_uri)
        assert len(redirect_url.fragment) > 0
        redirect_fragments: dict[str, str] = dict(
            urllib.parse.parse_qsl(
                redirect_url.fragment,
                keep_blank_values=True,
            ),
        )
        assert redirect_fragments["state"] == request_state
        assert redirect_fragments["scope"] == ""
        assert redirect_fragments["token_type"].lower() == "bearer"
        assert len(redirect_fragments["access_token"]) > 0
        assert (
            "refresh_token" not in redirect_fragments
            or len(redirect_fragments["refresh_token"]) == 0
        )

System Information

Python Version (3.x): 3.12
aioauth Version (0.x): 1.6.0
Framework (FastAPI / starlette, aiohttp, sanic, etc.)
- fastapi 0.112.0
- starlette 0.37.2
aliev commented

Thanks for spotting this issue! I'll definitely look into it. In the meantime, if you already have a solution in mind, feel free to create a pull request.

Hi @aliev

Thanks for spotting this issue! I'll definitely look into it. In the meantime, if you already have a solution in mind, feel free to create a pull request.

No, really, thanks for your great work! I created the PR #99 that indeed contains the proposed solutions for this issue and the issue #97 that I also opened a couple days ago.

Cheers
Jose M. Prieto