Repository: litestar-org/litestar

Issue:
StaticFilesConfig and virtual directories
I'm trying to write a ``FileSystemProtocol`` to load files from the package data using [importlib_resources](https://importlib-resources.readthedocs.io/en/latest/using.html#). But because ``directories`` is defined as ``DirectoryPath``, pydantic checks if the given directories exist in the local filesystem. 

This is not generally true, especially in any kind of virtual filesystem (e.g. a zipped package). I think this condition should be relaxed to support virtual filesystems.

https://github.com/starlite-api/starlite/blob/9bb6dcd57c10a591377cf8e3a537e9292566d5b9/starlite/config/static_files.py#L32


Discussion:
I agree. Do you want to submit a PR?

Resolving patch:
diff --git a/litestar/contrib/jwt/jwt_token.py b/litestar/contrib/jwt/jwt_token.py
--- a/litestar/contrib/jwt/jwt_token.py
+++ b/litestar/contrib/jwt/jwt_token.py
@@ -1,13 +1,18 @@
 from __future__ import annotations
 
+import dataclasses
 from dataclasses import asdict, dataclass, field
 from datetime import datetime, timezone
-from typing import cast
+from typing import TYPE_CHECKING, Any, cast
 
 from jose import JWSError, JWTError, jwt
 
 from litestar.exceptions import ImproperlyConfiguredException, NotAuthorizedException
 
+if TYPE_CHECKING:
+    from typing_extensions import Self
+
+
 __all__ = ("Token",)
 
 
@@ -42,6 +47,8 @@ class Token:
     """Audience - intended audience."""
     jti: str | None = field(default=None)
     """JWT ID - a unique identifier of the JWT between different issuers."""
+    extras: dict[str, Any] = field(default_factory=dict)
+    """Extra fields that were found on the JWT token."""
 
     def __post_init__(self) -> None:
         if len(self.sub) < 1:
@@ -63,8 +70,8 @@ def __post_init__(self) -> None:
         else:
             raise ImproperlyConfiguredException("iat must be a current or past time")
 
-    @staticmethod
-    def decode(encoded_token: str, secret: str | dict[str, str], algorithm: str) -> Token:
+    @classmethod
+    def decode(cls, encoded_token: str, secret: str | dict[str, str], algorithm: str) -> Self:
         """Decode a passed in token string and returns a Token instance.
 
         Args:
@@ -82,7 +89,12 @@ def decode(encoded_token: str, secret: str | dict[str, str], algorithm: str) ->
             payload = jwt.decode(token=encoded_token, key=secret, algorithms=[algorithm], options={"verify_aud": False})
             exp = datetime.fromtimestamp(payload.pop("exp"), tz=timezone.utc)
             iat = datetime.fromtimestamp(payload.pop("iat"), tz=timezone.utc)
-            return Token(exp=exp, iat=iat, **payload)
+            field_names = {f.name for f in dataclasses.fields(Token)}
+            extra_fields = payload.keys() - field_names
+            extras = payload.pop("extras", {})
+            for key in extra_fields:
+                extras[key] = payload.pop(key)
+            return cls(exp=exp, iat=iat, **payload, extras=extras)
         except (KeyError, JWTError, ImproperlyConfiguredException) as e:
             raise NotAuthorizedException("Invalid token") from e
 


Test patch:
diff --git a/tests/contrib/jwt/test_token.py b/tests/contrib/jwt/test_token.py
--- a/tests/contrib/jwt/test_token.py
+++ b/tests/contrib/jwt/test_token.py
@@ -8,6 +8,7 @@
 import pytest
 from hypothesis import given
 from hypothesis.strategies import datetimes
+from jose import jwt
 
 from litestar.contrib.jwt import Token
 from litestar.exceptions import ImproperlyConfiguredException, NotAuthorizedException
@@ -133,3 +134,28 @@ def test_sub_validation() -> None:
             iat=(datetime.now() - timedelta(seconds=30)),
             exp=(datetime.now() + timedelta(seconds=120)),
         )
+
+
+def test_extra_fields() -> None:
+    raw_token = {
+        "sub": secrets.token_hex(),
+        "iat": datetime.now(timezone.utc),
+        "azp": "extra value",
+        "email": "thetest@test.com",
+        "exp": (datetime.now(timezone.utc) + timedelta(seconds=30)),
+    }
+    token_secret = secrets.token_hex()
+    encoded_token = jwt.encode(claims=raw_token, key=token_secret, algorithm="HS256")
+    token = Token.decode(encoded_token=encoded_token, secret=token_secret, algorithm="HS256")
+    assert "azp" in token.extras
+    assert "email" in token.extras
+
+    raw_token = {
+        "sub": secrets.token_hex(),
+        "iat": datetime.now(timezone.utc),
+        "exp": (datetime.now(timezone.utc) + timedelta(seconds=30)),
+    }
+    token_secret = secrets.token_hex()
+    encoded_token = jwt.encode(claims=raw_token, key=token_secret, algorithm="HS256")
+    token = Token.decode(encoded_token=encoded_token, secret=token_secret, algorithm="HS256")
+    assert token.extras == {}