1

I've been very confused. I've been working with Google's User object, as can be found here:

https://cloud.google.com/appengine/docs/python/refdocs/google.appengine.api.users#google.appengine.api.users.User

After reading a few resources, I've been told that storing the User object itself is not a good idea, as emails can change. What Google recommends, is that you should store the User_Id itself, as it is persistently safe to do so (i.e., it remains static - always).

When I get the current user and try to get the User_Id value using the user_id() method on the user object, I get a None value response.

current_user = endpoints.get_current_user()
print current_user.user_id()
>> None

However, if I save the current_user as a UserProperty in NDB and then retrieve the same value, I do get the user_id. For example:

google_user = ndb.UserProperty(required = True)

print google_user.user_id()
>> 12345678901234567890

How are google user objects suppose to be handled? And more importantly, why is the user_id value None before NDB has saved the value? Is this a type of account/email verification?

You can find the advice from Google here:

https://cloud.google.com/appengine/docs/python/users/userobjects

"We strongly recommend that you do not store a UserProperty, because it includes the email address along with the user's unique ID. If a user changes their email address and you compare their old, stored User to the new User value, they won't match. Instead, consider using the User user ID value as the user's stable unique identifier."

Edit: I should mention, I'm testing on localhost. Is endpoints.get_current_user() handled differently when the applcation is deployed?

This is the example Google uses:

class ModelWithUser(ndb.Model):
    user_id = ndb.StringProperty()
    color = ndb.StringProperty()

    @classmethod
    def get_by_user(cls, user):
        return cls.query().filter(cls.user_id == user.user_id()).get()

If I'm querying the ModelWithUser via the user_id from the currently authenticated user (which I don't have access to), how am I suppose to get the user from ModelWithUser? Gah.

Mmm Donuts
  • 9,551
  • 6
  • 27
  • 49

2 Answers2

1

Okay, so after mining through Google searches, I came across this thread in regards to how you get the current users ID without a trip to the Datastore:

https://code.google.com/p/googleappengine/issues/detail?id=8848

Google's official response is that this is known issue and will not be fixed:

"Fixing this issue is not on our roadmap -- the Google ID token contains a G+ userid, not an App Engine userid, so the App Engine user object supplied cannot contain the App Engine userid. We recommend you use one of the workarounds mentioned above."

The workaround is here (from a Google developer): https://code.google.com/p/googleappengine/issues/detail?id=8848#c39

def _getUserId():
    """A workaround implementation for getting userid."""
    auth = os.getenv('HTTP_AUTHORIZATION')
    bearer, token = auth.split()
    token_type = 'id_token'
    if 'OAUTH_USER_ID' in os.environ:
        token_type = 'access_token'
    url = ('https://www.googleapis.com/oauth2/v1/tokeninfo?%s=%s'
           % (token_type, token))
    user = {}
    wait = 1
    for i in range(3):
        resp = urlfetch.fetch(url)
        if resp.status_code == 200:
            user = json.loads(resp.content)
            break
        elif resp.status_code == 400 and 'invalid_token' in resp.content:
            url = ('https://www.googleapis.com/oauth2/v1/tokeninfo?%s=%s'
                   % ('access_token', token))
        else:
            time.sleep(wait)
            wait = wait + i
    return user.get('user_id', '')

As much as I like working in Google's stack, I will admit - this is odd.

Edit:

Another workaround can be found here:

How can I determine a user_id based on an email address in App Engine?

Community
  • 1
  • 1
Mmm Donuts
  • 9,551
  • 6
  • 27
  • 49
0

This question is related (class UserProperty exists for backwards compatibility with existing datastore schemas only).

And, as it says in the documentation referenced in your question:

user_id()

Obtains the user ID of the user.

Returns

A permanent unique identifying string or None. If the email address was set explicitly, this will return None.

Was the email address of the user explicitly set?

Community
  • 1
  • 1
sclizard
  • 56
  • 5
  • I saw that as well. Where I'm stuck though is how you retrieve the user_id, if you don't store the object? `current_user().user_id()` returns `None`. – Mmm Donuts Nov 22 '16 at 18:15
  • @Kris, what does `endpoints` stand for? It should be the `users` class of the google appengine api, `from google.appengine.api import users`. – sclizard Nov 22 '16 at 20:06
  • @sclicard Hey, sorry for the late reply. It's from here https://cloud.google.com/appengine/docs/python/endpoints/auth#Specifying_authorized_clients_in_the_API_backend. I'm guessing its the same exact class. – Mmm Donuts Nov 23 '16 at 02:24