pycontribs / jira

Python Jira library. Development chat available on https://matrix.to/#/#pycontribs:matrix.org
https://jira.readthedocs.io
BSD 2-Clause "Simplified" License
1.95k stars 866 forks source link

TokenAuth can stop working if session cookie becomes invalid #1904

Open gmishkin opened 1 week ago

gmishkin commented 1 week ago

Bug summary

The Jira client initially works with token auth but eventually begins failing with a 401. A key part of the error response is

os_authType was 'any' and an invalid cookie was sent.

We ruled out any situation on our end where there might be multiple web servers involved setting separate session cookies and a load balancer not setting sticky session. There is only on web server so any session cookies should still be valid.

So we contacted Atlassian support and were given the suggestion: Disable cookies on outgoing requests: those are not necessary. The Authorization header is enough for authenticate REST requests.

There are two mechanisms involved here:

  • The Personal Access Token (PAT) that is used for authentication of your integration's requests to Jira.
  • The session cookie JSESSIONID that is created when a user session is established after the user has been authenticated. Every session has a JSESSIONID cookie regardless of authentication method. When that user session is closed (or times out) the session ID is removed from the active sessions list and any attempts to use the same session id from that on will be rejected.

So when you use the PAT to authenticate the normal flow is like this:

  1. Jira receives the request with the PAT and checks its validity to either authenticate the request or reject it.
  2. If the PAT authentication was successful, a session is established and a JSESSIONID cookie with a session id is sent back in Jira's response.

As you then have already established a session, you can then use the JSESSIONID cookie in subsequent requests to hook on to the same session without having to authenticate every single request. This is the same as for a user request in the UI. If you, on the other hand, send a stored JSESSIONID cookie with the first request to the REST API, it will be interpreted as that you want to hook on to a previously established session.

This session handling is taking place in the Tomcat web server, i.e. in the step before Jira, so the JSESSIONID will be checked first and if that given session id is not in the active sessions list (maintained by Tomcat) it will reject it.

Is there an existing issue for this?

Jira Instance type

Jira Server or Data Center (Self-hosted)

Jira instance version

9.16.1

jira-python version

3.8.0

Python Interpreter version

3.12.1

Which operating systems have you used?

Reproduction steps

# 1. Given a Jira client instance, which initially works
        jira_client: JIRA = JIRA(
            XXXX,
            token_auth=os.environ["JIRA_TOKEN"],
        )
# 2. Within a few minutes time period, when I call the function with argument x
jira.search_issues(x)
# 3. Get a 401 response with the body containing: os_authType was 'any' and an invalid cookie was sent. (note that we are not setting os_authType query string and library doesn't seem to either, also it is apparently deprecated anyway)

Stack trace

File "XXXXX/jira/client.py", line 3557, in search_issues
 issues = self._fetch_pages(
 ^^^^^^^^^^^^^^^^^^
 File "XXXXX/jira/client.py", line 817, in _fetch_pages
 resource = self._get_json(
 ^^^^^^^^^^^^^^^
 File "XXXXX/jira/client.py", line 4358, in _get_json
 else self._session.get(url, params=params)
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 File "XXXXX/requests/sessions.py", line 602, in get
 return self.request("GET", url, **kwargs)
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 File "XXXXX/jira/resilientsession.py", line 247, in request
 elif raise_on_error(response, **processed_kwargs):
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 File "XXXXX/jira/resilientsession.py", line 72, in raise_on_error
 raise JIRAError(
jira.exceptions.JIRAError: JiraError HTTP 401 url: https://jira.mongodb.org/rest/api/2/search?jql=XXXX&startAt=0&validateQuery=True&fields=comment&fields=components&fields=issuekey&fields=issuelinks&fields=issuetype&fields=labels&fields=priority&fields=resolution&fields=status&fields=summary&fields=created&maxResults=10000

Expected behaviour

Jira client to continue working indefinitely as long as token is valid.

Additional Context

requests==2.32.3

fabricat-mdb commented 1 week ago

I see a JiraCookieAuth.handle_401() function exists in client.py. Maybe the same can be useful also for the class TokenAuth?