After being sidetracked with unrelated work I'm finally back to 
implementing the OTP stuff.

Just to update you on how I decided to go about the 'self service' side of 
things - I'm basically using django-otp unchanged and I'm adding an 
AuthProfile model that users have some control of. This is where I control 
limiting them in what they can add - right now only a mobile device (TOTP) 
and static backup codes.

Everything is basically working. The decorators do what they say they will 
do but I've reached another place where I can see things can either be 
improved on django-otp's side or maybe some guidance can be given to lead 
me in the right direction.

The Login Page:

I'm trying to make it so that users will only see a Username / Password 
field initially. Then if they have OTP options configured, send them to 
step 2. Currently the OTP login form shows the token and allows for 
selection of a device intelligently. 

One issue (might be considered minor) is that if they login with this form 
and fat finger or fail the OTP token their password stays filled in the 
form. Removing the password is easy enough but then they have to type it 
again and the token again which isn't ideal since it leads to possibly more 
mistakes and frustration.

I BELIEVE I can achieve this by digging into the Django auth code and maybe 
some middleware. It seems like that would make sense - I've just not dug 
into the gritty details of the system.

Any tips on creating a 2 stage approach would be much appreciated.

On Wednesday, July 3, 2013 1:34:31 PM UTC-7, Peter Sagerson wrote:
>
> I took part of this conversation offline to spare the list from the gory 
> details, but the upshot was that verifying an OTP is logically a mutating 
> operation on the verifying device, and thus is not really valid on an 
> unsaved model object. I posted an update to the documentation to clarify. 
>
> There were a couple of other questions down there that got lost, but 
> they're more interesting to Django in general. Managing a heterogenous 
> collection of model objects is indeed a little tricky, although it's 
> definitely a solved problem: this is precisely what the admin interface 
> does. Technically, it should be easy to derive an AdminSite that allows a 
> user to manage their own devices, although integrating that cleanly into a 
> broader site design is probably more trouble than it's worth. I suspect 
> that your approach using ContentTypes is a good one--that's pretty much 
> what they're for. 
>
> When I first embarked on this project, I actually thought that device 
> management was one of the primary goals. Initially, writing a plugin 
> required not just a model class, but a collection of forms that could be 
> used to create and manage that device. Eventually I decided that there were 
> several good reasons not to go down this road. To take one example, a 
> TOTPDevice is fairly generic and could be paired with any number of 
> real-world provers. Google Authenticator is the main one I know about, but 
> there are others out there and more will emerge. Each prover may have a 
> unique setup flow with different required TOTP parameters, some of which 
> can't be anticipated in advance. For that reason alone, I think that 
> integrating any kind of management UI directly into the device definition 
> is a dangerous layer violation. 
>
> I'll back up for a minute and point out that--true to the open-source 
> ethic--one reason I haven't published any generic device management code is 
> that I haven't myself needed it. I currently have django-otp deployed in 
> two places. One is only for internal users ("staff" in Django parlance), so 
> it just uses the management interface. The other is targeted at competent 
> but not necessarily technical users, so it's fairly directed. For example, 
> I require that these users register and confirm an SMS device before I 
> issue backup codes and a Google Authenticator key. This all uses 
> device-specific UI, so no need to be generic. 
>
> This gets at the larger issue, which is designing policies for two-factor 
> authentication. The policy that you're proposing is essentially to support 
> as many kinds of OTP devices as you can and let users manage their own 
> security. I think that model has a lot of merits and I'd be happy to see 
> more sites adopt it where appropriate. Of course, the downside to being too 
> casual about two-factor policies is account recovery. I recently read an 
> article in which Google cited this as their Achilles heel in their push for 
> two-factor everywhere. Hence the not-uncommon policy of requiring an SMS 
> backup before allowing more volatile options. 
>
> What this all comes down to is that I think there's plenty of room for 
> code that facilitates different kinds of two-factor authentication policies 
> in Django apps. django-otp itself shouldn't include any of them, of course, 
> because it's just the framework that provides the underlying mechanisms. I 
> can easily imagine a django-otp-authenticator app which has tools for 
> generating Google-Authenticator-compatible TOTPDevice objects, rendering QR 
> codes, confirming, etc. Or, at a higher level, django-otp-devices, with 
> views that allow a user to manage their own heterogenous collection of 
> devices. The problem space is quite large and I'm not sure how it all 
> shakes out in the end. 
>
> On the decorator question, it occurred to me that I actually use the 
> otp-required-if-configured pattern as well, although I had it implemented 
> directly in a view hierarchy. There seems to be a bit of incoherence in 
> recent Django versions between the decorated-view pattern and the 
> view-as-class pattern. In any case, I expanded the otp_required decorator 
> to take an if_configured argument. New versions of django-otp and 
> django-otp-agents are available. 
>
> Thanks so much for your feedback. Do keep in touch either here or 
> privately to let me know how it works out. 
>
> Peter 
>
>
> On Jul 2, 2013, at 4:42 PM, Jason Arnst-Goodrich 
> <good...@gmail.com<javascript:>> 
> wrote: 
>
> > I think I found one more nicety to add. 
> > 
> > The following is the verify_token method for TOTPDevice: 
> > 
> >     def verify_token(self, token): 
> >         OTP_TOTP_SYNC = getattr(settings, 'OTP_TOTP_SYNC', True) 
> > 
> >         try: 
> >             token = int(token) 
> >         except StandardError: 
> >             verified = False 
> >         else: 
> >             key = self.bin_key 
> > 
> >             for offset in range(-self.tolerance, self.tolerance + 1): 
> >                 if totp(key, self.step, self.t0, self.digits, self.drift 
> + offset) == token: 
> >                     if (offset != 0) and OTP_TOTP_SYNC: 
> >                         self.drift += offset 
> >                         self.save() 
> > 
> >                     verified = True 
> >                     break 
> >             else: 
> >                 verified = False 
> > 
> >         return verified 
> > 
> > It some cases it might be nice to verify a token to a device that 
> doesn't actually exist (particularly for validating when users setup 2 
> factor auth themselves). The easiest way to do this right now is to 
> instantiate a Device and invoke verify_token but if the time is offset 
> it'll want to call save() (which then throws an exception). 
> > 
> > So maybe only call save if it already exists? Or allow some parameter 
> controlling the save on the method? Or maybe provide a separate class 
> method? 
> > 
> > 
> > On Monday, July 1, 2013 10:14:35 PM UTC-7, Jason Arnst-Goodrich wrote: 
> > I'm glad you saw my message - if nothing else just so you know this 
> project is appreciated. 
> > 
> > I've got it working with Google's Authenticator. 
> > 
> > I had initially planned to use another project out there for my OTP 
> needs (there's a small number of them that work 'out of the box') because 
> yours took a little extra effort to hook up. 
> > 
> > I ended up going back and using yours though because it's truly in 
> another class. I have the basics working right now. 
> > 
> > I have a couple of questions - I'm trying to make a self service system 
> for allowing users to enable two factor authentication. 
> > 
> > If I loop django_otp.devices_for_user to allow them to manage their 
> existing devices, It's hard to link to a details page for each device. It 
> might help to have a get_absolute_url() defined on the model (which can be 
> overridden is settings). Right now I'm piping it to a template filter using 
> ContentTypes. TBH, I'm pretty new to dealing with this pattern so I might 
> be thinking about it wrong. 
> > 
> > Lastly, what I'll probably end up doing is building a decorator that 
> basically says "require two factor auth if they have it turned on". 
> > 
> > I guess if I had a wishlist it would be to see a baseline for allowing 
> uses to manage their own OTP devices as well as that decorator built in. I 
> understand it's probably out of the scope of what you have right now. I'd 
> still like like to see something that ties it up nicely. That's basically 
> what I'm building right now except I don't trust my build to work in anyone 
> else's setup - although if I have time I'll see if I can go back and 
> refactor it. 
> > 
> > Anyways, thanks again for the work you've done - it's outstanding. 
> > 
> > On Monday, July 1, 2013 9:26:06 PM UTC-7, Peter Sagerson wrote: 
> > Thanks, I'm glad you like it. I can look into some kind of demo, 
> although Authenticator support is pretty simple. The documentation already 
> links to Google's URI scheme[1], which has all of the details. All you have 
> to do is create a TOTP or HOTP device (usually the former), encode the key 
> with base32, build a URI as documented, and render a QR code for the user 
> to scan. Alternatively, the user can also type the base32-encoded key in 
> manually. 
> > 
> > 
> > [1] http://code.google.com/p/google-authenticator/wiki/KeyUriFormat 
> > [2] https://pypi.python.org/pypi/qrcode 
> > 
> > 
> > On Jun 28, 2013, at 10:23 AM, Jason Arnst-Goodrich <good...@gmail.com> 
> wrote: 
> > 
> > > I just stumbled on this and it looks absolutely amazing. I do have one 
> request though: can we get a sample project up that uses Google's 
> authenticator (or anything else). 
> > > 
> > > This looks like the best solution for two factor authentication for 
> Django but I don't think many people will know where to start when it comes 
> to using it (myself included). 
> > > 
> > > On Wednesday, September 12, 2012 1:27:26 PM UTC-7, Peter Sagerson 
> wrote: 
> > > I recently released a suite of packages to support two-factor 
> authentication in Django by way of one-time passwords. 
> > > 
> > > The core package is django-otp, which defines the framework and 
> provides all of the shared APIs. Integration is possible at several levels, 
> from low-level APIs (devices_for_user(), match_token(), etc.); to an 
> AuthenticationForm subclass; to a replacement for Django's login view and 
> an OTP-enabled admin site. Other niceties include the otp_required 
> decorator, an analog to login_required. This is not an authentication 
> backend: although it depends on django.contrib.auth for modeling purposes, 
> it operates independently of the normal authentication machinery. 
> > > 
> > > A given user may have zero or more OTP devices against which we can 
> verify a one-time password. The core project includes Django apps that 
> implement common devices such as HOTP and TOTP (compatible with Google 
> Authenticator, among others) and static passwords (typically used as backup 
> codes). The former include standard features such as tolerance and drift. 
> Separately, django-otp-yubikey provides support for YubiKey devices 
> (locally or remotely verified). django-otp-twilio provides support for 
> Twilio's SMS service for delivering codes by SMS. Implementing support for 
> additional mechanisms is as simple as subclassing an abstract model class 
> and implementing a verification method (and optionally a challenge method). 
> Raw implementations of HOTP and TOTP are provided for convenience along 
> with a few other generally useful utility functions. 
> > > 
> > > As a companion to these, I've also released django-agent-trust, which 
> uses Django 1.4's signed key APIs to tag user-agents that the user has 
> identified as trustworthy. In other words, this implements the "This is a 
> private/shared computer" option one often sees on sensitive sites. Features 
> include revocation and expiration (both absolute and by inactivity; 
> globally, per-user, and per-agent). django-otp-agents is a project that 
> glues together django-otp and django-agent-trust to assign trust to 
> user-agents by way of two-factor authentication (one of the most common 
> scenarios, it seems). 
> > > 
> > > Documentation: django-otp, django-otp-yubikey, django-otp-twilio, 
> django-agent-trust, django-otp-agents 
> > > Bitbucket: django-otp, django-agent-trust 
> > > 
> > > As always, the as-is clause in the BSD license isn't kidding. It's 
> early days for these yet and while everything has been carefully documented 
> and unit-tested, not all of the code has had contact with the real world. 
> Feedback is always welcome. The Google group 
> https://groups.google.com/forum/#!forum/django-otp is available for 
> discussion and questions. 
> > > 
> > > Thanks, 
> > > Peter 
> > > 
> > > -- 
> > > You received this message because you are subscribed to a topic in the 
> Google Groups "Django users" group. 
> > > To unsubscribe from this topic, visit 
> https://groups.google.com/d/topic/django-users/b47ONAEWFos/unsubscribe. 
> > > To unsubscribe from this group and all its topics, send an email to 
> django-users...@googlegroups.com. 
> > > To post to this group, send email to django...@googlegroups.com. 
> > > Visit this group at http://groups.google.com/group/django-users. 
> > > For more options, visit https://groups.google.com/groups/opt_out. 
> > >   
> > >   
> > 
> > 
> > -- 
> > You received this message because you are subscribed to a topic in the 
> Google Groups "Django users" group. 
> > To unsubscribe from this topic, visit 
> https://groups.google.com/d/topic/django-users/b47ONAEWFos/unsubscribe. 
> > To unsubscribe from this group and all its topics, send an email to 
> django-users...@googlegroups.com <javascript:>. 
> > To post to this group, send email to 
> > django...@googlegroups.com<javascript:>. 
>
> > Visit this group at http://groups.google.com/group/django-users. 
> > For more options, visit https://groups.google.com/groups/opt_out. 
> >   
> >   
>
>

-- 
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-users+unsubscr...@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
Visit this group at http://groups.google.com/group/django-users.
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to