Skip to content

🚨(backend) fix search without refresh token#2090

Closed
mascarpon3 wants to merge 2 commits intomainfrom
fix-search-not-authenticated
Closed

🚨(backend) fix search without refresh token#2090
mascarpon3 wants to merge 2 commits intomainfrom
fix-search-not-authenticated

Conversation

@mascarpon3
Copy link
Contributor

@mascarpon3 mascarpon3 commented Mar 19, 2026

i remove the decorator on the search function
and refresh the token in the function so
i can try catch it.

Purpose

fix of #2087

Proposal

  • remove the refresh in the decorator
  • try catch the refresh in the function

External contributions

Thank you for your contribution! 🎉

Please ensure the following items are checked before submitting your pull request:

  • I have read and followed the contributing guidelines
  • I have read and agreed to the Code of Conduct
  • I have signed off my commits with git commit --signoff (DCO compliance)
  • I have signed my commits with my SSH or GPG key (git commit -S)
  • My commit messages follow the required format: <gitmoji>(type) title description
  • I have added a changelog entry under ## [Unreleased] section (if noticeable change)
  • I have added corresponding tests for new features or bug fixes (if applicable)

i remove the decorator on the search function
and refresh the token in the function so
i can try catch it.

Signed-off-by: charles <charles.englebert@protonmail.com>
@mascarpon3 mascarpon3 requested a review from lunika March 19, 2026 20:03
@mascarpon3 mascarpon3 marked this pull request as ready for review March 19, 2026 20:03
@lunika lunika requested a review from qbey March 19, 2026 20:07
)


def refresh_access_token(session):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is not tested, you should have dedicated unit tests.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, in this function you are not testing if the access token is expired or not. I don't see the interest in refreshing a still valid access token.

Also you are not refreshing the oidc_id_token_expiration parameter in the session

Comment on lines +1432 to +1439
try:
request.session = refresh_access_token(request.session)
except AuthenticationFailed:
logging.warning(
"User unauthenticated or error while refreshing token, "
"falling back to title search."
)
return self._title_search(request, params.validated_data, *args, **kwargs)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure this is the behavior we want.

You should probably put this code in the _search_with_indexer method and catch this exception in the try/except block calling the _search_with_indexer method.

Copy link
Contributor Author

@mascarpon3 mascarpon3 Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

honestly i think writing this in this function makes the code more readable. the main search function is in charged of all the dispatch to the sub-functions.

what i realise i should have done though is writing it bellow the if indexer is None sheck

Copy link
Member

@lunika lunika Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@drf.decorators.action(detail=False, methods=["get"], url_path="search")
    def search(self, request, *args, **kwargs):
        """
        Returns an ordered list of documents best matching the search query parameter 'q'.

        It depends on a search configurable Search Indexer. If no Search Indexer is configured
        or if it is not reachable, the function falls back to a basic title search.
        """
        params = serializers.SearchDocumentSerializer(data=request.query_params)
        params.is_valid(raise_exception=True)
        search_type = self._get_search_type()

        if search_type == SearchType.TITLE:
            return self._title_search(request, params.validated_data, *args, **kwargs)

        indexer = get_document_indexer()
        if indexer is None:
            # fallback on title search if the indexer is not configured
            return self._title_search(request, params.validated_data, *args, **kwargs)

        try:
            return self._search_with_indexer(
                indexer, request, params=params, search_type=search_type
            )
        except (requests.exceptions.RequestException, AuthenticationFailed) as e:
            logger.error(
                "Error while searching documents with indexer \n%s \nfall back on title search",
                e,
            )
            # fallback on title search if the indexer is not reached
            return self._title_search(request, params.validated_data, *args, **kwargs)

    def _get_search_type(self) -> SearchType:
        """
        Returns the search type to use for the search endpoint based on feature flags.
        If a user has both flags activated the most advanced search is used
        (HYBRID > FULL_TEXT > TITLE).
        A user with no flag will default to the basic title search.
        """
        if waffle.flag_is_active(self.request, FeatureFlag.FLAG_FIND_HYBRID_SEARCH):
            return SearchType.HYBRID
        if waffle.flag_is_active(self.request, FeatureFlag.FLAG_FIND_FULL_TEXT_SEARCH):
            return SearchType.FULL_TEXT
        return SearchType.TITLE

    @staticmethod
    def _search_with_indexer(indexer, request, params, search_type):
        """
        Returns a list of documents matching the query (q) according to the configured indexer.
        """
        request.session = refresh_access_token(request.session)
        queryset = models.Document.objects.all()

        results = indexer.search(
            q=params.validated_data["q"],
            search_type=search_type,
            token=request.session.get("oidc_access_token"),
            path=(
                params.validated_data["path"]
                if "path" in params.validated_data
                else None
            ),
            visited=get_visited_document_ids_of(queryset, request.user),
        )

        return drf_response.Response(
            {
                "count": len(results),
                "next": None,
                "previous": None,
                "results": results,
            }
        )

Well let me know if this code is less or more readable ?

Moreover, as the refresh_access_token is related to the refresh_access_token, I think it should be called in this method.

Comment on lines +150 to +151
@pytest.fixture(name="oidc_settings")
def fixture_oidc_settings(settings):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@pytest.fixture(name="oidc_settings")
def fixture_oidc_settings(settings):
@pytest.fixture
def oidc_settings(settings):

@lunika
Copy link
Member

lunika commented Mar 20, 2026

Can you also please update the readme ?
Thanks

Copy link
Member

@lunika lunika left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a bit more at this implementation, we are loosing all the benefice to use the django-lasuite implementation.

Using the decorator on the _search_with_indexer should be I think better but to do this you have to refactor the search and _search_with_indexer method to be compatible with the decorator. But I think it is the best way to fix this issue.

"client_secret": settings.OIDC_RP_CLIENT_SECRET,
"refresh_token": refresh_token,
},
timeout=5,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
timeout=5,
timeout=settings.OIDC_TIMEOUT,

)


def refresh_access_token(session):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, in this function you are not testing if the access token is expired or not. I don't see the interest in refreshing a still valid access token.

Also you are not refreshing the oidc_id_token_expiration parameter in the session

@mascarpon3 mascarpon3 force-pushed the fix-search-not-authenticated branch 5 times, most recently from fe962ee to 89286c7 Compare March 20, 2026 10:36
Signed-off-by: charles <charles.englebert@protonmail.com>
@lunika
Copy link
Member

lunika commented Mar 20, 2026

Closed in favor of #2097

@lunika lunika closed this Mar 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants