Example #1
0
    def validate_access_token_request(self):
        """
        Override the parent method from authlib to not fail immediately for
        public clients.
        """
        client = self.authenticate_token_endpoint_client()
        if not client.check_grant_type(self.GRANT_TYPE):
            raise UnauthorizedClientError(uri=self.uri)
        self._authenticated_client = client

        refresh_token = self.params.get("refresh_token")
        if refresh_token is None:
            raise InvalidRequestError('Missing "refresh_token" in request.',
                                      uri=self.uri)

        refresh_claims = self.authenticate_refresh_token(refresh_token)
        if not refresh_claims:
            raise InvalidRequestError('Invalid "refresh_token" in request.',
                                      uri=self.uri)

        scope = self.params.get("scope")
        if scope:
            original_scope = refresh_claims["scope"]
            if not original_scope:
                raise InvalidScopeError(uri=self.uri)
            original_scope = set(scope_to_list(original_scope))
            if not original_scope.issuperset(set(scope_to_list(scope))):
                raise InvalidScopeError(uri=self.uri)

        self._authenticated_token = refresh_claims
Example #2
0
    def create_token_response(self):
        """
        OVERRIDES method from authlib.

        Docs from authlib:

            If valid and authorized, the authorization server issues an access
            token as described in Section 5.1. If the request failed
            verification or is invalid, the authorization server returns an
            error response as described in Section 5.2.
        """
        credential = self.request.credential
        user = self.authenticate_user(credential)
        if not user:
            raise InvalidRequestError('There is no "user" for this token.')

        scope = self.request.scope
        if not scope:
            scope = credential["aud"]

        client = self.request.client
        expires_in = credential["exp"]
        token = self.generate_token(client,
                                    self.GRANT_TYPE,
                                    user=user,
                                    expires_in=expires_in,
                                    scope=scope)
        # TODO
        flask.current_app.logger.info("")

        self.request.user = user
        self.server.save_token(token, self.request)
        token = self.process_token(token, self.request)
        return 200, token, self.TOKEN_RESPONSE_HEADER
Example #3
0
    def create_token_response(self):
        """
        OVERRIDES method from authlib.

        Docs from authlib:

            If valid and authorized, the authorization server issues an access
            token as described in Section 5.1. If the request failed
            verification or is invalid, the authorization server returns an
            error response as described in Section 5.2.
        """
        credential = self.request.credential
        user = self.authenticate_user(credential)
        if not user:
            raise InvalidRequestError('There is no "user" for this token.')

        scope = self.request.scope
        if not scope:
            scope = credential["aud"]

        client = self.request.client
        expires_in = credential["exp"]
        token = self.generate_token(client,
                                    self.GRANT_TYPE,
                                    user=user,
                                    expires_in=expires_in,
                                    scope=scope)

        # replace the newly generated refresh token with the one provided
        # this prevents refreshing a refresh token in order to meet
        # the security requirement that users must authenticate every
        # 30 days
        #
        # TODO: this could be handled differently, we could track last authN
        #       and still allow refreshing refresh tokens
        if self.GRANT_TYPE == "refresh_token":
            token["refresh_token"] = self.request.data.get("refresh_token", "")

        # TODO
        logger.info("")

        self.request.user = user
        self.server.save_token(token, self.request)
        token = self.process_token(token, self.request)
        return 200, token, self.TOKEN_RESPONSE_HEADER
