Flask-Middleware / flask-security

Quick and simple security for Flask applications
MIT License
624 stars 155 forks source link

change password gives 401: You are not authenticated. Please supply the correct credentials #902

Closed lilz-egoto closed 1 month ago

lilz-egoto commented 6 months ago

This is for the JSON API. Trying to set up the changeable endpoint, but the endpoint returns a 401 with the message "You are not authenticated. Please supply the correct credentials" even if I supply both the X-XSRF-Token and the session cookie. This happens with both GET and POST.

This does not happen with 2FA setup/disable where I similarly need the session cookie

jwag956 commented 6 months ago

So just to be clear - using JSON or forms is completely independent of how you authorize/authenticate. The XSRF-Token is for CSRF protection - not authentication. Sounds like somehow the session cookie isn't being sent. Would need some code snippet to provide more insight.

lilz-egoto commented 6 months ago

I can confirm that I get sent a session cookie from flask once I login/register and is valid since I can setup/disable 2FA with it.

flask config:

  app.config["SESSION_COOKIE_SAMESITE"] = "Strict"
  app.config["SESSION_COOKIE_HTTPONLY"] = True
  app.config["SESSION_COOKIE_SECURE"] = True

  app.config["SECURITY_CSRF_COOKIE_NAME"] = "XSRF-TOKEN"
  app.config["SECURITY_CSRF_HEADER"] = "X-XSRF-Token"
  app.config["SECURITY_CSRF_PROTECT_MECHANISMS"] = ["session"]

  app.config["SECURITY_CHANGEABLE"] = True
  app.config["SECURITY_CHANGE_URL"] = "/change"

The frontend looks like this:

await axios(URL, {
  method: "GET",
  withCredentials: true,
  headers: {
    Accept: "application/json",
  },
})
  .then((response) => {
    this.csrf_token = response.data.response.csrf_token;
}

await axios(URL, {
  data: data,
  method: "POST",
  withCredentials: true,
  headers: {
    "Content-Type": "application/json",
    "X-XSRF-Token": this.csrf_token,
  },
})

I include X-XSRF-Token since it says to here: https://flask-security-too.readthedocs.io/en/stable/_static/openapi_view.html#post-/change

For comparison the request headers for 2FA setup and change password. I think this shows I'm sending the cookie in both instances:

change:

image

2fa:

image
jwag956 commented 6 months ago

Nothing jumps out - especially since you say you can access other endpoints like 2fa. I am curious that you set the CHANGE_URL to "/change" which is the default... Of course some reproducible case would be great - I assume that the user you are trying to /change has an existing password? Barring that - a more complete set of headers and body (including URL) would be good.

lilz-egoto commented 6 months ago
  const data = {
    password: this.password,
    new_password: this.new_password,
    new_password_confirm: this.new_password_confirm,
  };

More headers, including URL:

image
jwag956 commented 6 months ago

tf_setup is a bit odd since it supports being called whether the caller is authenticated or not. Looking at the code its possible it has a bug in the authenticated case where it isn't properly checking CSRF. I'll look into that.

I am wondering whether the caller is actually authenticated - could you go here: https://www.kirsle.net/wizards/flask-session.cgi and cut-paste in your session cookie contents - lets see what's there.

lilz-egoto commented 6 months ago

I deleted my cookies and logged back in. The first one is before 2FA, second is after 2FA validation (just in case you wanted to see both)

{
    "csrf_token": "65c92ed1a13d29c03020f411eacc0a30806cf203",
    "tf_state": "ready",
    "tf_user_id": "21db75026f2246e39f9ae439b7f95f28"
}
{
    "_fresh": true,
    "_id": "fd001fe1474e00d15677d374b44506f1c041384e870731386d2d09c93487ea1abf246de4705bc057cb9d7f4648a0a63ba7fd0cb70bcc6ce92a50fc6fcc63d626",
    "_user_id": "21db75026f2246e39f9ae439b7f95f28",
    "csrf_token": "65c92ed1a13d29c03020f411eacc0a30806cf203",
    "fs_cc": "sent",
    "fs_paa": 1705608186.4328818
}
jwag956 commented 6 months ago

All very odd - looks like the caller is in fact authenticated. All I can suggest is to start simplifying things - first - turn off CSRF (app.config["WTF_CSRF_ENABLED"] = False) (prior to initializing flask_security).

Did you change SECURITY_API_ENABLED_METHODS? could you show me your entire SECURITY config?

lilz-egoto commented 5 months ago

Sorry, it might be a few weeks until I can work on this again

jwag956 commented 1 month ago

My guess is that this is another manifestation of what we discovered with the session cookie being cleaned up on login GET. If this is still an issue - please re-open with more info. Thanks!