Skip to content

Commit

Permalink
Update AuthenticationBackend signature
Browse files Browse the repository at this point in the history
  • Loading branch information
aminalaee committed Aug 18, 2023
1 parent af2a95e commit 8359ce5
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 20 deletions.
26 changes: 15 additions & 11 deletions docs/authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ The class `AuthenticationBackend` has three methods you need to override:
* `logout`: Will be called only for the logout, usually clearin the session.

```python
from typing import Optional

from sqladmin import Admin
from sqladmin.authentication import AuthenticationBackend
from starlette.requests import Request
Expand All @@ -37,13 +35,14 @@ class AdminAuth(AuthenticationBackend):
request.session.clear()
return True

async def authenticate(self, request: Request) -> Optional[RedirectResponse]:
async def authenticate(self, request: Request) -> bool:
token = request.session.get("token")

if not token:
return RedirectResponse(request.url_for("admin:login"), status_code=302)
return False

# Check the token in depth
return True


authentication_backend = AdminAuth(secret_key="...")
Expand All @@ -56,8 +55,6 @@ admin = Admin(app=..., authentication_backend=authentication_backend، ...)
??? example "Full Example"

```python
from typing import Optional

from sqladmin import Admin, ModelView
from sqladmin.authentication import AuthenticationBackend
from sqlalchemy import Column, Integer, String, create_engine
Expand Down Expand Up @@ -93,9 +90,14 @@ admin = Admin(app=..., authentication_backend=authentication_backend، ...)
request.session.clear()
return True

async def authenticate(self, request: Request) -> Optional[RedirectResponse]:
if not "token" in request.session:
return RedirectResponse(request.url_for("admin:login"), status_code=302)
async def authenticate(self, request: Request) -> bool:
token = request.session.get("token")

if not token:
return False

# Check the token in depth
return True


app = Starlette()
Expand All @@ -120,7 +122,7 @@ You can also integrate OAuth into SQLAdmin, for this example we will integrate G
If you have followed the previous example, there are only two changes required to the authentication flow:

```python
from typing import Optional
from typing import Union

from authlib.integrations.starlette_client import OAuth
from sqladmin.authentication import AuthenticationBackend
Expand Down Expand Up @@ -155,12 +157,14 @@ class AdminAuth(AuthenticationBackend):
request.session.clear()
return True

async def authenticate(self, request: Request) -> Optional[RedirectResponse]:
async def authenticate(self, request: Request) -> Union[bool, RedirectResponse]:
user = request.session.get("user")
if not user:
redirect_uri = request.url_for('login_google')
return await google.authorize_redirect(request, redirect_uri)

return True


admin = Admin(app=app, engine=engine, authentication_backend=AdminAuth("test"))

Expand Down
16 changes: 9 additions & 7 deletions sqladmin/authentication.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import functools
import inspect
from typing import Any, Callable, Optional
from typing import Any, Callable, Union

from starlette.middleware import Middleware
from starlette.middleware.sessions import SessionMiddleware
from starlette.requests import Request
from starlette.responses import Response
from starlette.responses import RedirectResponse, Response


class AuthenticationBackend:
Expand All @@ -32,14 +32,14 @@ async def logout(self, request: Request) -> bool:
"""
raise NotImplementedError()

async def authenticate(self, request: Request) -> Optional[Response]:
async def authenticate(self, request: Request) -> Union[Response, bool]:
"""Implement authenticate logic here.
This method will be called for each incoming request
to validate the authentication.
If the request is authenticated, this method should return `None` or do nothing.
Otherwise it should return a `Response` object,
like a redirect to the login page or SSO page.
If a 'Response' or `RedirectResponse` is returned,
that response is returned to the user,
otherwise a True/False is expected.
"""
raise NotImplementedError()

Expand All @@ -56,8 +56,10 @@ async def wrapper_decorator(*args: Any, **kwargs: Any) -> Any:
auth_backend = getattr(admin, "authentication_backend", None)
if auth_backend is not None:
response = await auth_backend.authenticate(request)
if response and isinstance(response, Response):
if isinstance(response, Response):
return response
if not bool(response):
return RedirectResponse(request.url_for("admin:login"), status_code=302)

if inspect.iscoroutinefunction(func):
return await func(*args, **kwargs)
Expand Down
5 changes: 3 additions & 2 deletions tests/test_authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ async def logout(self, request: Request) -> bool:
request.session.clear()
return True

async def authenticate(self, request: Request) -> RedirectResponse:
if "token" not in request.session:
async def authenticate(self, request: Request) -> bool:
if "token" in request.session:
return RedirectResponse(request.url_for("admin:login"), status_code=302)
return False


app = Starlette()
Expand Down

0 comments on commit 8359ce5

Please sign in to comment.