authlib icon indicating copy to clipboard operation
authlib copied to clipboard

Authorization error responses are missing the state parameter

Open jaap3 opened this issue 2 years ago • 0 comments

Describe the bug

While trying to implement a "silent signing" flow (using prompt=none) the library I use refuses to accept the error response (i.e. login_required) because the state parameter is missing from the response.

RFC 6749 section-4.1.2.1 has the following normative text about the required presence of the state parameter on authorisation error responses:

state REQUIRED if a "state" parameter was present in the client authorization request. The exact value received from the client.

For example, the authorization server redirects the user-agent by sending the following HTTP response:

HTTP/1.1 302 Found Location: https://client.example.com/cb?error=access_denied&state=xyz

To Reproduce

Send an authorization request with prompt=none&state=xyz while not authenticated.

Expected behavior

The error response contains the given state parameter.

Environment:

  • OS: Debian 11
  • Python Version: 3.8.16
  • Authlib Version: 1.1.0

Additional context

OAuth2Error has a way to include the state parameter, but it's not always used. i.e. validate_request_prompt does not set it on the errors it raises. Other places do include the state parameter, i.e. validate_authorization_redirect_uri.

For the time being I've implemented a workaround on my side (Django), by setting the state parameter on OAuth2Errors raised by get_consent_grant:

class OAuth2AuthorizeView(AccessMixin, View):
    def get(self, request, *args, **kwargs):
        user = request.user if request.user.is_authenticated else None
        try:
            grant = oauth2_server.get_consent_grant(request, user)
            if grant.prompt == "login":
                # Redirect to login page
                return self.handle_no_permission()
        except OAuth2Error as error:
            logger.exception("Error during authorization request")
            # Make sure the state parameter is reflected in the error response
            error.state = request.GET.get("state")
            return oauth2_server.handle_error_response(
                oauth2_server.create_oauth2_request(request),
                error,
            )
        ....

jaap3 avatar Jan 26 '23 08:01 jaap3