Example #4
0
def _get_auth_response_for_prompts(prompts, grant, user, client, scope):
    """
    Get response based on prompt parameter. TODO: not completely conforming yet

    FIXME: To conform to spec, some of the prompt params should be handled
    before AuthN or if it fails (so adequate and useful errors are provided).

    Right now the behavior is that the endpoint will just continue to
    redirect the user to log in without checking these params....

    Args:
        prompts (TYPE): Description
        grant (TYPE): Description
        user (TYPE): Description
        client (TYPE): Description
        scope (TYPE): Description

    Returns:
        TYPE: Description
    """
    show_consent_screen = True

    if prompts:
        prompts = prompts.split(" ")
        if "none" in prompts:
            # don't auth or consent, error if user not logged in
            show_consent_screen = False

            # if none is here, there shouldn't be others
            if len(prompts) != 1:
                error = InvalidRequestError(
                    state=grant.params.get("state"), uri=grant.params.get("uri")
                )
                return _get_authorize_error_response(
                    error, grant.params.get("redirect_uri")
                )

            try:
                get_current_user()
                response = server.create_authorization_response(user)
            except Unauthorized:
                error = AccessDeniedError(
                    state=grant.params.get("state"), uri=grant.params.get("uri")
                )
                return _get_authorize_error_response(
                    error, grant.params.get("redirect_uri")
                )

        if "login" in prompts:
            show_consent_screen = True
            try:
                # Re-AuthN user (kind of).
                # TODO (RR 2018-03-16): this could also include removing active
                # refresh tokens.
                flask.session.clear()

                # For a POST, return the redirect in JSON instead of headers.
                if flask.request.method == "POST":
                    redirect_response = flask.make_response(
                        flask.jsonify({"redirect": response.headers["Location"]})
                    )
                else:
                    redirect_response = flask.make_response(
                        flask.redirect(flask.url_for(".authorize"))
                    )

                clear_cookies(redirect_response)
                return redirect_response
            except Unauthorized:
                error = AccessDeniedError(
                    state=grant.params.get("state"), uri=grant.params.get("uri")
                )
                return _get_authorize_error_response(
                    error, grant.params.get("redirect_uri")
                )

        if "consent" in prompts:
            # show consent screen (which is default behavior so pass)
            pass

        if "select_account" in prompts:
            # allow user to select one of their accounts, we
            # don't support this at the moment
            pass

    if show_consent_screen:
        shown_scopes = [] if not scope else scope.split(" ")
        if "openid" in shown_scopes:
            shown_scopes.remove("openid")

        enabled_idps = flask.current_app.config.get("OPENID_CONNECT", {})
        idp_names = []
        for idp, info in enabled_idps.iteritems():
            # prefer name if its there, then just use the key for the provider
            idp_name = info.get("name") or idp.title()
            idp_names.append(idp_name)

        resource_description = [
            SCOPE_DESCRIPTION[s].format(idp_names=" and ".join(idp_names))
            for s in shown_scopes
        ]

        response = flask.render_template(
            "oauthorize.html",
            grant=grant,
            user=user,
            client=client,
            app_name=flask.current_app.config.get("APP_NAME"),
            resource_description=resource_description,
        )

    return response
Example #5
0
def _get_auth_response_for_prompts(prompts, grant, user, client, scope):
    """
    Get response based on prompt parameter. TODO: not completely conforming yet

    FIXME: To conform to spec, some of the prompt params should be handled
    before AuthN or if it fails (so adequate and useful errors are provided).

    Right now the behavior is that the endpoint will just continue to
    redirect the user to log in without checking these params....

    Args:
        prompts (TYPE): Description
        grant (TYPE): Description
        user (TYPE): Description
        client (TYPE): Description
        scope (TYPE): Description

    Returns:
        TYPE: Description
    """
    show_consent_screen = True

    if prompts:
        prompts = prompts.split(' ')
        if 'none' in prompts:
            # don't auth or consent, error if user not logged in
            show_consent_screen = False

            # if none is here, there shouldn't be others
            if len(prompts) != 1:
                error = InvalidRequestError(
                    state=grant.params.get('state'),
                    uri=grant.params.get('uri')
                )
                return _get_authorize_error_response(
                    error, grant.params.get('redirect_uri'))

            try:
                get_current_user()
                response = server.create_authorization_response(user)
            except Unauthorized:
                error = AccessDeniedError(
                    state=grant.params.get('state'),
                    uri=grant.params.get('uri')
                )
                return _get_authorize_error_response(
                    error, grant.params.get('redirect_uri'))

        if 'login' in prompts:
            show_consent_screen = True
            try:
                # re-AuthN user
                # TODO not sure if this really counts as re-AuthN...
                handle_login(scope)
            except Unauthorized:
                error = AccessDeniedError(
                    state=grant.params.get('state'),
                    uri=grant.params.get('uri')
                )
                return _get_authorize_error_response(
                    error, grant.params.get('redirect_uri'))

        if 'consent' in prompts:
            # show consent screen (which is default behavior so pass)
            pass

        if 'select_account' in prompts:
            # allow user to select one of their accounts, we
            # don't support this at the moment
            pass

    if show_consent_screen:
        shown_scopes = scope.split(' ')
        if 'openid' in shown_scopes:
            shown_scopes.remove('openid')
        resource_description = [
            SCOPE_DESCRIPTION[scope] for scope in shown_scopes]
        response = flask.render_template(
            'oauthorize.html', grant=grant, user=user, client=client,
            app_name=flask.current_app.config.get('APP_NAME'),
            resource_description=resource_description
        )

    return response