o-nikolas commented on code in PR #72825:
URL: https://github.com/apache/airflow/pull/72825#discussion_r3974256911


##########
providers/fab/src/airflow/providers/fab/www/extensions/init_session.py:
##########
@@ -62,3 +70,32 @@ def make_session_permanent():
             f"[fab] session_backend: '{selected_backend}'. Please set "
             "this to either 'database' or 'securecookie'."
         )
+
+
+def init_session_max_lifetime(app):
+    """Expire sessions ``[fab] session_max_lifetime_minutes`` after login, 
regardless of activity."""
+    max_lifetime_minutes = conf.getint("fab", "session_max_lifetime_minutes", 
fallback=0)
+    if max_lifetime_minutes <= 0:
+        return
+
+    max_lifetime_seconds = max_lifetime_minutes * 60
+
+    # ``weak=False``: the receiver is a local function, so a weak subscription 
could be collected.
+    @user_logged_in.connect_via(app, weak=False)
+    def stamp_login_time(sender, user, **kwargs):
+        # Wall clock rather than ``time.monotonic()``: the stamp is persisted 
in the session and
+        # read back by other API server processes, which share no monotonic 
clock origin.
+        builtin_flask_session[SESSION_LOGIN_TIME_KEY] = time.time()
+
+    @app.before_request
+    def expire_session_past_max_lifetime():
+        login_time = builtin_flask_session.get(SESSION_LOGIN_TIME_KEY)
+        if login_time is None:
+            # Sessions that predate this setting have no stamp; cap them from 
now on rather than
+            # leaving them exempt forever.
+            if current_user.is_authenticated:
+                builtin_flask_session[SESSION_LOGIN_TIME_KEY] = time.time()
+            return
+        if time.time() - login_time > max_lifetime_seconds:
+            log.debug("Session reached [fab] session_max_lifetime_minutes, 
expiring it.")
+            builtin_flask_session.clear()

Review Comment:
   My agent seems to think calling `logout_user()` is more effective than 
`builtin_flask_session.clear()` (or both if really needed). Worth circling back 
with yours about.



##########
providers/fab/provider.yaml:
##########
@@ -208,11 +208,34 @@ config:
       session_lifetime_minutes:
         description: |
           The UI cookie lifetime in minutes. User will be logged out from UI 
after
-          ``[fab] session_lifetime_minutes`` of non-activity
+          ``[fab] session_lifetime_minutes`` of inactivity: the deadline 
slides forward on every
+          request, so it is only reached once the session has been idle for 
the whole period.
+
+          Note that leaving an Airflow UI tab open counts as activity even 
when nobody is at the
+          keyboard. The UI polls the API in the background and silently 
re-authenticates whenever
+          its API token expires, which keeps sliding the deadline, so a 
session with an open tab is
+          never idle and never expires. Use ``[fab] 
session_max_lifetime_minutes`` to log users out
+          after a fixed period regardless of activity.
         version_added: 2.0.0
         type: integer
         example: ~
         default: "43200"
+      session_max_lifetime_minutes:
+        description: |
+          Maximum lifetime of a UI session in minutes, counted from the login 
time and never
+          extended by activity. Unlike ``[fab] session_lifetime_minutes``, 
this deadline is
+          reached even when the user keeps working in the UI, so it forces 
periodic
+          re-authentication. Set to ``0`` (the default) to disable it.
+
+          The session is only checked when the UI talks to the auth manager, 
which it does when
+          its API token expires, so ``[api_auth] jwt_expiration_time`` bounds 
how precisely the
+          deadline is enforced: with the default 24 hour token, a shorter 
maximum lifetime still
+          takes up to 24 hours to log the user out. Set ``[api_auth] 
jwt_expiration_time`` no

Review Comment:
   My agent seems to think this gap is worse, if I understand correctly:
   
   >  The config text is honest that [api_auth] jwt_expiration_time bounds the 
precision. But the effective worst case is
     session_max_lifetime_minutes + jwt_expiration_time, and with the shipped 
defaults that's the config's own example (480 minutes) plus 86400
     seconds — up to roughly 32 hours after login for someone who thinks they 
configured 8. Even after the session is cleared, an already-issued
     JWT stays valid until it expires; the session isn't the boundary for API 
calls.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to