We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Track sign in status in user session
## Background

Once the user successfully signs in to their Login.gov account, we will receive a response from the authentication server indicating their status. We can store the fact the the user is then "signed in" in their session using the existing [`session` framework](https://github.com/cal-itp/benefits/blob/dev/benefits/core/session.py) which keeps everything local to the user in secure cookies.

This task is just about setting up the session code to be able to track this, but not actually getting the status in there (see #373 for that).

Check out #321 / #330 as an example of a similar addition last Sprint.

## Tasks

- [x] Add a [new key `_AUTH = "auth"`](https://github.com/cal-itp/benefits/blob/dev/benefits/core/session.py#L26) for use elsewhere
- [x] Create a new function `auth()` that uses the key to get the stored `bool` corresponding to whether the user is signed in via or not
- [x] Update the [`update()`](https://github.com/cal-itp/benefits/blob/dev/benefits/core/session.py#L192) function to accept a new kwarg `auth=False`; if it's a bool, store in the request's session using the key
- [x] Update the [`reset()`](https://github.com/cal-itp/benefits/blob/dev/benefits/core/session.py#L134) function to set the key to `False` by default
- [x] Update the [`context_dict()`](https://github.com/cal-itp/benefits/blob/dev/benefits/core/session.py#L52) function to add the new key/value - this shows up in the debug bar

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `benefits/core/session.py`
Content:
```
1 """
2 The core application: helpers to work with request sessions.
3 """
4 import hashlib
5 import logging
6 import time
7 import uuid
8 
9 from django.urls import reverse
10 
11 from benefits.settings import RATE_LIMIT_PERIOD
12 from . import models
13 
14 
15 logger = logging.getLogger(__name__)
16 
17 
18 _AGENCY = "agency"
19 _DEBUG = "debug"
20 _DID = "did"
21 _ELIGIBILITY = "eligibility"
22 _LANG = "lang"
23 _LIMITCOUNTER = "limitcounter"
24 _LIMITUNTIL = "limituntil"
25 _ORIGIN = "origin"
26 _START = "start"
27 _UID = "uid"
28 _VERIFIER = "verifier"
29 
30 # ignore bandit B105:hardcoded_password_string
31 # as these are not passwords, but keys for the session dict
32 _TOKEN = "token"  # nosec
33 _TOKEN_EXP = "token_exp"  # nosec
34 
35 
36 def agency(request):
37     """Get the agency from the request's session, or None"""
38     logger.debug("Get session agency")
39     try:
40         return models.TransitAgency.by_id(request.session[_AGENCY])
41     except (KeyError, models.TransitAgency.DoesNotExist):
42         logger.debug("Can't get agency from session")
43         return None
44 
45 
46 def active_agency(request):
47     """True if the request's session is configured with an active agency. False otherwise."""
48     logger.debug("Get session active agency flag")
49     a = agency(request)
50     return a and a.active
51 
52 
53 def context_dict(request):
54     """The request's session context as a dict."""
55     logger.debug("Get session context dict")
56     return {
57         _AGENCY: agency(request).slug if active_agency(request) else None,
58         _LIMITCOUNTER: rate_limit_counter(request),
59         _DEBUG: debug(request),
60         _DID: did(request),
61         _ELIGIBILITY: eligibility(request),
62         _LANG: language(request),
63         _ORIGIN: origin(request),
64         _LIMITUNTIL: rate_limit_time(request),
65         _START: start(request),
66         _TOKEN: token(request),
67         _TOKEN_EXP: token_expiry(request),
68         _UID: uid(request),
69         _VERIFIER: verifier(request),
70     }
71 
72 
73 def debug(request):
74     """Get the DEBUG flag from the request's session."""
75     logger.debug("Get session debug flag")
76     return bool(request.session.get(_DEBUG, False))
77 
78 
79 def did(request):
80     """Get the session's device ID, a hashed version of the unique ID."""
81     logger.debug("Get session did")
82     d = request.session.get(_DID)
83     if not d:
84         reset(request)
85         d = request.session.get(_DID)
86     return str(d)
87 
88 
89 def eligibility(request):
90     """Get the confirmed models.EligibilityType from the request's session, or None"""
91     logger.debug("Get session confirmed eligibility")
92     eligibility = request.session.get(_ELIGIBILITY)
93     if eligibility:
94         return models.EligibilityType.get(eligibility)
95     else:
96         return None
97 
98 
99 def eligible(request):
100     """True if the request's session is configured with an active agency and has confirmed eligibility. False otherwise."""
101     logger.debug("Get session eligible flag")
102     return active_agency(request) and agency(request).supports_type(eligibility(request))
103 
104 
105 def increment_rate_limit_counter(request):
106     """Adds 1 to this session's rate limit counter."""
107     logger.debug("Increment rate limit counter")
108     c = rate_limit_counter(request)
109     request.session[_LIMITCOUNTER] = int(c) + 1
110 
111 
112 def language(request):
113     """Get the language configured for the request."""
114     logger.debug("Get session language")
115     return request.LANGUAGE_CODE
116 
117 
118 def origin(request):
119     """Get the origin for the request's session, or None."""
120     logger.debug("Get session origin")
121     return request.session.get(_ORIGIN)
122 
123 
124 def rate_limit_counter(request):
125     """Get this session's rate limit counter."""
126     logger.debug("Get rate limit counter")
127     return request.session.get(_LIMITCOUNTER)
128 
129 
130 def rate_limit_time(request):
131     """Get this session's rate limit time, a Unix timestamp after which the session's rate limt resets."""
132     logger.debug("Get rate limit time")
133     return request.session.get(_LIMITUNTIL)
134 
135 
136 def reset(request):
137     """Reset the session for the request."""
138     logger.debug("Reset session")
139     request.session[_AGENCY] = None
140     request.session[_ELIGIBILITY] = None
141     request.session[_ORIGIN] = reverse("core:index")
142     request.session[_TOKEN] = None
143     request.session[_TOKEN_EXP] = None
144     request.session[_VERIFIER] = None
145 
146     if _UID not in request.session or not request.session[_UID]:
147         logger.debug("Reset session time and uid")
148         request.session[_START] = int(time.time() * 1000)
149         u = str(uuid.uuid4())
150         request.session[_UID] = u
151         request.session[_DID] = str(uuid.UUID(hashlib.sha512(bytes(u, "utf8")).hexdigest()[:32]))
152         reset_rate_limit(request)
153 
154 
155 def reset_rate_limit(request):
156     """Reset this session's rate limit counter and time."""
157     logger.debug("Reset rate limit")
158     request.session[_LIMITCOUNTER] = 0
159     # get the current time in Unix seconds, then add RATE_LIMIT_PERIOD seconds
160     request.session[_LIMITUNTIL] = int(time.time()) + RATE_LIMIT_PERIOD
161 
162 
163 def start(request):
164     """Get the start time from the request's session, as integer milliseconds since Epoch."""
165     logger.debug("Get session time")
166     s = request.session.get(_START)
167     if not s:
168         reset(request)
169         s = request.session.get(_START)
170     return s
171 
172 
173 def token(request):
174     """Get the token from the request's session, or None."""
175     logger.debug("Get session token")
176     return request.session.get(_TOKEN)
177 
178 
179 def token_expiry(request):
180     """Get the token's expiry time from the request's session, or None."""
181     logger.debug("Get session token expiry")
182     return request.session.get(_TOKEN_EXP)
183 
184 
185 def uid(request):
186     """Get the session's unique ID, generating a new one if necessary."""
187     logger.debug("Get session uid")
188     u = request.session.get(_UID)
189     if not u:
190         reset(request)
191         u = request.session.get(_UID)
192     return u
193 
194 
195 def update(request, agency=None, debug=None, eligibility_types=None, origin=None, token=None, token_exp=None, verifier=None):
196     """Update the request's session with non-null values."""
197     if agency is not None and isinstance(agency, models.TransitAgency):
198         logger.debug(f"Update session {_AGENCY}")
199         request.session[_AGENCY] = agency.id
200     if debug is not None:
201         logger.debug(f"Update session {_DEBUG}")
202         request.session[_DEBUG] = debug
203     if eligibility_types is not None and isinstance(eligibility_types, list):
204         logger.debug(f"Update session {_ELIGIBILITY}")
205         if len(eligibility_types) > 1:
206             raise NotImplementedError("Multiple eligibilities are not supported at this time.")
207         elif len(eligibility_types) == 1:
208             # get the eligibility corresponding to the session's agency
209             a = models.TransitAgency.by_id(request.session[_AGENCY])
210             t = str(eligibility_types[0]).strip()
211             request.session[_ELIGIBILITY] = a.get_type_id(t)
212         else:
213             # empty list, clear session eligibility
214             request.session[_ELIGIBILITY] = None
215     if origin is not None:
216         logger.debug(f"Update session {_ORIGIN}")
217         request.session[_ORIGIN] = origin
218     if token is not None:
219         logger.debug(f"Update session {_TOKEN}")
220         request.session[_TOKEN] = token
221         request.session[_TOKEN_EXP] = token_exp
222     if verifier is not None and isinstance(verifier, models.EligibilityVerifier):
223         logger.debug(f"Update session {_VERIFIER}")
224         request.session[_VERIFIER] = verifier.id
225 
226 
227 def valid_token(request):
228     """True if the request's session is configured with a valid token. False otherwise."""
229     if token(request) is not None:
230         logger.debug("Session contains a token")
231         exp = token_expiry(request)
232 
233         # ensure token does not expire in the next 5 seconds
234         valid = exp is None or exp > (time.time() + 5)
235 
236         logger.debug(f"Session token is {'valid' if valid else 'expired'}")
237         return valid
238     else:
239         logger.debug("Session does not contain a valid token")
240         return False
241 
242 
243 def verifier(request):
244     """Get the verifier from the request's session, or None"""
245     logger.debug("Get session verifier")
246     try:
247         return models.EligibilityVerifier.by_id(request.session[_VERIFIER])
248     except (KeyError, models.EligibilityVerifier.DoesNotExist):
249         logger.debug("Can't get verifier from session")
250         return None
251
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/benefits/core/session.py b/benefits/core/session.py
--- a/benefits/core/session.py
+++ b/benefits/core/session.py
@@ -16,6 +16,7 @@
 
 
 _AGENCY = "agency"
+_AUTH = "auth"
 _DEBUG = "debug"
 _DID = "did"
 _ELIGIBILITY = "eligibility"
@@ -50,11 +51,18 @@
     return a and a.active
 
 
+def auth(request):
+    """Get the auth from the request's session, or None"""
+    logger.debug("Get session auth")
+    return request.session.get(_AUTH)
+
+
 def context_dict(request):
     """The request's session context as a dict."""
     logger.debug("Get session context dict")
     return {
         _AGENCY: agency(request).slug if active_agency(request) else None,
+        _AUTH: auth(request),
         _LIMITCOUNTER: rate_limit_counter(request),
         _DEBUG: debug(request),
         _DID: did(request),
@@ -137,6 +145,7 @@
     """Reset the session for the request."""
     logger.debug("Reset session")
     request.session[_AGENCY] = None
+    request.session[_AUTH] = False
     request.session[_ELIGIBILITY] = None
     request.session[_ORIGIN] = reverse("core:index")
     request.session[_TOKEN] = None
@@ -192,11 +201,24 @@
     return u
 
 
-def update(request, agency=None, debug=None, eligibility_types=None, origin=None, token=None, token_exp=None, verifier=None):
+def update(
+    request,
+    agency=None,
+    auth=None,
+    debug=None,
+    eligibility_types=None,
+    origin=None,
+    token=None,
+    token_exp=None,
+    verifier=None,
+):
     """Update the request's session with non-null values."""
     if agency is not None and isinstance(agency, models.TransitAgency):
         logger.debug(f"Update session {_AGENCY}")
         request.session[_AGENCY] = agency.id
+    if auth is not None and type(auth) == bool:
+        logger.debug(f"Update session {_AUTH}")
+        request.session[_AUTH] = auth
     if debug is not None:
         logger.debug(f"Update session {_DEBUG}")
         request.session[_DEBUG] = debug

{"golden_diff": "diff --git a/benefits/core/session.py b/benefits/core/session.py\n--- a/benefits/core/session.py\n+++ b/benefits/core/session.py\n@@ -16,6 +16,7 @@\n \n \n _AGENCY = \"agency\"\n+_AUTH = \"auth\"\n _DEBUG = \"debug\"\n _DID = \"did\"\n _ELIGIBILITY = \"eligibility\"\n@@ -50,11 +51,18 @@\n     return a and a.active\n \n \n+def auth(request):\n+    \"\"\"Get the auth from the request's session, or None\"\"\"\n+    logger.debug(\"Get session auth\")\n+    return request.session.get(_AUTH)\n+\n+\n def context_dict(request):\n     \"\"\"The request's session context as a dict.\"\"\"\n     logger.debug(\"Get session context dict\")\n     return {\n         _AGENCY: agency(request).slug if active_agency(request) else None,\n+        _AUTH: auth(request),\n         _LIMITCOUNTER: rate_limit_counter(request),\n         _DEBUG: debug(request),\n         _DID: did(request),\n@@ -137,6 +145,7 @@\n     \"\"\"Reset the session for the request.\"\"\"\n     logger.debug(\"Reset session\")\n     request.session[_AGENCY] = None\n+    request.session[_AUTH] = False\n     request.session[_ELIGIBILITY] = None\n     request.session[_ORIGIN] = reverse(\"core:index\")\n     request.session[_TOKEN] = None\n@@ -192,11 +201,24 @@\n     return u\n \n \n-def update(request, agency=None, debug=None, eligibility_types=None, origin=None, token=None, token_exp=None, verifier=None):\n+def update(\n+    request,\n+    agency=None,\n+    auth=None,\n+    debug=None,\n+    eligibility_types=None,\n+    origin=None,\n+    token=None,\n+    token_exp=None,\n+    verifier=None,\n+):\n     \"\"\"Update the request's session with non-null values.\"\"\"\n     if agency is not None and isinstance(agency, models.TransitAgency):\n         logger.debug(f\"Update session {_AGENCY}\")\n         request.session[_AGENCY] = agency.id\n+    if auth is not None and type(auth) == bool:\n+        logger.debug(f\"Update session {_AUTH}\")\n+        request.session[_AUTH] = auth\n     if debug is not None:\n         logger.debug(f\"Update session {_DEBUG}\")\n         request.session[_DEBUG] = debug\n", "issue": "Track sign in status in user session\n## Background\r\n\r\nOnce the user successfully signs in to their Login.gov account, we will receive a response from the authentication server indicating their status. We can store the fact the the user is then \"signed in\" in their session using the existing [`session` framework](https://github.com/cal-itp/benefits/blob/dev/benefits/core/session.py) which keeps everything local to the user in secure cookies.\r\n\r\nThis task is just about setting up the session code to be able to track this, but not actually getting the status in there (see #373 for that).\r\n\r\nCheck out #321 / #330 as an example of a similar addition last Sprint.\r\n\r\n## Tasks\r\n\r\n- [x] Add a [new key `_AUTH = \"auth\"`](https://github.com/cal-itp/benefits/blob/dev/benefits/core/session.py#L26) for use elsewhere\r\n- [x] Create a new function `auth()` that uses the key to get the stored `bool` corresponding to whether the user is signed in via or not\r\n- [x] Update the [`update()`](https://github.com/cal-itp/benefits/blob/dev/benefits/core/session.py#L192) function to accept a new kwarg `auth=False`; if it's a bool, store in the request's session using the key\r\n- [x] Update the [`reset()`](https://github.com/cal-itp/benefits/blob/dev/benefits/core/session.py#L134) function to set the key to `False` by default\r\n- [x] Update the [`context_dict()`](https://github.com/cal-itp/benefits/blob/dev/benefits/core/session.py#L52) function to add the new key/value - this shows up in the debug bar\n", "before_files": [{"content": "\"\"\"\nThe core application: helpers to work with request sessions.\n\"\"\"\nimport hashlib\nimport logging\nimport time\nimport uuid\n\nfrom django.urls import reverse\n\nfrom benefits.settings import RATE_LIMIT_PERIOD\nfrom . import models\n\n\nlogger = logging.getLogger(__name__)\n\n\n_AGENCY = \"agency\"\n_DEBUG = \"debug\"\n_DID = \"did\"\n_ELIGIBILITY = \"eligibility\"\n_LANG = \"lang\"\n_LIMITCOUNTER = \"limitcounter\"\n_LIMITUNTIL = \"limituntil\"\n_ORIGIN = \"origin\"\n_START = \"start\"\n_UID = \"uid\"\n_VERIFIER = \"verifier\"\n\n# ignore bandit B105:hardcoded_password_string\n# as these are not passwords, but keys for the session dict\n_TOKEN = \"token\"  # nosec\n_TOKEN_EXP = \"token_exp\"  # nosec\n\n\ndef agency(request):\n    \"\"\"Get the agency from the request's session, or None\"\"\"\n    logger.debug(\"Get session agency\")\n    try:\n        return models.TransitAgency.by_id(request.session[_AGENCY])\n    except (KeyError, models.TransitAgency.DoesNotExist):\n        logger.debug(\"Can't get agency from session\")\n        return None\n\n\ndef active_agency(request):\n    \"\"\"True if the request's session is configured with an active agency. False otherwise.\"\"\"\n    logger.debug(\"Get session active agency flag\")\n    a = agency(request)\n    return a and a.active\n\n\ndef context_dict(request):\n    \"\"\"The request's session context as a dict.\"\"\"\n    logger.debug(\"Get session context dict\")\n    return {\n        _AGENCY: agency(request).slug if active_agency(request) else None,\n        _LIMITCOUNTER: rate_limit_counter(request),\n        _DEBUG: debug(request),\n        _DID: did(request),\n        _ELIGIBILITY: eligibility(request),\n        _LANG: language(request),\n        _ORIGIN: origin(request),\n        _LIMITUNTIL: rate_limit_time(request),\n        _START: start(request),\n        _TOKEN: token(request),\n        _TOKEN_EXP: token_expiry(request),\n        _UID: uid(request),\n        _VERIFIER: verifier(request),\n    }\n\n\ndef debug(request):\n    \"\"\"Get the DEBUG flag from the request's session.\"\"\"\n    logger.debug(\"Get session debug flag\")\n    return bool(request.session.get(_DEBUG, False))\n\n\ndef did(request):\n    \"\"\"Get the session's device ID, a hashed version of the unique ID.\"\"\"\n    logger.debug(\"Get session did\")\n    d = request.session.get(_DID)\n    if not d:\n        reset(request)\n        d = request.session.get(_DID)\n    return str(d)\n\n\ndef eligibility(request):\n    \"\"\"Get the confirmed models.EligibilityType from the request's session, or None\"\"\"\n    logger.debug(\"Get session confirmed eligibility\")\n    eligibility = request.session.get(_ELIGIBILITY)\n    if eligibility:\n        return models.EligibilityType.get(eligibility)\n    else:\n        return None\n\n\ndef eligible(request):\n    \"\"\"True if the request's session is configured with an active agency and has confirmed eligibility. False otherwise.\"\"\"\n    logger.debug(\"Get session eligible flag\")\n    return active_agency(request) and agency(request).supports_type(eligibility(request))\n\n\ndef increment_rate_limit_counter(request):\n    \"\"\"Adds 1 to this session's rate limit counter.\"\"\"\n    logger.debug(\"Increment rate limit counter\")\n    c = rate_limit_counter(request)\n    request.session[_LIMITCOUNTER] = int(c) + 1\n\n\ndef language(request):\n    \"\"\"Get the language configured for the request.\"\"\"\n    logger.debug(\"Get session language\")\n    return request.LANGUAGE_CODE\n\n\ndef origin(request):\n    \"\"\"Get the origin for the request's session, or None.\"\"\"\n    logger.debug(\"Get session origin\")\n    return request.session.get(_ORIGIN)\n\n\ndef rate_limit_counter(request):\n    \"\"\"Get this session's rate limit counter.\"\"\"\n    logger.debug(\"Get rate limit counter\")\n    return request.session.get(_LIMITCOUNTER)\n\n\ndef rate_limit_time(request):\n    \"\"\"Get this session's rate limit time, a Unix timestamp after which the session's rate limt resets.\"\"\"\n    logger.debug(\"Get rate limit time\")\n    return request.session.get(_LIMITUNTIL)\n\n\ndef reset(request):\n    \"\"\"Reset the session for the request.\"\"\"\n    logger.debug(\"Reset session\")\n    request.session[_AGENCY] = None\n    request.session[_ELIGIBILITY] = None\n    request.session[_ORIGIN] = reverse(\"core:index\")\n    request.session[_TOKEN] = None\n    request.session[_TOKEN_EXP] = None\n    request.session[_VERIFIER] = None\n\n    if _UID not in request.session or not request.session[_UID]:\n        logger.debug(\"Reset session time and uid\")\n        request.session[_START] = int(time.time() * 1000)\n        u = str(uuid.uuid4())\n        request.session[_UID] = u\n        request.session[_DID] = str(uuid.UUID(hashlib.sha512(bytes(u, \"utf8\")).hexdigest()[:32]))\n        reset_rate_limit(request)\n\n\ndef reset_rate_limit(request):\n    \"\"\"Reset this session's rate limit counter and time.\"\"\"\n    logger.debug(\"Reset rate limit\")\n    request.session[_LIMITCOUNTER] = 0\n    # get the current time in Unix seconds, then add RATE_LIMIT_PERIOD seconds\n    request.session[_LIMITUNTIL] = int(time.time()) + RATE_LIMIT_PERIOD\n\n\ndef start(request):\n    \"\"\"Get the start time from the request's session, as integer milliseconds since Epoch.\"\"\"\n    logger.debug(\"Get session time\")\n    s = request.session.get(_START)\n    if not s:\n        reset(request)\n        s = request.session.get(_START)\n    return s\n\n\ndef token(request):\n    \"\"\"Get the token from the request's session, or None.\"\"\"\n    logger.debug(\"Get session token\")\n    return request.session.get(_TOKEN)\n\n\ndef token_expiry(request):\n    \"\"\"Get the token's expiry time from the request's session, or None.\"\"\"\n    logger.debug(\"Get session token expiry\")\n    return request.session.get(_TOKEN_EXP)\n\n\ndef uid(request):\n    \"\"\"Get the session's unique ID, generating a new one if necessary.\"\"\"\n    logger.debug(\"Get session uid\")\n    u = request.session.get(_UID)\n    if not u:\n        reset(request)\n        u = request.session.get(_UID)\n    return u\n\n\ndef update(request, agency=None, debug=None, eligibility_types=None, origin=None, token=None, token_exp=None, verifier=None):\n    \"\"\"Update the request's session with non-null values.\"\"\"\n    if agency is not None and isinstance(agency, models.TransitAgency):\n        logger.debug(f\"Update session {_AGENCY}\")\n        request.session[_AGENCY] = agency.id\n    if debug is not None:\n        logger.debug(f\"Update session {_DEBUG}\")\n        request.session[_DEBUG] = debug\n    if eligibility_types is not None and isinstance(eligibility_types, list):\n        logger.debug(f\"Update session {_ELIGIBILITY}\")\n        if len(eligibility_types) > 1:\n            raise NotImplementedError(\"Multiple eligibilities are not supported at this time.\")\n        elif len(eligibility_types) == 1:\n            # get the eligibility corresponding to the session's agency\n            a = models.TransitAgency.by_id(request.session[_AGENCY])\n            t = str(eligibility_types[0]).strip()\n            request.session[_ELIGIBILITY] = a.get_type_id(t)\n        else:\n            # empty list, clear session eligibility\n            request.session[_ELIGIBILITY] = None\n    if origin is not None:\n        logger.debug(f\"Update session {_ORIGIN}\")\n        request.session[_ORIGIN] = origin\n    if token is not None:\n        logger.debug(f\"Update session {_TOKEN}\")\n        request.session[_TOKEN] = token\n        request.session[_TOKEN_EXP] = token_exp\n    if verifier is not None and isinstance(verifier, models.EligibilityVerifier):\n        logger.debug(f\"Update session {_VERIFIER}\")\n        request.session[_VERIFIER] = verifier.id\n\n\ndef valid_token(request):\n    \"\"\"True if the request's session is configured with a valid token. False otherwise.\"\"\"\n    if token(request) is not None:\n        logger.debug(\"Session contains a token\")\n        exp = token_expiry(request)\n\n        # ensure token does not expire in the next 5 seconds\n        valid = exp is None or exp > (time.time() + 5)\n\n        logger.debug(f\"Session token is {'valid' if valid else 'expired'}\")\n        return valid\n    else:\n        logger.debug(\"Session does not contain a valid token\")\n        return False\n\n\ndef verifier(request):\n    \"\"\"Get the verifier from the request's session, or None\"\"\"\n    logger.debug(\"Get session verifier\")\n    try:\n        return models.EligibilityVerifier.by_id(request.session[_VERIFIER])\n    except (KeyError, models.EligibilityVerifier.DoesNotExist):\n        logger.debug(\"Can't get verifier from session\")\n        return None\n", "path": "benefits/core/session.py"}], "after_files": [{"content": "\"\"\"\nThe core application: helpers to work with request sessions.\n\"\"\"\nimport hashlib\nimport logging\nimport time\nimport uuid\n\nfrom django.urls import reverse\n\nfrom benefits.settings import RATE_LIMIT_PERIOD\nfrom . import models\n\n\nlogger = logging.getLogger(__name__)\n\n\n_AGENCY = \"agency\"\n_AUTH = \"auth\"\n_DEBUG = \"debug\"\n_DID = \"did\"\n_ELIGIBILITY = \"eligibility\"\n_LANG = \"lang\"\n_LIMITCOUNTER = \"limitcounter\"\n_LIMITUNTIL = \"limituntil\"\n_ORIGIN = \"origin\"\n_START = \"start\"\n_UID = \"uid\"\n_VERIFIER = \"verifier\"\n\n# ignore bandit B105:hardcoded_password_string\n# as these are not passwords, but keys for the session dict\n_TOKEN = \"token\"  # nosec\n_TOKEN_EXP = \"token_exp\"  # nosec\n\n\ndef agency(request):\n    \"\"\"Get the agency from the request's session, or None\"\"\"\n    logger.debug(\"Get session agency\")\n    try:\n        return models.TransitAgency.by_id(request.session[_AGENCY])\n    except (KeyError, models.TransitAgency.DoesNotExist):\n        logger.debug(\"Can't get agency from session\")\n        return None\n\n\ndef active_agency(request):\n    \"\"\"True if the request's session is configured with an active agency. False otherwise.\"\"\"\n    logger.debug(\"Get session active agency flag\")\n    a = agency(request)\n    return a and a.active\n\n\ndef auth(request):\n    \"\"\"Get the auth from the request's session, or None\"\"\"\n    logger.debug(\"Get session auth\")\n    return request.session.get(_AUTH)\n\n\ndef context_dict(request):\n    \"\"\"The request's session context as a dict.\"\"\"\n    logger.debug(\"Get session context dict\")\n    return {\n        _AGENCY: agency(request).slug if active_agency(request) else None,\n        _AUTH: auth(request),\n        _LIMITCOUNTER: rate_limit_counter(request),\n        _DEBUG: debug(request),\n        _DID: did(request),\n        _ELIGIBILITY: eligibility(request),\n        _LANG: language(request),\n        _ORIGIN: origin(request),\n        _LIMITUNTIL: rate_limit_time(request),\n        _START: start(request),\n        _TOKEN: token(request),\n        _TOKEN_EXP: token_expiry(request),\n        _UID: uid(request),\n        _VERIFIER: verifier(request),\n    }\n\n\ndef debug(request):\n    \"\"\"Get the DEBUG flag from the request's session.\"\"\"\n    logger.debug(\"Get session debug flag\")\n    return bool(request.session.get(_DEBUG, False))\n\n\ndef did(request):\n    \"\"\"Get the session's device ID, a hashed version of the unique ID.\"\"\"\n    logger.debug(\"Get session did\")\n    d = request.session.get(_DID)\n    if not d:\n        reset(request)\n        d = request.session.get(_DID)\n    return str(d)\n\n\ndef eligibility(request):\n    \"\"\"Get the confirmed models.EligibilityType from the request's session, or None\"\"\"\n    logger.debug(\"Get session confirmed eligibility\")\n    eligibility = request.session.get(_ELIGIBILITY)\n    if eligibility:\n        return models.EligibilityType.get(eligibility)\n    else:\n        return None\n\n\ndef eligible(request):\n    \"\"\"True if the request's session is configured with an active agency and has confirmed eligibility. False otherwise.\"\"\"\n    logger.debug(\"Get session eligible flag\")\n    return active_agency(request) and agency(request).supports_type(eligibility(request))\n\n\ndef increment_rate_limit_counter(request):\n    \"\"\"Adds 1 to this session's rate limit counter.\"\"\"\n    logger.debug(\"Increment rate limit counter\")\n    c = rate_limit_counter(request)\n    request.session[_LIMITCOUNTER] = int(c) + 1\n\n\ndef language(request):\n    \"\"\"Get the language configured for the request.\"\"\"\n    logger.debug(\"Get session language\")\n    return request.LANGUAGE_CODE\n\n\ndef origin(request):\n    \"\"\"Get the origin for the request's session, or None.\"\"\"\n    logger.debug(\"Get session origin\")\n    return request.session.get(_ORIGIN)\n\n\ndef rate_limit_counter(request):\n    \"\"\"Get this session's rate limit counter.\"\"\"\n    logger.debug(\"Get rate limit counter\")\n    return request.session.get(_LIMITCOUNTER)\n\n\ndef rate_limit_time(request):\n    \"\"\"Get this session's rate limit time, a Unix timestamp after which the session's rate limt resets.\"\"\"\n    logger.debug(\"Get rate limit time\")\n    return request.session.get(_LIMITUNTIL)\n\n\ndef reset(request):\n    \"\"\"Reset the session for the request.\"\"\"\n    logger.debug(\"Reset session\")\n    request.session[_AGENCY] = None\n    request.session[_AUTH] = False\n    request.session[_ELIGIBILITY] = None\n    request.session[_ORIGIN] = reverse(\"core:index\")\n    request.session[_TOKEN] = None\n    request.session[_TOKEN_EXP] = None\n    request.session[_VERIFIER] = None\n\n    if _UID not in request.session or not request.session[_UID]:\n        logger.debug(\"Reset session time and uid\")\n        request.session[_START] = int(time.time() * 1000)\n        u = str(uuid.uuid4())\n        request.session[_UID] = u\n        request.session[_DID] = str(uuid.UUID(hashlib.sha512(bytes(u, \"utf8\")).hexdigest()[:32]))\n        reset_rate_limit(request)\n\n\ndef reset_rate_limit(request):\n    \"\"\"Reset this session's rate limit counter and time.\"\"\"\n    logger.debug(\"Reset rate limit\")\n    request.session[_LIMITCOUNTER] = 0\n    # get the current time in Unix seconds, then add RATE_LIMIT_PERIOD seconds\n    request.session[_LIMITUNTIL] = int(time.time()) + RATE_LIMIT_PERIOD\n\n\ndef start(request):\n    \"\"\"Get the start time from the request's session, as integer milliseconds since Epoch.\"\"\"\n    logger.debug(\"Get session time\")\n    s = request.session.get(_START)\n    if not s:\n        reset(request)\n        s = request.session.get(_START)\n    return s\n\n\ndef token(request):\n    \"\"\"Get the token from the request's session, or None.\"\"\"\n    logger.debug(\"Get session token\")\n    return request.session.get(_TOKEN)\n\n\ndef token_expiry(request):\n    \"\"\"Get the token's expiry time from the request's session, or None.\"\"\"\n    logger.debug(\"Get session token expiry\")\n    return request.session.get(_TOKEN_EXP)\n\n\ndef uid(request):\n    \"\"\"Get the session's unique ID, generating a new one if necessary.\"\"\"\n    logger.debug(\"Get session uid\")\n    u = request.session.get(_UID)\n    if not u:\n        reset(request)\n        u = request.session.get(_UID)\n    return u\n\n\ndef update(\n    request,\n    agency=None,\n    auth=None,\n    debug=None,\n    eligibility_types=None,\n    origin=None,\n    token=None,\n    token_exp=None,\n    verifier=None,\n):\n    \"\"\"Update the request's session with non-null values.\"\"\"\n    if agency is not None and isinstance(agency, models.TransitAgency):\n        logger.debug(f\"Update session {_AGENCY}\")\n        request.session[_AGENCY] = agency.id\n    if auth is not None and type(auth) == bool:\n        logger.debug(f\"Update session {_AUTH}\")\n        request.session[_AUTH] = auth\n    if debug is not None:\n        logger.debug(f\"Update session {_DEBUG}\")\n        request.session[_DEBUG] = debug\n    if eligibility_types is not None and isinstance(eligibility_types, list):\n        logger.debug(f\"Update session {_ELIGIBILITY}\")\n        if len(eligibility_types) > 1:\n            raise NotImplementedError(\"Multiple eligibilities are not supported at this time.\")\n        elif len(eligibility_types) == 1:\n            # get the eligibility corresponding to the session's agency\n            a = models.TransitAgency.by_id(request.session[_AGENCY])\n            t = str(eligibility_types[0]).strip()\n            request.session[_ELIGIBILITY] = a.get_type_id(t)\n        else:\n            # empty list, clear session eligibility\n            request.session[_ELIGIBILITY] = None\n    if origin is not None:\n        logger.debug(f\"Update session {_ORIGIN}\")\n        request.session[_ORIGIN] = origin\n    if token is not None:\n        logger.debug(f\"Update session {_TOKEN}\")\n        request.session[_TOKEN] = token\n        request.session[_TOKEN_EXP] = token_exp\n    if verifier is not None and isinstance(verifier, models.EligibilityVerifier):\n        logger.debug(f\"Update session {_VERIFIER}\")\n        request.session[_VERIFIER] = verifier.id\n\n\ndef valid_token(request):\n    \"\"\"True if the request's session is configured with a valid token. False otherwise.\"\"\"\n    if token(request) is not None:\n        logger.debug(\"Session contains a token\")\n        exp = token_expiry(request)\n\n        # ensure token does not expire in the next 5 seconds\n        valid = exp is None or exp > (time.time() + 5)\n\n        logger.debug(f\"Session token is {'valid' if valid else 'expired'}\")\n        return valid\n    else:\n        logger.debug(\"Session does not contain a valid token\")\n        return False\n\n\ndef verifier(request):\n    \"\"\"Get the verifier from the request's session, or None\"\"\"\n    logger.debug(\"Get session verifier\")\n    try:\n        return models.EligibilityVerifier.by_id(request.session[_VERIFIER])\n    except (KeyError, models.EligibilityVerifier.DoesNotExist):\n        logger.debug(\"Can't get verifier from session\")\n        return None\n", "path": "benefits/core/session.py"}]}