Django, Djoser social auth : State could not be found in server-side session data. status_code 400
Asked Answered
B

5

12

I'm implementing an auth system with django and react. The two app run respectively on port 8000, 3000. I have implemented the authentication system using the Djoser package. This package uses some dependencies social_core and social_django. Everything seems to be configured ok. I click on login google button...I'm redirected to the google login page and then back to my front-end react app at port 3000 with the state and code parameters on the url.

At this point I'm posting those parameters to the backend. The backend trying to validate the state checking if the state key is present in the session storage using the code below from (social_core/backends/oauth.py)

def validate_state(self):
        """Validate state value. Raises exception on error, returns state
        value if valid."""
        if not self.STATE_PARAMETER and not self.REDIRECT_STATE:
            return None
        state = self.get_session_state()
        request_state = self.get_request_state()
        if not request_state:
            raise AuthMissingParameter(self, 'state')
        elif not state:
            raise AuthStateMissing(self, 'state')
        elif not constant_time_compare(request_state, state):
            raise AuthStateForbidden(self)
        else:
            return state

At this point for some reasons the state session key is not there..and I receive an error saying that state cannot be found in session data ( error below )

{"error":["State could not be found in server-side session data."],"status_code":400}

I recap all the action I do:

  1. Front-end request to backend to generate given the provider google-oauth2 a redirect url. With this action the url is generated also the state key is stored on session with a specific value ( google-oauth2_state ).
  2. Front-end receive the url and redirect to google auth page.
  3. Authentication with google and redirection back to the front-end with a state and code parameters on the url.
  4. Front-end get the data form url and post data to back-end to verify that the state received is equal to the generated on the point (1).

For some reasons the state code is not persisted... Any ideas and help will be really appreciated.

Thanks to all.

Berkshire answered 15/2, 2021 at 8:48 Comment(5)
If you post the demo code, it helps figure out the issue.Smoothen
Did you solve this issue? happening to me also! But in my case having different URL for server and client so I guess session storing is causing issue but not sure how to fix it! Any help appericiated.Haggai
I wonder if this can be solved using Nginx, as the author did here: medium.com/swlh/…Zollie
I had React front-end running on localhost:3000 and backend running on 8000. This happened when session data was not being sent to localhost:8000. Unfortunately, I still don't know how to send session data from localhost:3000 to localhost:8000. So for time being, I build the react front-end using npm run build and used collectstatic command in Django. Then ran the front-end from the same port (localhost:8000). Now the session data was being sent automatically and everything works perfectly for me.Mourner
This answer is worked for me https://mcmap.net/q/1011993/-react-django-rest-framework-session-is-not-persisting-working/57316231#57316231Dincolo
F
3

ok so this is a common problem while you are working with social auth. I had the same problem for so many times.

The flow:

  1. make a request to http://127.0.0.1:8000/auth/o/google-oauth2/?redirect_uri=http://localhost:3000/ (example)

  2. you will get a authorization_url. if you notice in this authorization_url there is a state presented . this is the 'state of server side'.

  3. now you need to click the authorization_url link.Then you will get the google auth page.After that you will be redirect to your redirect url with a state and a code. Remember this state should be the same state as the server side state .(2)

  4. make post req to http://127.0.0.1:8000/auth/o/google-oauth2/?state=''&code=''. if your states are not the same then you will get some issue.

everytime you wanna login , you need to make a request to http://127.0.0.1:8000/auth/o/google-oauth2/?redirect_uri=http://localhost:3000/ and then to http://127.0.0.1:8000/auth/o/google-oauth2/?state=''&code='' thus you will get the same state.

Frisk answered 23/6, 2021 at 10:2 Comment(2)
Could you post a codepen of this? The explained way in the docs is to make the post-request from the frontend using the state & code, however this results in the error mentioned above because session ids or some other explanation does not match.Columbuscolumbyne
This is not working.Ibarra
M
1

Without necessary detailed information, I can only tell 2 possible reasons:

  1. You overrode backend with improper session operations(or the user was logged out before auth was finished).
  2. Front-end used incorrect state parameter

You could test social login without front-end, let's say if you're trying to sign in with Google:

  1. Enter the social login URL in browser, like domain.com:8000/login/google-oauth2/
  2. Authorize
  3. See if the page redirected to your default login page correctly

If yes, then probably you need to check your front-end code, and if no, then check your backend code.

At the end, if you're not so sensitive to the potential risk, you could also override GoogleOAuth2 class as following to disable state check:

from social_core.backends import google

class GoogleOAuth2(google.GoogleOAuth2):
    STATE_PARAMETER = False
Mildredmildrid answered 30/4, 2021 at 0:35 Comment(0)
T
1

Finally, I've reached a point where everything is functioning flawlessly, both on the local environment and in production. The core issue turned out to be related to cookies and sessions. The correct solution was to trick the backend server into perceiving the request as if it were coming from "localhost:8000" instead of "localhost:3000". This entails ensuring that the backend domain remains consistent.

To achieve this, there are two feasible approaches:

  1. Serve Frontend Build from the Server: The server should host the compiled version of the frontend. By doing this, the frontend will always share the same domain as the backend.

  2. Django View with Google Auth Logic: Implement a simple Django view and associate an empty template with it. This template should solely contain a script tag incorporating the necessary logic to manage Google authentication. The process involves the following steps:

    • When a user clicks on "Sign in with Google," they are directed to this view.
    • The view manages the authentication process, and upon completion, it retrieves the access token.
    • The access token is then passed to the frontend via URL parameters.

