django-oscar / django-oscar-api

RESTful JSON API for django-oscar

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How to Login with react js

shamszzazzo opened this issue · comments

commented

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.

@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.

@shamszzazzo There a few reasons:

  • Are the frontend and backend on different domain names/IP?
  • Have you set up the CORS headers correctly? You need "SameSite" as "None".

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

commented

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. ?

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.

commented

@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."
}

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.

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

commented

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

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.

commented

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.

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

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

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

commented

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

@shamszzazzo #294

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

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