Friday, August 9, 2013

Re: ANN: django-otp and friends: one-time passwords and trusted agents

Hi Jason,

The two-step verification flow should be a cinch. Try something like this:

@login_required
@otp_required(if_configured=True)
def protected_view(request):
    ...

I'm not sure I followed the question about the password field, although there is some deliberate behavior here (see django_otp/forms.py#119). By default (as of Django 1.3), password fields are not populated when a form is re-rendered after failing validation. In the case of an OTP-enabled form, that's not necessarily what we want. If the user logged in successfully but failed OTP verification (or didn't provide a token), we want to acknowledge that the password was correct by leaving it in the field. If you're not seeing reasonable behavior in some scenario, let me know. Of course, if you're only using the two-step flow above, this will be moot.

Thanks,
Peter


On Friday, August 9, 2013 11:54:01 AM UTC-7, Jason Arnst-Goodrich wrote:
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> 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.
> 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 the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@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.
 
 

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home


Real Estate