Skip to content

Commit

Permalink
Refs #75: move JWTProcessor to starlette_web.common
Browse files Browse the repository at this point in the history
  • Loading branch information
dolamroth committed Feb 8, 2024
1 parent b617fa7 commit 33c91cc
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 17 deletions.
24 changes: 21 additions & 3 deletions docs/common/authorization_permissions.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## Authorization and permissions
## Authentication and permissions

`starlette_web.common` contains definitions for base user, authentication backend and permission class.
`starlette_web.common.authorization` contains definitions for base user,
authentication backend and permission classes.

**BaseUserMixin** is designed to fit any model, that can represent a user.
It does not define database methods and does not inherit SQLAlchemy orm `declarative_base`.
Expand All @@ -9,7 +10,7 @@ An **AnonymousUser** is a special version of `BaseUserMixin`, that always return
**Authentication backend** accepts HttpConnection instance (Request/Websocket)
and returns an instance of user or an AnonymousUser.
It also seeds request scope with user instance.
It is a shallow copy of authentication backend from DRF.
It is a shallow copy of authentication backend from Django Rest Framework.
As to the latest version of starlette_web, you may only set a single backend on an endpoint.

**Permission class** accepts request and determines, whether user have respective permissions.
Expand All @@ -22,6 +23,23 @@ and you may combine them with boolean operations in the same way, as in DRF.
- By default, any endpoint has `NoAuthenticationBackend` and empty list of permission classes.
- Authentication backends and permission classes work the same for BaseHttpEndpoint and BaseWSEndpoint alike.

### JWT utils

`encode_jwt` and `decode_jwt` (implemented with `PyJWT`) are provided in `starlette_web.common.authorization`.

Syntax:
- `encode_jwt(payload: dict, expires_in: int, **kwargs) -> str`
- `decode_jwt(token: str, **kwargs) -> dict`

Default encoding algorithm is set as `settings.AUTH_JWT_ALGORITHM`.

### Contrib auth module

`starlette_web.contrib.auth` module provides additional
utilities, endpoints and models for authorization and authentication.

*TODO: refactoring of starlette_web.contrib.auth is planned*

### Examples

See `starlette_web.contrib.auth` and `starlette_web.tests.api.test_auth` for examples of usage.
1 change: 0 additions & 1 deletion docs/howto/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ pip install starlette-web[all]
starlette-web has a lot of extra dependencies, most of which correspond to contrib modules:
- apispec
- admin
- auth
- mqtt
- postgres
- redis
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ dependencies = [
"filelock>=3.13.1,<3.14",
"marshmallow>=3.20.1,<3.21",
"chardet>=5.2.0,<5.3",
"PyJWT[crypto]>=2.8,<2.9",
]

[project.urls]
Expand All @@ -58,7 +59,6 @@ apispec = [
"openapi-spec-validator>=0.7.1,<0.8",
]
admin = ["starlette-admin>=0.11.2,<0.12"]
auth = ["PyJWT[crypto]>=2.8,<2.9"]
mqtt = ["gmqtt>=0.6.13,<0.7"]
postgres = ["asyncpg>=0.29,<0.30"]
redis = ["redis>=5.0.1,<5.1"]
Expand Down
3 changes: 3 additions & 0 deletions starlette_web/common/authorization/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# flake8: noqa

from starlette_web.common.authorization.jwt_utils import encode_jwt, decode_jwt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

import jwt

from starlette_web.common.conf import settings
from starlette_web.common.utils.json import StarletteJSONEncoder
from starlette_web.common.utils.inspect import get_available_options


Expand Down Expand Up @@ -51,13 +53,16 @@ def decode_jwt(self, encoded_jwt: str, **kwargs):

@cached_property
def _get_encode_secret_key(self):
raise NotImplementedError()
return str(settings.SECRET_KEY)

@cached_property
def _get_decode_secret_key(self):
raise NotImplementedError()
return str(settings.SECRET_KEY)

def _get_expires_at(self, expires_in: int = None, **kwargs) -> datetime.datetime:
if expires_in is None and kwargs.get("expires_in"):
expires_in = kwargs["expires_in"]

return datetime.datetime.utcnow() + datetime.timedelta(seconds=expires_in)

def _enhance_payload_for_encode(self, payload: dict, **kwargs) -> None:
Expand Down Expand Up @@ -88,3 +93,12 @@ def _get_decode_options(self, **kwargs) -> dict:
res["options"] = options

return res


jwt_processor = JWTProcessor(
algorithm=settings.AUTH_JWT_ALGORITHM,
verify_signature=True,
json_encoder=StarletteJSONEncoder,
)
encode_jwt = jwt_processor.encode_jwt
decode_jwt = jwt_processor.decode_jwt
11 changes: 1 addition & 10 deletions starlette_web/contrib/auth/utils.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import datetime
from dataclasses import dataclass
from functools import cached_property

from starlette_web.common.conf import settings
from starlette_web.common.utils.json import StarletteJSONEncoder
from starlette_web.contrib.auth.jwt_utils import JWTProcessor
from starlette_web.common.authorization.jwt_utils import JWTProcessor


TOKEN_TYPE_ACCESS = "access"
Expand All @@ -21,14 +20,6 @@ class TokenCollection:


class AuthJWTProcessor(JWTProcessor):
@cached_property
def _get_encode_secret_key(self):
return str(settings.SECRET_KEY)

@cached_property
def _get_decode_secret_key(self):
return str(settings.SECRET_KEY)

def _get_expires_at(self, expires_in: int = None, **kwargs) -> datetime.datetime:
token_type: str = kwargs.get("token_type", TOKEN_TYPE_ACCESS)

Expand Down

0 comments on commit 33c91cc

Please sign in to comment.