In my case, I opted for the second approach, as it suited my requirements better. The procedure involves creating a basic view and linking it to a template. This way, when a user selects "Sign in with Google," the corresponding view is triggered. Subsequently, the view takes over the authentication process. Once the access token is obtained, it is transferred to the specified URL.

Treillage answered 10/8, 2022 at 15:51 Comment(1)
drive.google.com/file/d/17bTCCN8-UDRCfIbAPpVyJyIQKVqFdOeO/… if someone doesn't understand the above explanation, I made a video explaining how you can sort out this problem. ThanksTreillage
U
0

I think you may need some changes in you authorizing flow in step NO.3 and 4.

3.Authentication with google and redirection back to the front-end with a state and code parameters on the url.
4.Front-end get the data form url and post data to back-end to verify that the state received is equal to the generated on the point (1).

maybe you should redirect back to server side after google's authorization.

then at the server side, do the check! validate the state and code (maybe do more things).

then let server redirect to the front-end site you wanted to before.

for some reason, redirect to front-end directly will miss the param.. :-)

Untruth answered 30/4, 2021 at 3:40 Comment(1)
Could you try to explain using a codepen or some code? The docs very clearly state that the general way to do this is to make the post request from the front end, but since the session id's don't match / are never added, or similar, the request fails.Columbuscolumbyne
I
0

I was facing the same issue but I was able to solve it and was able to get the JWT Access Token and Refresh Token for Google Login without replacing DJoser.

I have my Front-end in React JS and Backend in DJango (DRF, DJoser, DJoser Social Login, drf_social_oauth2, social_django).

Problem: "non_field_errors": ["Invalid state has been provided."], I was getting this response when I performed POST request to http://127.0.0.1:8000/auth/o/google-oauth2/?state=[state]&code=[code]

Solution: The problem is probably with session storage. The following steps helped me solve it.

1. Add this to the React App where you are making HTTP/HTTPS Requests:

import axios from "axios";

axios.defaults.withCredentials = true;

Source: React Django REST framework session is not persisting/working

2. In your DJango settings.py, set the following settings:

    CORS_ALLOW_ALL_ORIGINS = True
    CORS_ALLOW_HEADERS = ["Authorization", "Content-Type", "Accept"]
    CORS_ALLOW_CREDENTIALS = True

CORS_ALLOW_HEADERS = ["*"], this stopped working for some unknown reason. I had to specify the headers I was using.

Source: React Django REST framework session is not persisting/working

3. In addition, make sure "corsheaders.middleware.CorsMiddleware" is the first thing in the middleware.

MIDDLEWARE = [
            "corsheaders.middleware.CorsMiddleware",
            ...
            ]

I am not sure whether this helped in fixing the issue or not but I made this change which might have also played some role in fixing this issue.

Source: GitHub Copilot

GitHub Copilot:

Make sure that corsheaders.middleware.CorsMiddleware is the first middleware in your MIDDLEWARE. This ensures that the django-cors-headers middleware is the first to process the request and the last to process the response, which is necessary for it to correctly handle the CORS headers.

4. Change your React App host:

This is the major change which helped in solving the problem.

My React App was being hosted at: localhost:3000/ and DJango at 127.0.0.1:8000/

I changed hosting of my React App from localhost:3000 -> 127.0.0.1:3000

So for example I was making my Google Login Request from 127.0.0.1/login-register, the route looked like: 127.0.0.1:3000/login-register -> Google Login Screen -> (Redirect) 127.0.0.1:3000/login-register

In my case I get redirected to 127.0.0.1:3000/login-register, so make sure the redirect uri is also changed to 127.0.0.1

My request looked like:

Was axios.get("http://127.0.0.1:8000/auth/o/google-oauth2/?redirect_uri=http://localhost:3000/login-register");

Now
axios.get("http://127.0.0.1:8000/auth/o/google-oauth2/?redirect_uri=http://127.0.0.1:3000/login-register");

Don't forget to add or change your base url from localhost:3000 -> 127.0.0.1:3000 in Authorized redirect URIs at Google Cloud -> Credentials.

Source: React Django REST framework session is not persisting/working

  1. Add changes to your DJoser -> SOCIAL_AUTH_ALLOWED_REDIRECT_URIS in settings.py: Now add the your updated redirect uri to:
DJOSER = {
            # Your rest of DJoser Settings:
            "SOCIAL_AUTH_ALLOWED_REDIRECT_URIS": [
            "http://localhost:3000/login-register",
            "http://127.0.0.1:3000/login-register", # (Update)
            ],
        }

After applying the following changes, I was able to login with google and get Access and Refresh Tokens.

NOTE: I spent good time at fixing this issue. These are the changes which helped me in properly implementing Google Social Login. I hope these steps also help you fix this issue if you are facing. I have also attached screenshot of successful creation of User in the database. I have tried my best to share everything that has helped me.

User created: User Created

Getting the Access Token Refresh Token and Logged In: Access & Refresh Tokens

Inerrant answered 7/4 at 15:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.