From 9264fe7ec743044f61d6f7b9c7a297467e94dfd2 Mon Sep 17 00:00:00 2001 From: Massimo Di Pierro Date: Sun, 29 Jan 2012 15:22:11 -0600 Subject: [PATCH] http://en.wikipedia.org/wiki/One-time_password, thanks Madhukar R Pai --- VERSION | 2 +- gluon/contrib/login_methods/motp_auth.py | 104 +++++++++++++++++++++++ 2 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 gluon/contrib/login_methods/motp_auth.py diff --git a/VERSION b/VERSION index 27ed510d..06ba1e44 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -Version 1.99.4 (2012-01-29 15:21:17) stable +Version 1.99.4 (2012-01-29 15:22:07) stable diff --git a/gluon/contrib/login_methods/motp_auth.py b/gluon/contrib/login_methods/motp_auth.py new file mode 100644 index 00000000..a776b3e4 --- /dev/null +++ b/gluon/contrib/login_methods/motp_auth.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python + +import time +from hashlib import md5 +from gluon.dal import DAL + +def motp_auth(db=DAL('sqlite://storage.sqlite'), + time_offset=60): + + """ + motp allows you to login with a one time password(OTP) generated on a motp client, + motp clients are available for practically all platforms. + to know more about OTP visit http://en.wikipedia.org/wiki/One-time_password + to know more visit http://motp.sourceforge.net + + + Written by Madhukar R Pai (madspai@gmail.com) + License : MIT or GPL v2 + + thanks and credits to the web2py community + + to use motp_auth: + motp_auth.py has to be located in gluon/contrib/login_methods/ folder + first auth_user has to have 2 extra fields - motp_secret and motp_pin + for that define auth like shown below: + + ## after auth = Auth(db) + db.define_table( + auth.settings.table_user_name, + Field('first_name', length=128, default=''), + Field('last_name', length=128, default=''), + Field('email', length=128, default='', unique=True), # required + Field('password', 'password', length=512, # required + readable=False, label='Password'), + Field('motp_secret',length=512,default='', + label='MOTP Seceret'), + Field('motp_pin',length=128,default='', + label='MOTP PIN'), + Field('registration_key', length=512, # required + writable=False, readable=False, default=''), + Field('reset_password_key', length=512, # required + writable=False, readable=False, default=''), + Field('registration_id', length=512, # required + writable=False, readable=False, default='')) + + ##validators + custom_auth_table = db[auth.settings.table_user_name] # get the custom_auth_table + custom_auth_table.first_name.requires = \ + IS_NOT_EMPTY(error_message=auth.messages.is_empty) + custom_auth_table.last_name.requires = \ + IS_NOT_EMPTY(error_message=auth.messages.is_empty) + custom_auth_table.password.requires = CRYPT() + custom_auth_table.email.requires = [ + IS_EMAIL(error_message=auth.messages.invalid_email), + IS_NOT_IN_DB(db, custom_auth_table.email)] + + auth.settings.table_user = custom_auth_table # tell auth to use custom_auth_table + ## before auth.define_tables() + + ##after that: + + from gluon.contrib.login_methods.motp_auth import motp_auth + auth.settings.login_methods.append(motp_auth(db=db)) + + ##Instructions for using MOTP + - after configuring motp for web2py, Install a MOTP client on your phone (android,IOS, java, windows phone, etc) + - initialize the motp client (to reset a motp secret type in #**#), + During user creation enter the secret generated during initialization into the motp_secret field in auth_user and + similarly enter a pre-decided pin into the motp_pin + - done.. to login, just generate a fresh OTP by typing in the pin and use the OTP as password + + ###To Dos### + - both motp_secret and pin are stored in plain text! need to have some way of encrypting + - web2py stores the password in db on successful login (should not happen) + - maybe some utility or page to check the otp would be useful + - as of now user field is hardcoded to email. Some way of selecting user table and user field. + """ + + def verify_otp(otp,pin,secret,offset=60): + epoch_time = int(time.time()) + time_start = int(str(epoch_time - offset)[:-1]) + time_end = int(str(epoch_time + offset)[:-1]) + for t in range(time_start-1,time_end+1): + to_hash = str(t)+secret+pin + hash = md5(to_hash).hexdigest()[:6] + if otp == hash: return True + return False + + def motp_auth_aux(email, + password, + db=db, + offset=time_offset): + if db: + user_data = db(db.auth_user.email == email ).select().first() + if user_data: + if user_data['motp_secret'] and user_data['motp_pin']: + motp_secret = user_data['motp_secret'] + motp_pin = user_data['motp_pin'] + otp_check = verify_otp(password,motp_pin,motp_secret,offset=offset) + if otp_check: return True + else: return False + else: return False + return False + return motp_auth_aux