i have an application where we allow users to use Oauth2 for authentication and even Custom User Registrations. All the Users are saved into the default User entity in the datastore. If the user is logging in using Oauth2 for the first time a new record in the default User entity is created like this:
"""Check if user is already logged in"""
if self.logged_in:
logging.info('User Already Logged In. Updating User Login Information')
u = self.current_user
u.auth_ids.append(auth_id)
u.populate(**self._to_user_model_attrs(data, self.USER_ATTRS[provider]))
u.put()
else:
"""Create a New User"""
logging.info('Creating a New User')
ok, user = self.auth.store.user_model.create_user(auth_id, **self._to_user_model_attrs(data, self.USER_ATTRS[provider]))
if ok:
self.auth.set_session(
self.auth.store.user_to_dict(user)
)
self.redirect(continue_url)
for custom registrations records are inserted through the following handler.
class RegistrationHandler(TemplateHandler, SimpleAuthHandler):
def get(self):
self.render('register.html')
def post(self):
"""Process registration form."""
user = 'appname:%s' % self.request.get('email')
name = '%s %s' % (self.request.get('first_name'), self.request.get('last_name'))
password = self.request.get('password')
avatar = self.request.get('avatar')
act_url = user_activation.Activate(self.request.get('first_name'), self.request.get('email'))
ok, user = User.create_user(auth_id=user, name=name, password_raw=password, email=self.request.get('email'))
if ok:
self.auth.set_session(self.auth.store.user_to_dict(user))
acc = models.Account(display_name=self.request.get('first_name'), act_url=act_url, act_key=act_url.split('activate/')[1], user=users.User(User.get_by_auth_id(self.current_user.auth_ids[0]).email))
acc.put()
if avatar:
avt = models.Picture(is_avatar=True, is_approved=True, image=avatar, user=users.User(User.get_by_auth_id(self.current_user.auth_ids[0]).email))
avt.put()
self.redirect('/')
Now we are using webapp2_extras.sessions for session handling. We have different models like, Comments, Images, Reviews etc in which we want to use db.UserProperty() as the author field. However, the author field shows blank or None whenever we enter a record into any of these models using 'users.get_current_user()'. I think this is because we are handling the sessions through webapp2 sessions.
What we want to achieve is to be able to use the db.UserProperty field in various models and link appropriately to the current user using webapp2 sessions ?
the UserProperty() has to be passed with a User Object in order for it to properly insert the records. Even though we are able to enter the records using the following code :
user = users.User(User.get_by_auth_id(self.current_user.auth_ids[0]).email)
or
user = users.User(User.get_by_auth_id(self.current_user.auth_ids[0]).name)
but then we are not able to get the whole user object by referencing to model.author
Any ideas how we should achieve this ?
OAuth 2.0 is not currently supported by Users service. Supported options are
Google Accounts
OpenId
OAuth 1.0
I don't frankly understand what you're trying to accomplish with introducing db.User in to the codebase. Given there's self.current_user, I assume you're already handling authentication process.
When you do self.auth.store.user_model.create_user - that already gives you a webapp2's user object/entity (it has nothing to do with db.User though). I believe that's what you'll have to use as your author field given OAuth 2.0 constraint.
users.get_current_user() relies on a special cookie (App Engine internal). In fact, it has nothing to do with webapp2's session (or any other "custom" session for that matter). You could hack it by setting the cookie to a value that App Engine internals can understand and be tricked as if a user were logged in with one of the methods I mentioned, but I wouldn't recommend this approach. It is not documented (cookie name, format, etc.) and might be changed at any time.
Instead of using UserProperty to store references to the webapp2 user objects, you should instead store the auth_id as a StringProperty and add a convenience method for fetching the corresponding webapp2 user entity.
Something like this
from webapp2_extras.appengine.auth.models import User
class Comment(db.model):
text = db.StringProperty()
author = db.StringProperty()
def get_author(self):
return User.get_by_auth_id(self.author)
Related
Hi i'm currently using firebase admin sdk on my django server to handle my app. I would like to check if user a user first time login on the server side.I would like to use firebase isNewUser() on the django server but in the firebase admin sdk docs i don't see any information related to that.
My server side to get user from token(send from app):
from utils.firebase_utils import auth
class FirebaseAuthentication(authentication.BaseAuthentication):
def authenticate(self, request):
token = request.META['HTTP_AUTHORIZATION']
if not token:
return None
try:
decoded_token = auth.verify_id_token(token)
uid = decoded_token['uid']
user = auth.get_user(uid, app=None)
# need to check if user first time log in too
except Exception as e:
raise exceptions.AuthenticationFailed('No such user')
return (user, None) # authentication successful
How can i check if a user first time login?
There indeed is no isNewUser method in the Admin SDK for Python.
A workaround is to compare the creation_timestamp and last_sign_in_timestamp properties of the UserMetadata object. If the two are close (within 1 second of each other), it is a new user. This is in fact how the documentation said to check for "newness" of a user record before the isNewUser method was introduced, which happened because there was a granularity difference in the two timestamps for a short while.
O.K. I have a headache with this problem. I have to different sites(non django) with login option and I would like to join in it into one new website based on django.
Each of these two user databases consist of table with columns:(username, password, e-mail).
The problem is, I just can not copy it to User table in Django as we all know django is very rigid about it, so I am trying to think a way, existing users would be able to login to site as nothing has changed.
Is there any django/pythonic way to do so?
I was thinking to create an app, which would take a foreign key to User model. Within User model I would create two users (database_1, database_2), so whenever a user from database 1 would login, e.g. JohnSmith, he would be connected as database_1 user, but so would JessicaSimpson if she would be in database 1. I am just thing to create Authentication and Authorization app as system app in some way... Is this a right way thinking? Would love to hear from professionals. Thanks
in models:
from django.db import models
from django.contrib.auth.models import User, Group
# Create your models here.
class New_users(models.Model):
new_user_id = models.ForeignKey(User, unique=False)
username = models.CharField(max_length=25)
password = models.CharField(max_length=25)
email = models.CharField(max_length=25)
in views:
from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.
def home(request):
if request.method == 'POST':
#if username and password from New_users are ok...
login()#User where id is the same blah blah....
I'm a professional, and I would add the old users to the new DB and put in random passwords. I would also make a table of old_users with their old hashed passwords.
I would flag these old users such that, when they visit the new app, they would be forced to enter their old pw (you'd need to know the hash method) and, if successful, then set the old pw to the new user, and log them in.
If that's too much trouble, you could write a script that sends all the old users an email (naturally, you have their email address) and a link to a change_password form. It's pretty easy to extend the django reset password functionality. And it's a good thing to have.
Could you just migrate the existing users into the new database by looping through the existing users and calling the create_user function? This would take care of the password hashing and everything, assuming that you can decrypt your current passwords back to plaintext.
I currently use the "Google Accounts API" to allow users to login to my GAE app. So I use users.create_login_url and users.get_current_user and add an ndb.UserProperty to my own user entity so that I can retrieve data for that user.
I'm now in the process of switching to oauth2 (using authomatic).
I need to convert all of my existing user accounts to oauth2 and I'd like to make this as easy as possible for my users. This is my current plan:
Change the login from users service to oauth2.
After the user logs in, it will look like a new account and the user will not see his or her previous data.
I'll add a prominent message asking the user to login with the old users service.
I'll then merge the old users service account with the oauth2 account.
This should work, but it will be a little confusing for the users. Is there a better way of doing this?
I'll explain how I ended up doing this in case it helps others.
I call my users managers and I have a Manager entity for each user:
class Manager(ndb.Model):
user_account = ndb.StructuredProperty(UserAccount))
linked = ndb.BooleanProperty(default=False)
user = ndb.UserProperty()
The user property is the old users service account that I will get rid of. The user_account property stores info to identify the Oauth2 account:
class UserAccount(ndb.Model):
provider = ndb.StringProperty(required=True)
id = ndb.StringProperty(required=True)
name = ndb.StringProperty()
email = ndb.StringProperty()
Essentially, for each manager, I want to set a value for user_account (Oauth2 login) and remove user (old user account). I want to do this with minimum burden on the manager.
When the user has recently logged in under the old user account, that cookie will sill be active. Now, however, the user is logging in with an Oauth2 account. After logging in with Oauth2, we check to see if the old user account cookie is still active. If so, we merge the accounts automatically. Here is a sketch of the handler.
class ManagerPage(webapp2.RequestHandler):
def get(self):
# This returns a Manager entity after the user has logged in with
# Oauth2. If the user is logging in for the first time, this will
# be a blank Manager entity.
self.get_manager()
# Temporary processing to link accounts. If the user is still logged
# as a Google user (because that cookie hasn't expired), then we
# automatically transfer their old information to the new Manager
# entity. In doing the conversion below, manager.linked is set to
# True so this can't happen more than once. Now that the Manager
# entity has been updated, redirect back to the same page.
gae_user = users.get_current_user()
if not manager.linked and gae_user:
manager.convert_old_manager(gae_user)
self.redirect("/manager")
# Present info to the manager
...
template = JINJA_ENVIRONMENT.get_template("manager.html")
self.response.write(template.render(template_values))
If the old user account cookie is not active, then I have a link in the above manager page that asks the user to link the old account with the new account. When the user logs in with the old account, they are redirected to the above Manager Page, and the account is automatically linked.
Is it possible to use Flask-Social and Flask-Security if I only want to use Facebook Login, for example, for user registration and login, i.e. no local registration/login forms?
I looked through the Flask-Social example application and documentation but couldn't tell if this is possible. In the example application, users cannot login with Facebook unless they've previously registered. After registering with the example application, they can associate their Facebook account with their local account.
When I tried to call social.facebook.get_connection() I got an AttributeError 'AnonymousUser' object has no attribute 'id' because there's no current_user, which is defined by flask-security after registration/login.
This is doable without too much extra work using the #login_failed.connect_via decorator. With app as your instance of a Flask app, it would look like
#login_failed.connect_via(app):
def on_login_failed(sender, provider, oauth_response):
connection_values = get_connection_values_from_oauth_response(provider, oauth_response)
ds = current_app.security.datastore
user = ds.create_user( ... ) #fill in relevant stuff here
ds.commit()
connection_values['user_id'] = user.id
connect_handler(connection_values, provider)
login_user(user)
db.commit()
return render_template('success.html')
As for filling in the relevant stuff for creating the user, I just create a random string for the password, and haven't had issues leaving the email null. I also just included the exact same answer on the Flask-Social github page.
I need to save settings for each user on my application.
I tried to use the user as a parent of my settings object, but I cannot do this since users do not have keys.
I then created an instance in my settings object that has a reference to user property, but in the docs it says UserProperty is unstable if the user changes their email address.
I then decided to save the user_id in a StringProperty() but if the user logs in with OpenId, the user_id element is None.
Is there a way to tie the user settings to the user object that works for both google accounts and open_id accounts?
Couldn't you add a wrapper class around the google account / open_id account, so you can use the parent relationship? Something like the following:
UserWrapper(db.Model):
user = db.UserProperty()
UserSettings(db.Model):
...
# New User
user = users.get_current_user()
new_user_settings = UserSettings(...)
new_user_wrapper = UserWrapper(key=user.nickname(),
parent=new_user_settings,
user=user)
# Login Existing User
user = users.get_current_user()
user_wrapper = UserWrapper.get_by_key_name(user.nickname())
user_settings = user_wrapper.parent()
If the user wants to change their email address, look up the UserSettings with the old email, delete the associated UserWrapper, create a new UserWrapper for the new email address and associate with the old UserSettings.
Note I've made the UserSettings a parent of UserWrapper, in case associating multiple email addresses with the same account may be something of interest.