Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Async Auth is broken #658

Closed
shirecoding opened this issue Jan 22, 2023 · 3 comments
Closed

Async Auth is broken #658

shirecoding opened this issue Jan 22, 2023 · 3 comments

Comments

@shirecoding
Copy link

shirecoding commented Jan 22, 2023

I've tried django_auth and it does not work with async endpoints because it reads request.user.is_authenticated which access the database, ok so i followed the "custom" authentication method as described in the documentation

async def django_auth_async(request: HttpRequest):
    """
    Default django cookie based authentication
    """
    
    is_authenticated = await sync_to_async(lambda: request.user.is_authenticated)()

    return is_authenticated

...

api = NinjaAPI(urls_namespace="fireside", auth=django_auth_async, csrf=True)

...

@router.get("/cron_pretty", url_name="cron_pretty")
async def cron_pretty(request, cron: str) -> str:
    auth = await request.auth
    return _cron_pretty(cron)

I tried accessing the API cron_pretty without passing in any cookies, and it still manages to allow it to pass through! pretty dangerous. the only way is to throw an exception inside the auth function, but the documentation makes it sounds like django ninja handles this which is misleading

async def django_auth_async(request: HttpRequest):
    """
    Default django cookie based authentication
    """
    
    is_authenticated = await sync_to_async(lambda: request.user.is_authenticated)()
    if not is_authenticated:
        raise Exception("PERMISSION DENIED")  # <------------- this is required
    return is_authenticated
@shirecoding shirecoding changed the title Async Auth is totally broken Async Auth is broken Jan 22, 2023
skokado added a commit to skokado/django-ninja that referenced this issue Apr 29, 2023
@skokado

This comment was marked as outdated.

@skokado
Copy link
Contributor

skokado commented May 1, 2023

@shirecoding described behavior is related to Django's standard WSGIRequest/ASGIRequest will set AnonymousUser to request.user default.

I think the root cause of this problem is that the Auth handler does not expect async operations.
The following simple code confirms this.

async def my_auth(request):
    request.called = True
    return "ok"


@app.get("/my_view", auth=my_auth)
def my_view(request):
    auth_called = hasattr(request, "called") and request.called
    return {"auth_called": auth_called}
$ curl localhost:8000/api/my_view
{"auth_called": false}

And you can also see that by making my_view from async to sync mode then get {"auth_called": true} as response
This is because it does not expect async function/method as callback here.

result = callback(request)

I will re-open the PR about this.

@vitalik
Copy link
Owner

vitalik commented Jun 4, 2023

@shirecoding this should work in 0.22

@vitalik vitalik closed this as completed Jun 4, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants