Repository: aogier/starlette-authlib (language: python)

Issue:
Middleware Does Not Honor nbf Claim
Issue:
If an  [nbf](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.5) registered claim is provided to this middleware, it is not honored.

Expected:
If an [nbf](https://datatracker.ietf.org/doc/html/rfc7519#section-4.1.5) claim is provided and it is in the future, then the session dict should be empty since the session is not technically valid.


Discussion / hints:

Fix patch:
diff --git a/starlette_authlib/middleware.py b/starlette_authlib/middleware.py
index 9aa14b0..6106a9a 100644
--- a/starlette_authlib/middleware.py
+++ b/starlette_authlib/middleware.py
@@ -9,7 +9,7 @@
 from collections import namedtuple
 
 from authlib.jose import jwt
-from authlib.jose.errors import BadSignatureError, DecodeError, ExpiredTokenError
+from authlib.jose.errors import BadSignatureError, DecodeError, ExpiredTokenError, InvalidTokenError
 from starlette.config import Config
 from starlette.datastructures import MutableHeaders, Secret
 from starlette.requests import HTTPConnection
@@ -86,9 +86,10 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
                     ),
                 )
                 jwt_payload.validate_exp(time.time(), 0)
+                jwt_payload.validate_nbf(time.time(), 0)
                 scope["session"] = jwt_payload
                 initial_session_was_empty = False
-            except (BadSignatureError, ExpiredTokenError, DecodeError):
+            except (BadSignatureError, ExpiredTokenError, DecodeError, InvalidTokenError):
                 scope["session"] = {}
         else:
             scope["session"] = {}


Test patch:
diff --git a/tests/test_session.py b/tests/test_session.py
index 797b05f..2253d9a 100644
--- a/tests/test_session.py
+++ b/tests/test_session.py
@@ -1,5 +1,6 @@
 import os
 import re
+from datetime import timedelta, datetime
 
 import pytest
 from starlette.applications import Starlette
@@ -120,6 +121,84 @@ def test_session_expires():
         )
         assert response.json() == {"session": {}}
 
+def test_session_futue_nbf():
+    now = datetime.now()
+    nbf = datetime.timestamp(now + timedelta(days=1))
+    claims = {
+        "nbf": nbf,
+        "some": "data"
+    }
+    for jwt_alg, secret_key in (
+        ("HS256", "example"),
+        (
+            "RS256",
+            SecretKey(
+                Secret(open(os.path.join(KEYS_DIR, "rsa.key")).read()),
+                Secret(open(os.path.join(KEYS_DIR, "rsa.pub")).read()),
+            ),
+        ),
+    ):
+        app = create_app()
+        app.add_middleware(
+            SessionMiddleware, jwt_alg=jwt_alg, secret_key=secret_key, https_only=True
+        )
+        secure_client = TestClient(app, base_url="https://testserver")
+
+        response = secure_client.get("/view_session")
+        assert response.json() == {"session": {}}
+
+        response = secure_client.post("/update_session", json=claims.copy())
+        assert response.json() == {"session": claims.copy()}
+
+        response = secure_client.get("/view_session").json()
+        assert response == {"session": {}}
+
+        response = secure_client.post("/clear_session")
+        assert response.json() == {"session": {}}
+
+        response = secure_client.get("/view_session")
+        assert response.json() == {"session": {}}
+    
+def test_session_past_nbf():
+    now = datetime.now()
+    nbf = datetime.timestamp(now - timedelta(seconds=1))
+    claims = {
+        "nbf": nbf,
+        "some": "data"
+    }
+    for jwt_alg, secret_key in (
+        ("HS256", "example"),
+        (
+            "RS256",
+            SecretKey(
+                Secret(open(os.path.join(KEYS_DIR, "rsa.key")).read()),
+                Secret(open(os.path.join(KEYS_DIR, "rsa.pub")).read()),
+            ),
+        ),
+    ):
+        app = create_app()
+        app.add_middleware(
+            SessionMiddleware, jwt_alg=jwt_alg, secret_key=secret_key, https_only=True
+        )
+        secure_client = TestClient(app, base_url="https://testserver")
+
+        response = secure_client.get("/view_session")
+        assert response.json() == {"session": {}}
+
+        response = secure_client.post("/update_session", json=claims.copy())
+        assert response.json() == {"session": claims.copy()}
+
+        response = secure_client.get("/view_session").json()
+        assert "exp" in response["session"]
+        del response["session"]["exp"]
+        assert response == {"session": claims.copy()}
+
+        response = secure_client.post("/clear_session")
+        assert response.json() == {"session": {}}
+
+        response = secure_client.get("/view_session")
+        assert response.json() == {"session": {}}
+
 
 def test_secure_session():
     for jwt_alg, secret_key in (