If you benefit from web2py hope you feel encouraged to pay it forward by contributing back to society in whatever form you choose!

I have integrated the GAEMA code gaema with web2py to allow my sites to authenticate via Google, Twitter, and/or Facebook.

I modified the module very slightly and collapsed it into a single file - auth_base.py. From there I created the classes for Facebook, Twitter, and Google authentication. The auth_base.py file supports Friend Feed and perhaps other things that I was not interested in, so I did not implement them yet.

download the module files here authmodules.tgz

To use the modules, make sure that your auth_user table has both username and email in the fields. Twitter and Facebook don't provide email address in their callback, so they use username. Google uses the email field. (I suppose it would be easy to make Google use username as well). For Twitter and Facebook the username that I create is "Twitter_<uid>" or "Facebook_<uid>". I have considered creating a custom auth.get_or_create_user() method to allow the same user to associate multiple account types with a single auth_user record, but don't yet have use for that.

To use the auth modules, consider a controller something like:

def index():
    retval = dict()
    retval['twitter'] = A('Login Via Twitter', _href=URL(r=request, c='authtest', f='twitter'))
    retval['facebook'] = A('Login Via Facebook', _href=URL(r=request, c='authtest', f='facebook'))
    retval['google'] = A('Login Via Google', _href=URL(r=request, c='authtest', f='google'))
    return retval    

def twitter():
    if auth.is_logged_in():
        redirect(URL(r=request, c='default', f='index'))
    from applications.ec.modules.twitter_account import TwitterAuth
    auth.settings.login_form=TwitterAuth(request, response,
         **{
        'twitter_consumer_key':'<your key here>',
        'twitter_consumer_secret':'<your secret here>',
        'globals':globals()})

    return auth.login(next=URL(r=request, c='default', f='index'))

def facebook():
    if auth.is_logged_in():
        redirect(URL(r=request, c='default', f='index'))
    from applications.ec.modules.facebook_account import FacebookAuth
    auth.settings.login_form=FacebookAuth(request, response,
         **{
        'facebook_api_key':'<your key here>',
        'facebook_secret':'<your secret here>',
        'globals':globals()})

    return auth.login(next=URL(r=request, c='default', f='index'))

def google():
    if auth.is_logged_in():
        redirect(URL(r=request, c='default', f='index'))
    from applications.<appname>.modules.google_account import GoogleAuth
    auth.settings.login_form=GoogleAuth(request, response,
         **{'globals':globals()})

    return auth.login(next=URL(r=request, c='default', f='index'))

Update 2010-May-04

Facebook released the Graph API, and announced the end of FacebookConnect, so I did some rejiggering. the updated code is now an hg repository here: web2py_multiauth. The updated version also merges accounts if the user has the same email with a twitter login and facebook login for example. see the README file in the code for installation and configuration instructions.

Related slices

