django-oscar / django-oscar-api

RESTful JSON API for django-oscar
Other
369 stars 161 forks source link

How to Login with react js #293

Closed shamszzazzo closed 2 years ago

shamszzazzo commented 2 years ago

Trying to use react js to log in and other options but the problem is httponly session can't access by any kind of js even use withCredentials: true.

please give good documentation to communicate with API with js frameworks. or at least js.

maerteijn commented 2 years ago

@shamszzazzo I gave this a thought and it would indeed help to at least have some handgrips to get started.

I'm working on a javascript example with the fetch API. and how you should / could configure django-rest-framework to make this possible. I'll keep you posted in the following weeks.

farooqaaa commented 2 years ago

@shamszzazzo There a few reasons:

Also, don't use session authentication. Use something like django-knox.

shamszzazzo commented 2 years ago

Thank you for that, yes I use the same domain name and there it works. and I already use them in the setting.

- CSRF_COOKIE_SECURE = True
- SESSION_COOKIE_SECURE = True
- CORS_ALLOW_CREDENTIALS = True
- SESSION_COOKIE_SAMESITE = "None"
- CSRF_COOKIE_SAMESITE = "None"
CORS_ALLOWED_ORIGINS = [
    'http://127.0.0.1:3000',
    'http://localhost:3000',
    'https://mydomain.com',
]

but I am already using session authentication that oscar gives in by default. So should I change to django-knox. ?

farooqaaa commented 2 years ago

I think Oscar API is supposed to work best when your frontend is already inside Django (or share the same domain). If you have a separate frontend on a different domain, I would suggest moving away from session authentication. It's not easy to set up and not recommended in that case.

There is nothing special about session authentication. It's just a middleware that can be easily replaced. But there is a caveat: when you move to django-knox, your basket will no longer persist for unauthenticated users. Every request will generate a new basket. This can solved by creating your own basket views and maybe save the basket ID in cookies on the frontend. Then there are basket permission issues but explore the idea and see how it goes.

shamszzazzo commented 2 years ago

@farooqaaa so the idea is to use make my own custom authentication and session views for basket and others?

well, I have already done authentication but my problem is I can't read cookies from frontend because it httponly, besides, there is a CSRF issue when I need to log out (delete request in login).

All I want is to use session authentication and add and remove items from the basket but I can't because of the CSRF problem when I request logout (delete request in login) and after login tries to add something in the basket.

{
    "detail": "CSRF Failed: Referer checking failed - no Referer."
}
farooqaaa commented 2 years ago

If you want to stick to Session Authentication still then I guess something like this could work:

CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SAMESITE = 'None'
SESSION_COOKIE_SAMESITE = 'None'

Try these settings.

maerteijn commented 2 years ago

If you want to use oscarapi (or any restframework application):

For the first scenario I implemented a simple example how to login and fetch the basket. You can see if you are logged in by inspecting the owner field of the basket. if it's set, you are logged in. You can see this by clicking the "Fetch basket" button.

This example is now added to the sandbox.

Note that I have to send the CSRF token while communicating with the API..

This is explained in newer versions of the django documentation: https://docs.djangoproject.com/en/4.1/howto/csrf/#setting-the-token-on-the-ajax-request

shamszzazzo commented 2 years ago

If you want to use oscarapi (or any restframework application):

  • From a view in Django -> Session authentication
  • Any other situation -> Use tokens / JWT. You will need to set the correct CORS headers with django-cors-headers and do some session handling mechanism yourself.

For the first scenario I implemented a simple example how to login and fetch the basket. You can see if you are logged in by inspecting the owner field of the basket. if it's set, you are logged in. You can see this by clicking the "Fetch basket" button.

This example is now added to the sandbox.

Note that I have to send the CSRF token while communicating with the API..

This is explained in newer versions of the django documentation: https://docs.djangoproject.com/en/4.1/howto/csrf/#setting-the-token-on-the-ajax-request

Thanks for your effort,

it gives a good understanding of ajax requests for OscarAPI.

But I still face problems with connecting ReactJS. I did log in with reactJS, where my session and CSRF go-to cookies. So, When I try to delete a request in login it shows:

fetch(loginURL, {
      method: "DELETE",
      headers: {
        "Content-Type": "application/json",
        "X-CSRFToken": cookies.get("csrftoken"),
      },
      credentials: "same-origin"
    })
    .then(res => res.json())
    .then((data) => {
      console.log(data);
      // this.setState({isAuthenticated: true, username: "", password: "", error: ""});
    })
    .catch((err) => {
      console.log(err);
      // this.setState({error: "Wrong username or password."});
    });

