I have a webapp on google app engine that checks to see if a user is logged in (through the federated id) with the following code. I just changed domain names and now for some reason it isn't recognizing any of my current users. This is because the openID changes depending on the domain name which I found through a little testing. Is there any workaround or way to let these users log in?
user = users.get_current_user()
currentregistereduser = None
try:
user_db_qry = User.query(User.theid == user.federated_identity())
user_db_list = user_db_qry.fetch(1)
currentregistereduser = user_db_list[0]
# I go on here to do all of the stuff for a logged in user
#if the user does not exist yet
except IndexError:
logging.error("indexerror" + str(User.theid) + " and " + str(user.federated_identity()))
user_db = User(
name=user.nickname(),
email=user.email(),
theid=user.federated_identity(),
visits = 0,
medals = 0,
prestige = 1,
)
user_db.put()
#they go on to create their profile data here
self.redirect("/profile")
This seems to be a similar problem to the one faced by StackExchange when it implemented OpenID. You can see how they dealt with it here:
https://blog.stackoverflow.com/2010/04/openid-one-year-later/
From that page:
So our cross-site user account matching now works this way:
Match by GUID. This is something we generate and assign during account association, so it’s a perfect fingerprint.
match by OpenID URL. This works for the vast majority of OpenID providers.
match by OpenID provided email address … if you are on our trust whitelist. This works for those rare OpenID providers (currently, only Google GMail) who generate domain-specific identifiers.
You could also try using a OpenID Library. Several are listed here:
http://openid.net/developers/libraries/
Hope this helps.
Related
I'm using Firebase authentication to manage my users accounts.
Now, I need to change the uid of the users, then I'm trying to delete the user and import it again with the same password using python.
I'm trying to follow the documentation. But I might be missing something.
So, in the Firebase authentication page, I'm going to menu (in the right upper corner) and getting the base64_signer_key and base64_salt_separator values.
And trying to use the code below to delete the user, import the user and update the other fields:
for user in auth.list_users().iterate_all():
if user.email == 'myname#yahoo.com':
newId = CPF(cpf()).rawValue
oldId = user.uid
print('User: {}'.format(user._data))
# Delete the user
auth.delete_user(oldId)
# Recreate the user
users = [
auth.ImportUserRecord(
uid=newId,
email=user.email,
password_hash=user.password_hash.encode('utf-8'),
password_salt=None
),
]
hash_alg = auth.UserImportHash.scrypt(
key=base64.b64decode(base64_signer_key),
salt_separator=base64.b64decode(base64_salt_separator),
rounds=8,
memory_cost=14
)
try:
result = auth.import_users(users, hash_alg=hash_alg)
print('Successfully imported {0} users. Failed to import {1} users.'.format(
result.success_count, result.failure_count))
for err in result.errors:
print('Failed to import {0} due to {1}'.format(users[err.index].uid, err.reason))
except Exception as e:
print(e)
# Update user
auth.update_user(
newId,
phone_number=user.phone_number,
email_verified=user.email_verified,
display_name=user.display_name,
disabled=user.disabled
)
I'm following this documentation https://firebase.google.com/docs/auth/admin/import-users#import_users_with_firebase_scrypt_hashed_passwords
I'm able to delete and recreate the user, but when I try to login with the same user/password I'm getting FirebaseInvalidPasswordError.
What should I do recreate the user with same password and be able to authenticate in the standard way ?
After many tests, maybe I've managed to find a working way to solve the problem.
First of all, if you have created a new service account private key, go to GCP console here https://console.cloud.google.com/iam-admin/iam?authuser=0&project=[your_firebase-proect-id] and make sure your service account have the "Firebase Authentication" admin rights
(note the service account)
(check permission)
This was my first problem since without that permission, the firebase admin SDK always returns an empty password_salt and the string "UkVEQUNURUQ=" for the password_hash (which translates to "REDACTED").
Once I got the correct password hash and salt for user, your code should looks like this
# Recreate the user
users = [
auth.ImportUserRecord(
uid=newId,
email=user.email,
password_hash=base64.urlsafe_b64decode(user.password_hash),
password_salt=base64.urlsafe_b64decode(user.password_salt)
),
]
Note the base64.urlsafe_b64decode part? I've tried to manually export my probject users with the firebase cli though
firebase auth:export --project [project-id] users.csv
and noticed a big difference: Python password hash was
utfZLdz4phgAnRIKRUOxxFTKmbUEenbV1CbkQC0o4iorXpx-BJsdwofjAQkb1mUAgs_sO49cBv_lT8QuCztRzA== while CSV password hash was utfZLdz4phgAnRIKRUOxxFTKmbUEenbV1CbkQC0o4iorXpx+BJsdwofjAQkb1mUAgs/sO49cBv/lT8QuCztRzA== (in python slashes are undercores)
Don't know if my approach would cover all cases, but exporting auth from the cli and comparing their hashes with the python ones could lead you to solve further cases.
I'm using Cornice and Pyramid with ACL Auth. This is a duplicate of an older question which I'm re-asking as Pyramid has changed.
Current docs say that pyramid.security.has_permission has ben replaced with request.has_permission which has an optional context arg. I'm trying to use has_permission in a loop through all the services to see which services the current user (request) has access to.
The end goal is to dynamically scan all Cornice services (ie view files with Cornice's #resource decorator) to see which ones are authorized for a given permission (ie 'view') for the current user. I'm open to using another way of doing this besides has_permission.
The use case of this knowledge is to provide a Swagger Spec JSON document which only documents API endpoints available to the current user.
I'm expecting code to look something like this:
from cornice import service
# Get our list of services
services = service.get_services()
# Assume we have an authenticated user logged in, thus attaching auth info to request
for svc in services:
context = magic_context_function(svc)
if request.has_permission('view', context) == False:
# Code will go here to hide endpoint documentation for this endpoint
It seems the answer should be to use view_execution_permitted(context, request, name=''), but I can't get it to work with an arbitrary view name, as the name arg does not match to a cornice.service.name value.
However, here's a semi-solution from a Pyramid issue on Github . You'll need a few imports to make the linked solution work (better). Here's the full code
from pyramid.security import _get_registry, Allowed
from pyramid.interfaces import IRouteRequest, IRequest, IViewClassifier, ISecuredView, IView
from zope.interface import providedBy
def route_view_execution_permitted(context, request, route_name, name=''):
reg = _get_registry(request)
context_iface = providedBy(context)
request_iface = reg.queryUtility(
IRouteRequest,
name=route_name,
default=IRequest)
provides = (IViewClassifier, request_iface, context_iface)
view = reg.adapters.lookup(provides, ISecuredView, name=name)
if view is None:
view = reg.adapters.lookup(provides, IView, name=name)
if view is None:
raise TypeError('No registered view satisfies the constraints. '
'It would not make sense to claim that this view '
'"is" or "is not" permitted.')
return Allowed(
'Allowed: view name %r in context %r for route %r (no permission defined)' %
(name, context, route_name))
return view.__permitted__(context, request)
One can use the above function to determine if the current user (determined from request object) is able to access a service (by name) like so:
from cornice import service
services = service.get_services()
for svc in services:
view_permitted = route_view_execution_permitted(request.context, request, svc.name)
if view_permitted == True:
# Do something interesting...
I found that the above solution has two deficiencies:
It's slow because each iteration of the svc loop opens a new connection to the API for some reason.
It returns an erroneous results (ie says one has permission for services he does not, and vice versa)
Perhaps someone can see a way to improve the answer above. In the meantime, here's a solution using the ACL's attached to each service and then determining if the current request.effective_principals match.
# Now see if current user meets ACL requirements for any permission
is_permitted = None # set our default.
for ace in acl:
for principal in request.effective_principals:
if ace[1] == principal:
is_permitted = True if ace[0] == Allow else False
break
if is_permitted is not None:
break
if is_permitted is True:
# Do something interesting...
The weaknesses here are:
It's slow for the same reason as the previous solution
As implemented, it only looks at #resource-decorated service classes, and not the #view-decorated methods which may have permissions or acls of their own.
This can be remedied with something like:
for method, view, args in service.definitions:
if 'permission' in args:
# Now start looking at permission to see if they match what's given by the parent ACL in the resource class
# Also, the special "__no_permission_required__" value means we should not have a Security Requirement Object
if args['permission'] == NO_PERMISSION_REQUIRED :
# Interesting....
I am working on a webapp based on google app engine.
The application uses the google authentication apis.
Basically every handler extends from this BaseHandler and as first operation of any get/post the checkAuth is executed.
class BaseHandler(webapp2.RequestHandler):
googleUser = None
userId = None
def checkAuth(self):
user = users.get_current_user()
self.googleUser = user;
if user:
self.userId = user.user_id()
userKey=ndb.Key(PROJECTNAME, 'rootParent', 'Utente', self.userId)
dbuser = MyUser.query(MyUser.key==userKey).get(keys_only=True)
if dbuser:
pass
else:
self.redirect('/')
else:
self.redirect('/')
The idea is that it redirects to / if no user is logged in via Google OR if there is not a User in my db of users having that google id.
The problem is that I can succesfully log in my web app and make operations. Then, from gmail, o Logout from any google account BUT if i try to keep using the web app it works.
This means the users.get_current_user() still returns a valid user (valid but actually OLD).
Is that possible?
IMPORTANT UPDATE
I Do Understand what explained in the Alex Martelli's Comment: There is a cookie which keeps the former GAE authentication valid.
The problem is that the same web app also exploits the Google Api Client Library for Python https://developers.google.com/api-client-library/python/ to perform operations on Drive and Calendar. In GAE apps such library can be easily used through decorators implementing the whole OAuth2 Flow (https://developers.google.com/api-client-library/python/guide/google_app_engine).
I therefore have my Handlers get/post methods decorated with oauth_required like this
class SomeHandler(BaseHandler):
#DECORATOR.oauth_required
def get(self):
super(SomeHandler,self).checkAuth()
uid = self.googleUser.user_id()
http = DECORATOR.http()
service = build('calendar', 'v3')
calendar_list = service.calendarList().list(pageToken=page_token).execute(http=http)
Where decorator is
from oauth2client.appengine import OAuth2Decorator
DECORATOR = OAuth2Decorator(
client_id='XXXXXX.apps.googleusercontent.com',
client_secret='YYYYYYY',
scope='https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/drive.appdata https://www.googleapis.com/auth/drive.file'
)
It usually works fine.
However (!!) when the app is idle for a long time it happens that the oauth2 decorator redirects me to the Google authentication page where, if I change account (I have 2 different accounts) Something WEIRD happens:
The app is still logged as the former account (retrieved through users.get_current_user()) while the api client library, and thus the oauth2 decorator, returns data (drive, calendar, etc.) belonging to the second account.
Which is REALLY not appropriate.
Following the example above (SomeHandler class) suppose I am logged as Account A. The users.get_current_user() always returns A as expected. Now suppose I stopped using the app, after a long while the oauth_required redirects me to the Google Account page. I therefore decide (or make a mistake) to log is as Account B. When accessing the Get method of the SomeHandler class the userId (retrived through users.get_current_user() is A while the list of calendars returned through the service object (Google Api client Library) is the list of calendars belonging to B (the actual currently logged user).
Am I doing something wrong? is Something expected?
Another Update
this is after the Martelli's Answer.
I have updated the handlers like this:
class SomeHandler(BaseHandler):
#DECORATOR.oauth_aware
def get(self):
if DECORATOR.has_credentials():
super(SomeHandler,self).checkAuth()
uid = self.googleUser.user_id()
try:
http = DECORATOR.http()
service = build('calendar', 'v3')
calendar_list = service.calendarList().list(pageToken=page_token).execute(http=http)
except (AccessTokenRefreshError, appengine.InvalidXsrfTokenError):
self.redirect(users.create_logout_url(
DECORATOR.authorize_url()))
else:
self.redirect(users.create_logout_url(
DECORATOR.authorize_url()))
so basically I now use oauth_aware and, in case of none credentials I logout the user and redirect it to the DECORATOR.authorize_url()
I have noticed that after a period of inactivity, the handler raises AccessTokenRefreshError and appengine.InvalidXsrfTokenError exceptions (but the has_credentials() method returns True). I catch them and (again) redirect the flow to the logout and authorize_url()
It seems to work and seems to be robust to accounts switch.
Is it a reasonable solution or am I not considering some aspects of the issue?
I understand the confusion, but the system is "working as designed".
At any point in time a GAE handler can have zero or one "logged-in user" (the object returned by users.get_current_user(), or None if no logged-in user) and zero or more "oauth2 authorization tokens" (for whatever users and scopes have been granted and not revoked).
There is no constraint that forces the oauth2 thingies to match, in any sense, the "logged-in user, if any".
I would recommend checking out the very simple sample at https://code.google.com/p/google-api-python-client/source/browse/samples/appengine/main.py (to run it, you'll have to clone the whole "google-api-python-client" package, then copy into the google-api-python-client/source/browse/samples/appengine directory directories apiclient/ and oauth2client/ from this same package as well as httplib2 from https://github.com/jcgregorio/httplib2 -- and also customize the client_secrets.json -- however, you don't need to run it, just to read and follow the code).
This sample doesn't even use users.get_current_user() -- it doesn't need it nor care about it: it only shows how to use oauth2, and there is no connection between holding an oauth2-authorized token, and the users service. (This allows you for example to have cron execute on behalf of one or more users certain tasks later -- cron doesn't log in, but it doesn't matter -- if the oauth2 tokens are properly stored and retrieved then it can use them).
So the code makes a decorator from the client secrets, with scope='https://www.googleapis.com/auth/plus.me', then uses #decorator.oauth_required on a handler's get to ensure authorization, and with the decorator's authorized http, it fetches
user = service.people().get(userId='me').execute(http=http)
with service built earlier as discovery.build("plus", "v1", http=http) (with a different non-authorized http).
Should you run this locally, it's easy to add a fake login (remember, user login is faked with dev_appserver) so that users.get_current_user() returns princess#bride.com or whatever other fake email you input at the fake login screen -- and this in no way inhibits the completely separate oauth2 flow from still performing as intended (i.e, exactly the same way as it does without any such fake login).
If you deploy the modified app (with an extra user login) to production, the login will have to be a real one -- but it's just as indifferent to, and separate from, the oauth2 part of the app.
If your application's logic does require constraining the oauth2 token to the specific user who's also logged into your app, you'll have to implement this yourself -- e.g by setting scope to 'https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/plus.profile.emails.read' (plus whatever else you need), you'll get from service.people().get(userId='me') a user object with (among many other things) an emails attribute in which you can check that the authorization token is for the user with the email you intended to authorize (and take remedial action otherwise, e.g via a logout URL &c). ((This can be done more simply and in any case I doubt you really need such functionality, but, just wanted to mention it)).
this is my first web-programming experience so I hope my questions doesn't sound very dumb. I have been stucked on this for many days.
I am trying to understand a sample code:
https://github.com/facebook/runwithfriends
However I am not understanding very well how the information flow works and how can I modify that sample (i.e. how the code works).
For example, in the following section of the code:
class RecentRunsHandler(BaseHandler):
"""Show recent runs for the user and friends"""
def get(self):
if self.user:
friends = {}
for friend in select_random(
User.get_by_key_name(self.user.friends), 30):
friends[friend.user_id] = friend
self.render(u'runs',
friends=friends,
user_recent_runs=Run.find_by_user_ids(
[self.user.user_id], limit=5),
friends_runs=Run.find_by_user_ids(friends.keys()),
)
else:
self.render(u'welcome')
As I understand (along with HTML) is useful for showing friends that are using the same app, and if I understand correctly, here is the essential part:
*friends_runs=Run.find_by_user_ids(friends.keys())*
But what if I want to show any given friend. How can I do it?
Summarizing, I would like to know:
1- How the flow of the code works? (I don't fully understand the explanation here)
2- How can I manipulate the code so to get, for example, to show a list of friends of the user (not necessary that use the same app)?
Moreover, Can I show friends filtered by some characteristic (for example, gender)?
Thanks a lot!
The python "SDK" for facebook I use I took from https://gist.github.com/1190267
and combined it with the code from the example app to achieve the functionality I wanted both for a canvas app and for website usage.
It depends whether you're using facebook with websites or a canvas application. For a canvas application you probably could do well with the javascript SDK but for a "login with facebook" I required serverside logic that should work with javascript turned off so I've completed that solution with details you might have help to know. You can try make small changes of that specific app 'runwithfriends' to get an understanding which code does what. The project you're looking at contains some outdated practice though:
getting and setting cookies is likely preferable now doing with webapp2's builtin functions for this instead of the code that comes with the FB example app
logging in and out is now done with OAuth 2.0 so it's likely that the login system you're looking at is outdated and you need to use OAuth 2.0 which is described here. I much rather do login/logout serverside so I did an OAuth 2.0 pure python solution to login / logout following the authentication steps mentioned in the tutorial from FB. I had to clear the cookie to log a user out which was not documented.
To upgrade to python 2.7 I had to also modify so that HTTP header did not cast to unicode. I don't know why but otherwise it complained that headers were "not strings"
To more elaborately answer your specific questions:
1) The requesthandler class you posted is a subclass of a BaseHandler so to fully understand what it does you can look at the BaseHandler class since what you are posting is a BAseHandler. The BaseHandler uses django templates for rendering and if you want to can switch the template engine to jinja2 which is remmended. Further the code accesses the user object inherited from the BaseHandler and does some operations on it and renders it to a template. You can try make a requesthandler of your own, subclass BaseHandler and do what you want.
2) I could manipulate the code and I'm not an expert so you should be able to do it too. I wanted a simple FB app to display random images and I could manipulate it to select random images via blobs and render to to a template while keeping the facebook base functions. A function to use for getting the user using the Graph API I do this:
def parse_signed_request(signed_request, secret):
"""
Parse signed_request given by Facebook (usually via POST),
decrypt with app secret.
Arguments:
signed_request -- Facebook's signed request given through POST
secret -- Application's app_secret required to decrpyt signed_request
"""
if '.' in signed_request:
(esig, payload) = signed_request.split('.')
else:
return {}
sig = urlsafe_b64decode(str(esig))
data = _parse_json(urlsafe_b64decode(str(payload)))
if not isinstance(data, dict):
raise SignedRequestError('Pyload is not a json string!')
return {}
if data['algorithm'].upper() == 'HMAC-SHA256':
if hmac.new(secret, payload, hashlib.sha256).digest() == sig:
return data
else:
raise SignedRequestError('Not HMAC-SHA256 encrypted!')
return {}
def get_user_from_cookie(cookies, app_id, app_secret):
"""Parses the cookie set by the official Facebook JavaScript SDK.
cookies should be a dictionary-like object mapping cookie names to
cookie values.
If the user is logged in via Facebook, we return a dictionary with the
keys "uid" and "access_token". The former is the user's Facebook ID,
and the latter can be used to make authenticated requests to the Graph API.
If the user is not logged in, we return None.
Download the official Facebook JavaScript SDK at
http://github.com/facebook/connect-js/. Read more about Facebook
authentication at http://developers.facebook.com/docs/authentication/.
"""
cookie = cookies.get('fbsr_' + app_id, '')
if not cookie:
return None
response = parse_signed_request(cookie, app_secret)
if not response:
return None
args = dict(code=response['code'], client_id=app_id,
client_secret=app_secret, redirect_uri='')
file = \
urllib.urlopen('https://graph.facebook.com/oauth/access_token?'
+ urllib.urlencode(args))
try:
token_response = file.read()
finally:
file.close()
access_token = cgi.parse_qs(token_response)['access_token'][-1]
logging.debug('returning cookie')
return dict(uid=response['user_id'], access_token=access_token)
See http://developers.facebook.com/docs/api for complete documentation for the API. And you can get the the official Facebook JavaScript SDK at http://github.com/facebook/connect-js/
I'm now writing code to sync a webapp2_extras.auth account with facebook so that custom accounts and facebook accounts can co-exist and we're discussing solutions for this in the webapp2 groups and categories. The current way I do it is adding the recommended current_user to a basehandler and using that as the FB identity while working on "merging" my class FBUser that is a custom class for facebook users that autheorized my website and/or canvas application to sync with webapp2_extras.auth.models.User which is an expando model so it can just add the properties it doesn't have such as facebookid, firstname, lastname, etc.
#property
def current_user(self):
if not hasattr(self, '_current_user'):
self._current_user = None
cookie = get_user_from_cookie(self.request.cookies,
facebookconf.FACEBOOK_APP_ID,
facebookconf.FACEBOOK_APP_SECRET)
if cookie:
# Store a local instance of the user data so we don't need
# a round-trip to Facebook on every request
user = FBUser.get_by_key_name(cookie['uid'])
if not user:
graph = GraphAPI(cookie['access_token'])
profile = graph.get_object('me')
user = FBUser(key_name=str(profile['id']),
id=str(profile['id']),
name=profile['name'],
profile_url=profile['link'],
access_token=cookie['access_token'])
user.put()
elif user.access_token != cookie['access_token']:
user.access_token = cookie['access_token']
user.put()
self._current_user = user
return self._current_user
You can also solve your authentication with session objects and build your authentication system around that. That is what I do when using both custom accounts and facebook accounts and you're welcome to have a lok at my repository for more code examples how to intregrate facebook with google app engine using python 2.7.
I built a wiki using Google App engine and the Data APIs. The wiki pages are stored as Google Base 'Reference Articles.' I want users to be able to view, edit, and delete the items, so when a request is made to the server, client login uses my username and password, and retrieves or edits the data on the user's behalf. The login code:
client = gdata.base.service.GBaseService()
client.ssl = False
gdata.alt.appengine.run_on_appengine(client)
#EMAIL, API_KEY and PASSWORD are constants stored on the server
client.email = EMAIL
client.password = PASSWORD
client.api_key = API_KEY
client.ProgrammaticLogin()
q = gdata.base.service.BaseQuery()
q.feed = '/base/feeds/items/' + self.base_id
item = base_client.GetItem(q.ToUri())
This works fine for me, but if I log out of my google account, it returns the following error:
'status': 401L, 'body': '<HTML>\n<HEAD>\n<TITLE>Authorization required</TITLE>
All I want is for the users to be able to CRUD my data stored on Base. What am I doing wrong?
Thanks in advance
It sounds like logging out in your client is invalidating all sessions for your account. Your best bet is probably to create a role account specifically for your app to use.