milesrichardson / ParsePy

A relatively up-to-date fork of ParsePy, the Python wrapper for the Parse.com API. Originally maintained by @dgrtwo
MIT License
516 stars 184 forks source link

User.current_user() never seems to be working for me. #98

Closed GGross5213 closed 9 years ago

GGross5213 commented 9 years ago

Traceback (most recent call last): File "app-test.py", line 152, in test_session_token me = User.current_user() File "/usr/local/lib/python2.7/dist-packages/parse_rest/user.p current_user return cls(**User.GET(user_url)) File "/usr/local/lib/python2.7/dist-packages/parse_rest/connec 30, in GET return cls.execute(uri, 'GET', **kw) File "/usr/local/lib/python2.7/dist-packages/parse_rest/connec 24, in execute raise exc(e.read()) ResourceRequestNotFound: {"code":101,"error":"invalid session"}

This is the error I keep getting. I have tried doing the login and then immediately using the Session Token to get the current user and it never works. Can someone please help?

dankrause commented 9 years ago

I'm not sure exactly what you're doing, but here's an example of how it works:

from parse_rest.connection import SessionToken, register
from parse_rest.user import User

# First, get a session token. This is probably the easiest way:
register(APPLICATION_ID, REST_API_KEY)
token = User.login(USERNAME, PASSWORD).sessionToken

# Then, you can either use that token in a block, like so:
with SessionToken(token):
    me = User.current_user()

# or use it globally, like so:
register(APPLICATION_ID, REST_API_KEY, session_token=token)
me = User.current_user()
dankrause commented 9 years ago

Feel free to reopen if this doesn't work for you.

codegoddesskh commented 9 years ago

This should still work when the session token is saved into our own local session, correct?

I do the following:

class LoginPage(BaseHandler):
    def get(self):
        self.render_template("login.html")

    @uses_parse_master
    def post(self):
        email = self.request.get('email')
        password = self.request.get('password')
        u = User.login(email, password)

        logging.info("session: " + str(self.session))
        logging.info("parse session: " + str(u.sessionToken))

        # To set a value into our session:                                                                                                                                                                                                     
        self.session[SESSION_TOKEN_KEY] = u.sessionToken
        logging.info("sessionToken put into session dict: " + str(u.sessionToken))
        self.display_message("You are logged in..." + str(u.sessionToken) +"<br><br>" + "<a href='/thing'>go to thing</a>")
def user_required(handler):
    """                                                                                                                                                                                                                                        
    Decorator that checks if there's a user associated with the current session.                                                                                                                                                               
    Will also fail if there's no session present.                                                                                                                                                                                              
    """
    #user_required registers the parse app itself so it doesn't need to                                                                                                                                                                        
    #decorate wth uses_parse_master. Think of it as a superset.                                                                                                                                                                                
    def check_login(self, *args, **kwargs):
        logging.exception("checking login... to login with session key")
        if not SESSION_TOKEN_KEY in self.session or not self.session[SESSION_TOKEN_KEY]:
            self.redirect(self.uri_for('login'), abort=True)
        else:
            logging.info("sending parse session key: " + str(self.session[SESSION_TOKEN_KEY]))
            register(APPLICATION_ID, REST_API_KEY, master_key=MASTER_KEY, session_token=self.session[SESSION_TOKEN_KEY])
            return handler(self, *args, **kwargs)

    return check_login

I've verified that the parse session key is printing when we register, matches what was saved, and matches the session token in Parse that is still not expired.

But, after the decorator executes, we still get the 101 invalid session error when trying to grab the user.

class UsefulThing(BaseHandler):
    @user_required
    def get(self):
        self.display_message("this is useful thing...." + str(User.current_user()))
codegoddesskh commented 9 years ago

Actually, let's simply that all a bit :)

I get an invalid key even when trying to pull the session directly after login. (The decorator uses_parse_master does our register call without a session key.)

class LoginPage(BaseHandler):
    def get(self):
        self.render_template("login.html")

    @uses_parse_master
    def post(self):
        email = self.request.get('email')
    password = self.request.get('password')
        u = User.login(email, password)

        logging.info("session: " + str(self.session))
        logging.info("parse session: " + str(u.sessionToken))
        logging.info("parse session type: " + type(u.sessionToken).__name__)

        # To set a value into our session:                                                                                                                                                                                                     
        #self.session[SESSION_TOKEN_KEY] = u.sessionToken                                                                                                                                                                                      
        #logging.info("sessionToken put into session dict: " + str(u.sessionToken))                                                                                                                                                            

    with SessionToken(u.sessionToken):
          me = User.current_user()
          logging.info("current user is...." + str(me))

        self.display_message("You are logged in..." + str(u.sessionToken) +"<br><br>" + "<a href='/thing'>go to thing</a>")

the User.current_user() throws the ResourceRequestNotFound: {"code":101,"error":"invalid session"} error.

  File "/Users/snip/Documents/Projects/guestbook/guestbook.py", line 162, in post
    me = User.current_user()
  File "/Users/snip/Documents/Projects/guestbook/lib/parse_rest/user.py", line 97, in current_user
    return cls(**User.GET(user_url))
  File "/Users/snip/Documents/Projects/guestbook/lib/parse_rest/connection.py", line 133, in GET
    return cls.execute(uri, 'GET', **kw)
  File "/Users/snip/Documents/Projects/guestbook/lib/parse_rest/connection.py", line 127, in execute
    raise exc(e.read())
