codecov[bot]: # [Codecov](https://codecov.io/gh/aio-libs/aiohttp-session/pull/317?src=pr&el=h1) Report
> Merging [#317](https://codecov.io/gh/aio-libs/aiohttp-session/pull/317?src=pr&el=desc) into [master](https://codecov.io/gh/aio-libs/aiohttp-session/commit/cecdc7282e3c2bdd0fd4931ce2a562c8812a82a6?src=pr&el=desc) will **increase** coverage by `0.12%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/aio-libs/aiohttp-session/pull/317/graphs/tree.svg?width=650&token=M1jTmwOZ5C&height=150&src=pr)](https://codecov.io/gh/aio-libs/aiohttp-session/pull/317?src=pr&el=tree)

```diff
@@            Coverage Diff            @@
##           master    #317      +/-   ##
=========================================
+ Coverage   97.08%   97.2%   +0.12%     
=========================================
  Files           4       4              
  Lines         206     215       +9     
  Branches       25      25              
=========================================
+ Hits          200     209       +9     
  Misses          4       4              
  Partials        2       2
```


| [Impacted Files](https://codecov.io/gh/aio-libs/aiohttp-session/pull/317?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [aiohttp\_session/nacl\_storage.py](https://codecov.io/gh/aio-libs/aiohttp-session/pull/317/diff?src=pr&el=tree#diff-YWlvaHR0cF9zZXNzaW9uL25hY2xfc3RvcmFnZS5weQ==) | `100% <100%> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/aio-libs/aiohttp-session/pull/317?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/aio-libs/aiohttp-session/pull/317?src=pr&el=footer). Last update [cecdc72...7240128](https://codecov.io/gh/aio-libs/aiohttp-session/pull/317?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

diff --git a/aiohttp_session/nacl_storage.py b/aiohttp_session/nacl_storage.py
index eb7105d..c855ff8 100644
--- a/aiohttp_session/nacl_storage.py
+++ b/aiohttp_session/nacl_storage.py
@@ -1,10 +1,13 @@
+import binascii
 import json
 
 import nacl.secret
 import nacl.utils
+import nacl.exceptions
 from nacl.encoding import Base64Encoder
 
 from . import AbstractStorage, Session
+from .log import log
 
 
 class NaClCookieStorage(AbstractStorage):
@@ -22,16 +25,26 @@ class NaClCookieStorage(AbstractStorage):
 
         self._secretbox = nacl.secret.SecretBox(secret_key)
 
+    def empty_session(self):
+        return Session(None, data=None, new=True, max_age=self.max_age)
+
     async def load_session(self, request):
         cookie = self.load_cookie(request)
         if cookie is None:
-            return Session(None, data=None, new=True, max_age=self.max_age)
+            return self.empty_session()
         else:
-            data = self._decoder(
-                self._secretbox.decrypt(cookie.encode('utf-8'),
-                                        encoder=Base64Encoder).decode('utf-8')
-            )
-            return Session(None, data=data, new=False, max_age=self.max_age)
+            try:
+                data = self._decoder(
+                    self._secretbox.decrypt(
+                        cookie.encode('utf-8'),
+                        encoder=Base64Encoder).decode('utf-8')
+                )
+                return Session(None, data=data, new=False,
+                               max_age=self.max_age)
+            except (binascii.Error, nacl.exceptions.CryptoError):
+                log.warning("Cannot decrypt cookie value, "
+                            "create a new fresh session")
+                return self.empty_session()
 
     async def save_session(self, request, response, session):
         if session.empty:
diff --git a/docs/reference.rst b/docs/reference.rst
index 267c882..a22970b 100644
--- a/docs/reference.rst
+++ b/docs/reference.rst
@@ -348,7 +348,7 @@ To use the storage you should push it into
 :func:`~aiohttp_session.session_middleware`::
 
    app = aiohttp.web.Application(middlewares=[
-       aiohttp_session.cookie_storage.NaClCookieStorage(
+       aiohttp_session.nacl_storage.NaClCookieStorage(
            b'Thirty  two  length  bytes  key.'])
 
 .. class:: NaClCookieStorage(secret_key, *, \

Error 500 when switching from EncryptedCookieStorage to NaClCookieStorage
```Traceback (most recent call last):
  File "[..]/site-packages/aiohttp/web_protocol.py", line 390, in start
    resp = await self._request_handler(request)
  File "[..]/site-packages/aiohttp/web_app.py", line 366, in _handle
    resp = await handler(request)
  File "[..]/site-packages/aiohttp/web_middlewares.py", line 106, in impl
    return await handler(request)
  File "[..]/site-packages/aiohttp_session/__init__.py", line 149, in factory
    response = await handler(request)
  File "/Users/az02096/dev/nestor/elasticsearch_authorizations/elasticsearch_authorizations/app.py", line 122, in index_handler
    session = await get_session(request)
  File "[..]/site-packages/aiohttp_session/__init__.py", line 113, in get_session
    session = await storage.load_session(request)
  File "[..]/site-packages/aiohttp_session/nacl_storage.py", line 32, in load_session
    encoder=Base64Encoder).decode('utf-8')
  File "[..]/site-packages/nacl/secret.py", line 112, in decrypt
    ciphertext = encoder.decode(ciphertext)
  File "[..]/site-packages/nacl/encoding.py", line 73, in decode
    return base64.b64decode(data)
  File "[..]/base64.py", line 87, in b64decode
    return binascii.a2b_base64(s)
binascii.Error: Incorrect padding
INFO:aiohttp.access:::1 [10/Sep/2018:14:27:23 +0000] "GET / HTTP/1.1" 500 330 "http://localhost:8000/" 
```

Same issue when changing the secret.

probably we need to port this patch to NaClCookieStorage:
https://github.com/aio-libs/aiohttp-session/commit/0741f5a5380094ff6a970194870b2c41451747cf

Not sure what is the benefit of NaClCookieStorage over  EncryptedCookieStorage.

diff --git a/tests/test_nacl_storage.py b/tests/test_nacl_storage.py
index 66bd814..836bfe6 100644
--- a/tests/test_nacl_storage.py
+++ b/tests/test_nacl_storage.py
@@ -179,3 +179,36 @@ async def test_load_session_dont_load_expired_session(aiohttp_client,
 
     resp = await client.get('/?exp=yes')
     assert resp.status == 200
+
+
+async def test_load_corrupted_session(aiohttp_client, key):
+
+    async def handler(request):
+        session = await get_session(request)
+        assert isinstance(session, Session)
+        assert session.new
+        assert {} == session
+        return web.Response(body=b'OK')
+
+    client = await aiohttp_client(create_app(handler, key))
+    client.session.cookie_jar.update_cookies({'AIOHTTP_SESSION': 'bad key'})
+    resp = await client.get('/')
+    assert resp.status == 200
+
+
+async def test_load_session_different_key(aiohttp_client, key):
+
+    async def handler(request):
+        session = await get_session(request)
+        assert isinstance(session, Session)
+        assert session.new
+        assert {} == session
+        return web.Response(body=b'OK')
+
+    client = await aiohttp_client(create_app(handler, key))
+    # create another box with another key
+    key = nacl.utils.random(nacl.secret.SecretBox.KEY_SIZE)
+    secretbox = nacl.secret.SecretBox(key)
+    make_cookie(client, secretbox, {'a': 1, 'b': 12})
+    resp = await client.get('/')
+    assert resp.status == 200

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest pytest-cov pytest-asyncio pytest-bdd pytest-benchmark pytest-randomly responses mock hypothesis freezegun trustme requests-mock requests tomlkit
apt-get update apt-get install -y gcc
3.9
requirements-dev.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
aiohttp==3.3.2
-e git+https://github.com/aio-libs/aiohttp-session.git@cecdc7282e3c2bdd0fd4931ce2a562c8812a82a6#egg=aiohttp_session
aiomcache==0.6.0
aioredis==1.1.0
alabaster==0.7.16
asn1crypto==1.5.1
async-timeout==3.0.1
atomicwrites==1.4.1
attrs==25.3.0
babel==2.17.0
certifi==2025.1.31
cffi==1.17.1
chardet==3.0.4
charset-normalizer==3.4.1
coverage==7.8.0
cryptography==44.0.2
docker==3.4.1
docker-pycreds==0.4.0
docutils==0.21.2
exceptiongroup==1.2.2
flake8==3.5.0
freezegun==1.5.1
gherkin-official==29.0.0
hiredis==3.1.0
hypothesis==6.130.5
idna==3.10
imagesize==1.4.1
importlib_metadata==8.6.1
iniconfig==2.1.0
Jinja2==3.1.6
Mako==1.3.9
MarkupSafe==3.0.2
mccabe==0.6.1
mock==5.2.0
more-itertools==10.6.0
multidict==4.3.0
packaging==24.2
parse==1.20.2
parse_type==0.6.4
pep257==0.7.0
pluggy==1.5.0
py==1.11.0
py-cpuinfo==9.0.0
pycodestyle==2.3.1
pycparser==2.22
pyflakes==1.6.0
Pygments==2.19.1
PyNaCl==1.2.1
pytest==8.3.5
pytest-aiohttp==0.3.0
pytest-asyncio==0.26.0
pytest-bdd==8.1.0
pytest-benchmark==5.1.0
pytest-cov==2.5.1
pytest-mock==1.10.0
pytest-randomly==3.16.0
pytest-sugar==0.9.1
python-dateutil==2.9.0.post0
PyYAML==6.0.2
requests==2.32.3
requests-mock==1.12.1
responses==0.25.7
six==1.17.0
snowballstemmer==2.2.0
sortedcontainers==2.4.0
Sphinx==1.7.6
sphinxcontrib-serializinghtml==2.0.0
sphinxcontrib-websupport==1.2.4
termcolor==3.0.0
tomli==2.2.1
tomlkit==0.13.2
trustme==1.2.1
typing_extensions==4.13.0
urllib3==2.3.0
websocket-client==1.8.0
yarl==1.2.6
zipp==3.21.0

name: aiohttp-session
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - aiohttp==3.3.2
      - aiomcache==0.6.0
      - aioredis==1.1.0
      - alabaster==0.7.16
      - asn1crypto==1.5.1
      - async-timeout==3.0.1
      - atomicwrites==1.4.1
      - attrs==25.3.0
      - babel==2.17.0
      - certifi==2025.1.31
      - cffi==1.17.1
      - chardet==3.0.4
      - charset-normalizer==3.4.1
      - coverage==7.8.0
      - cryptography==44.0.2
      - docker==3.4.1
      - docker-pycreds==0.4.0
      - docutils==0.21.2
      - exceptiongroup==1.2.2
      - flake8==3.5.0
      - freezegun==1.5.1
      - gherkin-official==29.0.0
      - hiredis==3.1.0
      - hypothesis==6.130.5
      - idna==3.10
      - imagesize==1.4.1
      - importlib-metadata==8.6.1
      - iniconfig==2.1.0
      - jinja2==3.1.6
      - mako==1.3.9
      - markupsafe==3.0.2
      - mccabe==0.6.1
      - mock==5.2.0
      - more-itertools==10.6.0
      - multidict==4.3.0
      - packaging==24.2
      - parse==1.20.2
      - parse-type==0.6.4
      - pep257==0.7.0
      - pluggy==1.5.0
      - py==1.11.0
      - py-cpuinfo==9.0.0
      - pycodestyle==2.3.1
      - pycparser==2.22
      - pyflakes==1.6.0
      - pygments==2.19.1
      - pynacl==1.2.1
      - pytest==8.3.5
      - pytest-aiohttp==0.3.0
      - pytest-asyncio==0.26.0
      - pytest-bdd==8.1.0
      - pytest-benchmark==5.1.0
      - pytest-cov==2.5.1
      - pytest-mock==1.10.0
      - pytest-randomly==3.16.0
      - pytest-sugar==0.9.1
      - python-dateutil==2.9.0.post0
      - pyyaml==6.0.2
      - requests==2.32.3
      - requests-mock==1.12.1
      - responses==0.25.7
      - six==1.17.0
      - snowballstemmer==2.2.0
      - sortedcontainers==2.4.0
      - sphinx==1.7.6
      - sphinxcontrib-serializinghtml==2.0.0
      - sphinxcontrib-websupport==1.2.4
      - termcolor==3.0.0
      - tomli==2.2.1
      - tomlkit==0.13.2
      - trustme==1.2.1
      - typing-extensions==4.13.0
      - urllib3==2.3.0
      - websocket-client==1.8.0
      - yarl==1.2.6
      - zipp==3.21.0
prefix: /opt/conda/envs/aiohttp-session

tests/test_nacl_storage.py::test_load_session_different_key tests/test_nacl_storage.py::test_load_corrupted_session
tests/test_nacl_storage.py::test_change_session tests/test_nacl_storage.py::test_create_new_session tests/test_nacl_storage.py::test_del_cookie_on_session_invalidation tests/test_nacl_storage.py::test_nacl_session_fixation tests/test_nacl_storage.py::test_load_session_dont_load_expired_session tests/test_nacl_storage.py::test_invalid_key tests/test_nacl_storage.py::test_load_existing_session
aiohttp_session/nacl_storage.py docs/reference.rst
aiohttp_session/nacl_storage.py docs/reference.rst