Comments (20)

  • Login to post



  • 0
    sanjaym 14 years ago
    Hi chhowes, thats a great module. Congrats !! However I am getting an error when I try to instantiate the CustomAuth class in my db.py. The error is: Error traceback 1.2.3.4.5.6.7.8.9. Traceback (most recent call last): File "D:\apps-new\gae-web2py\web2py\gluon\restricted.py", line 178, in restricted exec ccode in environment File "D:/apps-new/gae-web2py/web2py/applications/productwars/models/db.py", line 57, in auth = CustomAuth(globals(),T,db,True) File "D:\apps-new\gae-web2py\web2py\applications\productwars\modules\customAuth.py", line 28, in __init__ "Please try again.")TypeError: 'NoneType' object is not callable Looks like the T class has not been instantiated. Any help will be appreciated. regards sanjaym

  • 0
    cfhowes 14 years ago
    Sanjaym, sorry that i did not reply - i was not smart enough to subscribe to receive comments. are you still having problems with the code from the update? if so let me know and i'll try and help, cfhowes

  • 0
    malagaonrails 14 years ago
    Hi cfhowes! I'm trying to implement this script, but I haven't too much experience with web2py. I'm following the google code files, but don't know how implement this item: * upgrade your database if needed to have the fields of the custom auth_user table Can you help me? Thanks in advance. Greetings.

  • 0
    cfhowes 14 years ago
    malagaonrails, It looks like my readme needs a little work. included is a custom auth class that adds 2 fields to the auth_user table (otherwise it's the same as the built-in auth). so in db.py you should add the following import:
    from applications..modules.customAuth import CustomAuth
    
    and, where you initialize auth for the first time, use the custom auth like so:
    auth=CustomAuth(globals(),T,db, True)  # authentication/authorization
    
    and then keep auth.define_tables() or use auth.define_tables(migrate=True) if you are not using auto migration you will have to update the database to add the two new fields. for postgres the SQL updates commands would be like:
    ALTER TABLE auth_user ADD COLUMN twitter_id var_char(128) UNIQUE;
    ALTER TABLE auth_user ADD COLUMN facebook_id var_char(128) UNIQUE; 
    
    this allows the code that links accounts together to work properly. good luck, christian

  • 0
    malagaonrails 14 years ago
    Thank you very much, very helpful! I'm just trying.

  • 0
    pystar 13 years ago
    This slice doesnt work as it sats it should. It complains of my having duplicate columns named "marketing". Please help

  • 0
    cfhowes 13 years ago
    pystar, can you send traceback info? the custom table should only be creating one column named marketing. (which you might not even need for your app....i probably should have stripped it out of the sample code) christian

  • 0
    urielbertoche 13 years ago
    hi cfhowes I'm having some issues with your authentications. It's generating a really strange ID for every user, even those not using facebook, google or twitter auth. I didn't extended auth_table, could be that is the problem? Is there anyway to talk with you directly? do you have a gtalk account? thanks

  • 0
    cfhowes 13 years ago
    without the extended auth_user table i would not expect the code to work at all. auth_user.id is created by the database, so that depends on the database platform you are using. you can email me directly at howesc umich edu

  • 0
    abhishekguptaiitd 13 years ago
    First of all, really a big vote of thanks for such an awesome plugin. I followed the instructions and I am getting the following error. Traceback (most recent call last): File "/home/www-data/web2py/gluon/restricted.py", line 194, in restricted exec ccode in environment File "/home/www-data/web2py/applications/auth/controllers/login.py", line 19, in File "/home/www-data/web2py/gluon/globals.py", line 149, in self._caller = lambda f: f() File "/home/www-data/web2py/applications/auth/controllers/login.py", line 12, in login login_form = auth.login()#next=full_url('http',r=request)) File "/home/www-data/web2py/gluon/tools.py", line 1553, in login elif 'username' in table_user.fields: AttributeError: 'NoneType' object has no attribute 'fields'

  • 0
    cfhowes 13 years ago
    abhishek, it seems that somehow the auth_user table is not defined. can you check that you are calling auth.define_tables in db.py (or some model before you try and login)

  • 0
    abhishekguptaiitd 13 years ago
    In my db model I have the following code
    auth_user = db.define_table(
       'auth_user',
        db.Field('first_name', length=128,default=''),
        db.Field('last_name', length=128,default=''),
        db.Field('email', length=128,default=''),#,unique=True),
        db.Field('twitter_id', length=128,default=None),#,unique=True),
        db.Field('facebook_id', length=128,default=None),#,unique=True),
        db.Field('password', 'password', readable=False),
        db.Field('registration_key', length=128, writable=False,readable=False,default=''))
    
    auth_user.first_name.requires = IS_NOT_EMPTY()
    auth_user.last_name.requires = IS_NOT_EMPTY()
    auth_user.password.requires = [IS_LENGTH(minsize=6,
                                error_message="We're sorry.  Please " + \
                                "enter a password with at least six " + \
                                "characters."),
                               CRYPT()]
    auth_user.email.requires = [IS_EMAIL(error_message=T("Please enter a valid email address.")),
                         IS_NOT_IN_DB(db, 'auth_user.email',
                         error_message=T("We're sorry. " + \
                         "That email address already exists in our system. " + \
                         "Please login or choose another email address."))]
    auth_user.registration_key.default = ''
    
    auth.define_tables()
    
    Also, the table is there from appadmin view. But I am still getting the same error.

  • -1
    cfhowes 13 years ago
    try changing your auth_user definition to : auth.settings.table_user = db.define_table( auth.settings.table_user_name, ... )

  • 0
    abhishekguptaiitd 13 years ago
    Thanks. It is working now. Facebook and Gmail works fine but unfortunately, when I log in using twitter it does mark me as logged in, and instead I am taken back to the website without any session being created.

  • 0
    titogarrido 12 years ago
    Hi! I'm trying to use the updated version of your code but I couldn't put the facebook login to work, it always returns access denied (not sure how to debug it...) but the google auth is working... Any clue? Thanks in advance
    updates: seems that the "perms" parameter changed to scope: fblogin = function() {FB.login(function(response) { if (response.session) { window.location='{{=URL(r=request, c='auth_ext', f='facebook')}}' } else { // user is not logged in //window.location='{{=URL(r=request, c='auth_ext', f='denied')}}' console.log('falha') } }, {scope:'email'});}; Still not works, maybe response.session is wrong?
show more comments

Hosting graciously provided by:
Python Anywhere