ResourceRequestNotFound: {"code":101,"error":"invalid session"}

This is in a Google App Engine project, should that prove to be a useful bit of information!

dankrause commented 9 years ago

The code in your simplified example seems to be not valid. At the very least, there's some indentation issues. In any case, I simplified it down, and removed App Engine specific code, and ended up with this example, which works for me:

from parse_rest.connection import SessionToken, register
from parse_rest.user import User

register(APPLICATION_ID, REST_API_KEY)

u = User.login(USERNAME, PASSWORD)

print("parse session: " + str(u.sessionToken))
print("parse session type: " + type(u.sessionToken).__name__)

with SessionToken(u.sessionToken):
    me = User.current_user()
    print("current user is...." + str(me))

print("You are logged in..." + str(u.sessionToken))

In this case, the with SessionToken block is actually redundant. Calling User.login will update the current user.

A good use case for the with SessionToken syntax is storing Parse session tokens between requests.

For example, a user logs in to your app, which initiates a login with their user to Parse. Your app now has a Parse session token for that user. Your app stores that session token in it's own datastore. The next time the user requests a page from your own app, you simply wrap the requests to Parse in a with SessionToken block, and supply the stored token.

You could also do other things with it, such as have some app logged in as two users simultaneously, and operate on one user at a time within these blocks.

codegoddesskh commented 9 years ago

Agreed that the with SessionToken in this case is redundant, but I was just trying to nail down an example that was shorter and not working. We're trying to store the session token and use it on another request, but we get the invalid session error then as well.

I will explore some possible GAE library conflicts and report back if that was the cause.

GGross5213 commented 9 years ago

I never figured out what my problem was. I ended up just using the python requests library and calling the parse REST API directly.

codegoddesskh commented 9 years ago

Tracked it down, here are the layers:

        if ACCESS_KEYS.get('session_token'):
            request.add_header('X-Parse-Session-Token', ACCESS_KEYS.get('session_token'))

        if master_key and 'X-Parse-Session-Token' not in headers.keys():
            request.add_header('X-Parse-Master-Key', master_key)

I changed that snippet of code in connection.py, around line 111 to:

        if ACCESS_KEYS.get('session_token'):
            request.add_header('X-Parse-Session-Token', ACCESS_KEYS.get('session_token'))
        elif master_key:
            request.add_header('X-Parse-Master-Key', master_key)

And all is working now.

(Apologies for any indentation issues, I'm working on this code via screensharing, and I think some of the indentation gets mucked up in the various passing around. The change should be obvious despite any copy/paste problems.)

dgrtwo commented 9 years ago

That's a great fix @codegoddesskh- I've tried it in 70d5343. I'll hold off closing the issue until I've confirmed it works in other scenarios (I've been bit by this behavior a few times recently, actually)

codegoddesskh commented 9 years ago

Thanks for the quick response and confirmation. I'll keep an eye out for anything wacky happening too.

jeremyk23 commented 9 years ago

Maybe I'm missing something but even after using the fix in commit 70d5343 I still can't get User.current_user() or any authenticated call such as user = User.Query.get(objectId=userId) to work for me. I have the user log in client side via the Parse.FacebookUtils.logIn() Javascript method. Within the success block of that function I store the user's session token using the Django database-backed sessions.

Then later on, possibly after the user has navigated to a few different pages, I call:

token = request.session['session_token']
with SessionToken(token):
        user = User.current_user()

With this code, I get a ResourceRequestLoginRequired: {"error":"unauthorized"} error.

I never log the user in server-side with a call to User.login(), but if I had them login client side and I am using the valid Parse Session Token for that login, I should not be running into this problem. Correct?

codegoddesskh commented 9 years ago

Asking this just in case: Did you remember to call register with your application ID and API key?

jeremyk23 commented 9 years ago

Yeah, same as above I even tried changing it to the redundant:

register(...., session_token = token)
with SessionToken(token):
    user = User.current_user()

And I still get the same error.

codegoddesskh commented 9 years ago

Everything you're describing sounds reasonable and like it should work.

I did a little digging on {"error":"unauthorized"} which should be error information bubbled up directly from the Parse REST API.

Results point to possibly a wrong or changed API key. https://www.parse.com/questions/i-receive-errorunauthorized-for-rest-api-login https://www.parse.com/questions/getting-errorunauthorizederrorunauthorizederrorunauthorized https://www.parse.com/questions/401-unauthorized-error-with-parse-rest-api

jeremyk23 commented 9 years ago

Thank you! That's exactly what it was!

My REST API key was changed. I saw those posts about a wrong app ID, and after verifying my app ID was correct I didn't even check the the REST API key. Thanks again!

codegoddesskh commented 9 years ago

Yay! :) Glad I could help.

dankrause commented 9 years ago

To prevent this tread from ending up a mile long, I'm locking the issue. Please open a new issue if you have any other trouble with User.current_user() as it's not likely to be directly related to the discussion here.