it shows me success(delete request) but shows how don't delete cookies session. so it not properly works. Please help me to solve this issue.

in my setting.py.

CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_HTTPONLY = False  # False since we will grab it via universal-cookies
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = "None"
CSRF_COOKIE_SAMESITE = "None"

CORS_ALLOWED_ORIGINS = [
    'http://127.0.0.1:3000',
    'http://localhost:3000',
    'http://192.168.3.72:3000',
]
CORS_EXPOSE_HEADERS = ['Content-Type', 'X-CSRFToken']
CORS_ALLOW_CREDENTIALS = True
maerteijn commented 2 years ago

I updated the example with a Logout button and it now reads the CSRF token from the cookie (it changes of course after a POST or DELETE).

This is documented in the django docs: https://docs.djangoproject.com/en/4.1/howto/csrf/#acquiring-the-token-if-csrf-use-sessions-and-csrf-cookie-httponly-are-false

See the fulll example here: https://github.com/django-oscar/django-oscar-api/blob/6e2f7f994ad2fdddeba8fce8c337d811530a04fe/sandbox/templates/js-login-example.js

Please test it yourself with the sandbox.

I'll write some documentation regarding this the following weeks.

shamszzazzo commented 2 years ago

I updated the example with a Logout button and it now reads the CSRF token from the cookie (it changes of course after a POST or DELETE).

This is documented in the django docs: https://docs.djangoproject.com/en/4.1/howto/csrf/#acquiring-the-token-if-csrf-use-sessions-and-csrf-cookie-httponly-are-false

See the fulll example here: https://github.com/django-oscar/django-oscar-api/blob/6e2f7f994ad2fdddeba8fce8c337d811530a04fe/sandbox/templates/js-login-example.js

Please test it yourself with the sandbox.

I'll write some documentation regarding this the following weeks.

Sorry to say I try many ways but it won't delete Session id and CSRF token from cookies even if I try to logout(delete request)

login delete request:

   def delete(self, request, *args, **kwargs):
        """
        Destroy the session.

        for anonymous users that means having their basket destroyed as well,
        because there is no way to reach it otherwise.
        """
        request = request._request
        if request.user.is_anonymous:
            basket = operations.get_anonymous_basket(request)
            if basket:
                operations.flush_and_delete_basket(basket)

        request.session.clear()
        request.session.delete()
        request.session = None

        return Response("")

maybe here need some change like response.delete_cookie or something.

maerteijn commented 2 years ago

it won't delete Session id and CSRF token from cookies

And how do you determine that this is not the case?

It isn't deleting the session cookie, I'm investigating now why this is

maerteijn commented 2 years ago

I forgot to remove a line in the example, so if you did try the sandbox example you should pull the main branch again.

Here is a scenario that I think is doing what you are expecting.

1) I login with a user: You can see the response cookies csrftoken and sessionid are set: image

2) logout with the DELETE command: You can see that request cookies are the same as before. This makes sense as we are calling the DELETE before the session has been deleted on the server. You can also see there are no response cookies. image

3) fetch the basket: image You can see that the user is not logged in anymore because the owner is not set. You can also see that there is a new sessionid cookie set in the response, with a different value as the first one. At this point, we have a new (anonymous) session, as the old one was deleted. Django will detect that the earlier session cookie is not valid and sends back a new one.

So I think there is no explicit need for deleting the cookies, as they are overridden with new ones when needed. Session renewal is also depending on how you are maintaining sessions, so it's not a case of adding delete_cookie to the delete view, as sessions could also be maintained in many other ways.

I do however, would expect that the session cookie will be deleted. See the middleware of django which should do it:

https://github.com/django/django/blob/0dd29209091280ccf34e07c9468746c396b7778e/django/contrib/sessions/middleware.py#L36

maerteijn commented 2 years ago

Ok I figured out that the current implementation of the delete view is deleting too much so the standard django middleware isn't able to expire the session cookie.

I'm working on a PR to fix is, the result is that an expired cookie will be set by the django session middleware which will delete the cookie client side.

image

shamszzazzo commented 2 years ago

Thanks for your extraordinary effort, I will wait for it.

maerteijn commented 2 years ago

@shamszzazzo https://github.com/django-oscar/django-oscar-api/pull/294

This has been merged in the master branch so session cookies will be deleted when you call the delete logout view.

maerteijn commented 2 years ago

@shamszzazzo Any chance you had te opportunity to test the master branch?