File: overview.rst

package info (click to toggle)
python-django-otp 1.6.1-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 756 kB
  • sloc: python: 3,221; makefile: 145; sh: 6
file content (602 lines) | stat: -rw-r--r-- 20,088 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
Overview and Key Concepts
=========================

The django_otp package contains a framework for processing one-time passwords as
well as support for :ref:`several types <built-in-plugins>` of OTP devices.
Support for additional devices is handled by plugins, :ref:`distributed
separately <other-plugins>`.

Adding two-factor authentication to your Django site involves four main tasks:

    #. Installing the django-otp plugins you want to use.

    #. Adding one or more OTP-enabled login views.

    #. Restricting access to all or portions of your site based on whether users
       have been verified by a registered OTP device.

    #. Providing mechanisms to register OTP devices to user accounts (or
       relying on the Django admin interface).


.. _installation:

Installation
------------

Basic installation has only two steps:

    #. Install :mod:`django_otp` and any :ref:`plugins <built-in-plugins>` that
       you'd like to use. These are simply Django apps to be installed in the
       usual way.

    #. Add :class:`django_otp.middleware.OTPMiddleware` to
       :setting:`MIDDLEWARE`. It must be installed *after*
       :class:`~django.contrib.auth.middleware.AuthenticationMiddleware`.

For example::

    INSTALLED_APPS = [
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.sites',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'django.contrib.admin',
        'django.contrib.admindocs',

        'django_otp',
        'django_otp.plugins.otp_totp',
        'django_otp.plugins.otp_hotp',
        'django_otp.plugins.otp_static',
    ]

    MIDDLEWARE = [
        'django.middleware.common.CommonMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django_otp.middleware.OTPMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
    ]

The plugins contain models that must be migrated.


.. _upgrading:

Upgrading
---------

Version 0.2.4 of django-otp introduced a South migration to the otp_totp plugin.
Version 0.3.0 added Django 1.7 and South migrations to all apps. Care must be
taken when upgrading in certain cases.

The recommended procedure is:

    1. Upgrade django-otp to 0.2.7, as described below.
    2. Upgrade Django to 1.7 or later.
    3. Upgrade django-otp to the latest version.

django-otp 0.4 dropped support for Django < 1.7.


Upgrading from 0.2.3 or Earlier
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If you're using django-otp <= 0.2.3, you need to convert otp_totp to South
before going any further::

    pip install 'django-otp==0.2.7'
    python manage.py migrate otp_totp 0001 --fake
    python manage.py migrate otp_totp

If you're not using South, you can run ``python manage.py sql otp_totp`` to see
the definition of the new ``last_t`` field and then construct a suitable ``ALTER
TABLE`` SQL statement for your database.


Upgrading to Django 1.7+
~~~~~~~~~~~~~~~~~~~~~~~~

Once you've upgraded django-otp to version 0.2.4 or later (up to 0.2.7), it's
safe to switch to Django 1.7 or later. You should not have South installed at
this point, so any old migrations will simply be ignored.

Once on Django 1.7+, it's safe to upgrade django-otp to 0.3 or later. All
plugins with models have Django migrations, which will be ignored if the tables
have already been created.

If you're already on django-otp 0.3 or later when you move to Django 1.7+ (see
below), you'll want to make sure Django knows that all migrations have already
been run::

    python manage.py migrate --fake <otp_plugin>
    ...


Upgrading to 0.3.x with South
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

If you want to upgrade django-otp to 0.3.x under South, you'll need to convert
all of the remaining plugins. First make sure you're running South 1.0, as
earlier versions will not find the migrations. Then convert any plugin that you
have installed::

    pip install 'django-otp>=0.3'
    python manage.py migrate otp_hotp 0001 --fake
    python manage.py migrate otp_static 0001 --fake
    python manage.py migrate otp_yubikey 0001 --fake
    python manage.py migrate otp_twilio 0001 --fake


Authentication and Verification
-------------------------------

In a normal Django deployment, the user associated with a request is either
authenticated or not. With the introduction of two-factor authentication, the
situation becomes a little more complicated: while it is certainly possible to
design a site such that two factors are required for any authentication, that's
only one option. It's entirely reasonable to allow users to log in with either
one or two factors and grant then access accordingly.

In this documentation, a user that has passed Django's authentication API is
called :term:`authenticated`. A user that has additionally been accepted by a
registered OTP device is called :term:`verified`. On an OTP-enabled Django site,
there are thus three levels of authentication:

    - anonymous
    - authenticated
    - authenticated + verified

When planning your site, you'll want to consider whether different views will
require different levels of authentication. As a convenience, we provide the
decorator :func:`django_otp.decorators.otp_required`, which is analogous to
:func:`~django.contrib.auth.decorators.login_required`, but requires the user to
be both authenticated and verified.

:class:`~django_otp.middleware.OTPMiddleware` populates
``request.user.otp_device`` to the OTP device object that verified the current
user (if any). As a convenience, it also adds ``user.is_verified()`` as a
counterpart to ``user.is_authenticated()``. It is not possible for a user to be
verified without also being authenticated. [#agents]_


Plugins and Devices
-------------------

A django-otp plugin is simply a Django app that contains one or more models that
are subclassed from :class:`django_otp.models.Device`. Each model class supports
a single type of OTP device. Remember that when we use the term :term:`device`
in this context, we're not necessarily referring to a physical device. At the
code level, a device is a model object that can verify a particular type of OTP.
For example, you might have a `YubiKey`_ that supports both the Yubico OTP
algorithm and the HOTP standard: these would be represented as different devices
and likely served by different plugins. A device that delivered HOTP values to a
user by SMS would be a third device defined by another plugin.

OTP plugins are distributed as Django apps; to install a plugin, just add it to
:setting:`INSTALLED_APPS` like any other. The order can be significant: any time
we enumerate a user's devices, such as when we ask the user which device they
would like to authenticate with, we will present them according to the order in
which the apps are installed.

OTP devices come in two general flavors: passive and interactive. A passive
device is one that can accept a token from the user and verify it with no
preparation. Examples include devices corresponding to dedicated hardware or
smartphone apps that generate sequenced or time-based tokens. An interactive
device needs to communicate something to the user before it can accept a token.
Two common types are devices that use a challenge-response OTP algorithm and
devices that deliver a token to the user through an independent channel, such as
SMS.

Internally, device instances can be flagged as confirmed or unconfirmed. By
default, devices are confirmed as soon as they are created, but a plugin or
deployment that wishes to include a confirmation step can mark a device
unconfirmed initially. Unconfirmed devices will be ignored by the high-level OTP
APIs.


.. _YubiKey: http://www.yubico.com/yubikey


.. _built-in-plugins:

Built-in Plugins
~~~~~~~~~~~~~~~~

django-otp includes support for several standard device types.
:class:`~django_otp.plugins.otp_hotp.models.HOTPDevice` and
:class:`~django_otp.plugins.otp_totp.models.TOTPDevice` handle standard OTP
algorithms, which can be used with a variety of OTP generators. For example,
it's easy to pair these devices with `Google Authenticator`_ using the `otpauth
URL scheme`_. If you have either the `segno`_ or `qrcode`_ packages installed,
the admin interface will generate QR Codes for you.


.. _Google Authenticator: https://github.com/google/google-authenticator
.. _otpauth URL scheme: https://github.com/google/google-authenticator/wiki/Key-Uri-Format
.. _segno: https://pypi.python.org/pypi/segno/
.. _qrcode: https://pypi.python.org/pypi/qrcode/


.. _hotp-devices:

HOTP Devices
++++++++++++

`HOTP`_ is an algorithm that generates a pseudo-random sequence of codes based
on an incrementing counter. Every time a prover generates a new code or a
verifier verifies one, they increment their respective counters. This algorithm
will fail if the prover generates too many codes without a successful
verification.

If there is a failed attempt, this plugin will enforce an exponentially
increasing delay before allowing verification to succeed (see
:setting:`OTP_HOTP_THROTTLE_FACTOR`). The
:meth:`~django_otp.models.Device.verify_token` method automatically applies this
policy. For a better user experience, before calling
:meth:`~django_otp.models.Device.verify_token` check whether verification is
disabled by calling the :meth:`~django_otp.models.Device.verify_is_allowed`
method.

.. module:: django_otp.plugins.otp_hotp

.. automodule:: django_otp.plugins.otp_hotp.models
    :members:

.. autoclass:: django_otp.plugins.otp_hotp.admin.HOTPDeviceAdmin

.. _HOTP: http://tools.ietf.org/html/rfc4226#section-5

HOTP Settings
'''''''''''''

.. setting:: OTP_HOTP_ISSUER

**OTP_HOTP_ISSUER**

Default: ``None``

The ``issuer`` parameter for the otpauth URL generated by
:attr:`~django_otp.plugins.otp_hotp.models.HOTPDevice.config_url`.
This can be a string or a callable to dynamically set the value.


.. setting:: OTP_HOTP_THROTTLE_FACTOR

**OTP_HOTP_THROTTLE_FACTOR**

Default: ``1``

This controls the rate of throttling. The sequence of 1, 2, 4, 8... seconds is
multiplied by this factor to define the delay imposed after 1, 2, 3, 4...
successive failures. Set to ``0`` to disable throttling completely.


.. _totp-devices:

TOTP Devices
++++++++++++

`TOTP`_ is an algorithm that generates a pseudo-random sequence of codes based
on the current time. A typical implementation will change codes every 30
seconds, although this is configurable. This algorithm will fail if the prover
and verifier have clocks that drift too far apart.

If there is a failed attempt, this plugin will enforce an exponentially
increasing delay before allowing verification to succeed (see
:setting:`OTP_TOTP_THROTTLE_FACTOR`). The
:meth:`~django_otp.models.Device.verify_token` method automatically applies this
policy. For a better user experience, before calling
:meth:`~django_otp.models.Device.verify_token` check whether verification is
disabled by calling the :meth:`~django_otp.models.Device.verify_is_allowed`
method.

.. module:: django_otp.plugins.otp_totp

.. automodule:: django_otp.plugins.otp_totp.models
    :members:

.. autoclass:: django_otp.plugins.otp_totp.admin.TOTPDeviceAdmin

.. _TOTP: http://tools.ietf.org/html/rfc6238#section-4

TOTP Settings
'''''''''''''

.. setting:: OTP_TOTP_ISSUER

**OTP_TOTP_ISSUER**

Default: ``None``

The ``issuer`` parameter for the otpauth URL generated by
:attr:`~django_otp.plugins.otp_totp.models.TOTPDevice.config_url`.
This can be a string or a callable to dynamically set the value.

.. setting:: OTP_TOTP_IMAGE

**OTP_TOTP_IMAGE**

Default: ``None``

The ``image`` parameter for the otpauth URL generated by
:attr:`~django_otp.plugins.otp_totp.models.TOTPDevice.config_url`.
It should be a HTTPS URL pointing to a square PNG image.
This will be read and displayed by some authenticator applications, e.g. FreeOTP.
This can be a string or a callable to dynamically set the value.

.. setting:: OTP_TOTP_SYNC

**OTP_TOTP_SYNC**

Default: ``True``

If true, then TOTP devices will keep track of the difference between the
prover's clock and our own. Any time a
:class:`~django_otp.plugins.otp_totp.models.TOTPDevice` matches a token in the
past or future, it will update
:attr:`~django_otp.plugins.otp_totp.models.TOTPDevice.drift` to the number of
time steps that the two sides are out of sync. For subsequent tokens, we'll
slide the window of acceptable tokens by this number.


.. setting:: OTP_TOTP_THROTTLE_FACTOR

**OTP_TOTP_THROTTLE_FACTOR**

Default: ``1``

This controls the rate of throttling. The sequence of 1, 2, 4, 8... seconds is
multiplied by this factor to define the delay imposed after 1, 2, 3, 4...
successive failures. Set to ``0`` to disable throttling completely.


Static Devices
++++++++++++++

.. module:: django_otp.plugins.otp_static

.. automodule:: django_otp.plugins.otp_static.models
    :members: StaticDevice, StaticToken

.. autoclass:: django_otp.plugins.otp_static.admin.StaticDeviceAdmin

Static Settings
'''''''''''''''
.. setting:: OTP_STATIC_THROTTLE_FACTOR

**OTP_STATIC_THROTTLE_FACTOR**

Default: ``1``

This controls the rate of throttling. The sequence of 1, 2, 4, 8… seconds is
multiplied by this factor to define the delay imposed after 1, 2, 3, 4…
successive failures. Set to 0 to disable throttling completely.

.. _addstatictoken:

addstatictoken
''''''''''''''

The static plugin also includes a management command called ``addstatictoken``,
which will add a single static token to any account. This is useful for
bootstrapping and emergency access. Run ``manage.py addstatictoken -h`` for
details.


Email Devices
+++++++++++++

.. module:: django_otp.plugins.otp_email

.. automodule:: django_otp.plugins.otp_email.models
   :members: EmailDevice

.. autoclass:: django_otp.plugins.otp_email.admin.EmailDeviceAdmin

Email Settings
''''''''''''''

.. setting:: OTP_EMAIL_SENDER

**OTP_EMAIL_SENDER**

Default: ``None``

The email address to use as the sender when we deliver tokens. If not set, this
will automatically use :setting:`DEFAULT_FROM_EMAIL`.


.. setting:: OTP_EMAIL_SUBJECT

**OTP_EMAIL_SUBJECT**

Default: ``'OTP token'``

The subject of the email. You probably want to customize this. A ``{token}``
placeholder is available for the generated token.


.. setting:: OTP_EMAIL_BODY_TEMPLATE

**OTP_EMAIL_BODY_TEMPLATE**

Default: ``None``

A raw template string to use for the email body. The render context will
include the generated token in the ``token`` key. Additional template context
may be passed to
:meth:`~django_otp.plugins.otp_email.models.EmailDevice.generate_challenge`.

If this and :setting:`OTP_EMAIL_BODY_TEMPLATE_PATH` are not set, we'll render
the template 'otp/email/token.txt', which you'll most likely want to override.


.. setting:: OTP_EMAIL_BODY_HTML_TEMPLATE

**OTP_EMAIL_BODY_HTML_TEMPLATE**

Default: ``None``

A raw template string to use for the email html alternative body. The render context will
include the generated token in the ``token`` key. Additional template context
may be passed to
:meth:`~django_otp.plugins.otp_email.models.EmailDevice.generate_challenge`.

If this and :setting:`OTP_EMAIL_BODY_HTML_TEMPLATE_PATH` are not set, we won't attach any
html alternative to the email.


.. setting:: OTP_EMAIL_BODY_TEMPLATE_PATH

**OTP_EMAIL_BODY_TEMPLATE_PATH**

Default: ``otp/email/token.txt``

A path string to a template file to use for the email body. The render context
will include the generated token in the ``token`` key. Additional template
context may be passed to
:meth:`~django_otp.plugins.otp_email.models.EmailDevice.generate_challenge`.

If this and :setting:`OTP_EMAIL_BODY_TEMPLATE` are not set, we'll render the
template 'otp/email/token.txt', which you'll most likely want to override.


.. setting:: OTP_EMAIL_BODY_HTML_TEMPLATE_PATH

**OTP_EMAIL_BODY_HTML_TEMPLATE_PATH**

Default: ``None``

A path string to a template file to use for the email html alternative body. The render context
will include the generated token in the ``token`` key. Additional template
context may be passed to
:meth:`~django_otp.plugins.otp_email.models.EmailDevice.generate_challenge`.

If this and :setting:`OTP_EMAIL_BODY_HTML_TEMPLATE` are not set, we won't attach any html
alternative to the email.


.. setting:: OTP_EMAIL_TOKEN_VALIDITY

**OTP_EMAIL_TOKEN_VALIDITY**

Default: ``300``

The maximum number of seconds a token is valid.


.. setting:: OTP_EMAIL_COOLDOWN_DURATION

**OTP_EMAIL_COOLDOWN_DURATION**

Default: ``60``

This controls the cooldown period after a successful token generation. The
token can be regenerated after the designated time period has fully elapsed.
Set to 0 to disable cooldown completely.


.. setting:: OTP_EMAIL_THROTTLE_FACTOR

**OTP_EMAIL_THROTTLE_FACTOR**

Default: ``1``

This controls the rate of throttling. The sequence of 1, 2, 4, 8… seconds is
multiplied by this factor to define the delay imposed after 1, 2, 3, 4…
successive failures. Set to 0 to disable throttling completely.

.. _other-plugins:

Other Plugins
~~~~~~~~~~~~~~

The framework author also maintains a couple of other plugins for less common
devices. Third-party plugins are not listed here.

    - `django-otp-yubikey`_ supports YubiKey USB devices.
    - `django-otp-twilio`_ supports delivering tokens via Twilio's SMS service.


Settings
--------

.. setting:: OTP_LOGIN_URL

**OTP_LOGIN_URL**

Default: alias for :setting:`LOGIN_URL`

The URL where requests are redirected for two-factor authentication, especially
when using the :func:`~django_otp.decorators.otp_required` decorator.


.. setting:: OTP_ADMIN_HIDE_SENSITIVE_DATA

**OTP_ADMIN_HIDE_SENSITIVE_DATA**

Default: `False`

This controls showing some sensitive data on the Django admin site (e.g., keys
and corresponding QR codes, static tokens). Note, it is respected by built-in
plugins, but external ones may or may not support it.


Glossary
--------

.. glossary::

    authenticated
        A user whose credentials have been accepted by Django's authentication
        API is considered authenticated.

    device
        A mechanism by which a user can acquire an OTP. This might correspond to
        a physical device dedicated to such a purpose, a virtual device such as
        a smart phone app, or even a set of stored single-use tokens.

    OTP
        A one-time password. This is a generated value that a user can present
        as evidence of their identity. OTPs are only valid for a single use or,
        in some cases, for a strictly limited period of time.

    prover
        An entity that is using an OTP to prove its identity. For example, a
        user who is providing an OTP token.

    token
        An encoded OTP. Some OTPs consist of structured data, in which case
        they will be encoded into a printable string for transport.

    two-factor authentication
        An authentication policy that requires a user to present two proofs of
        identity. The first is typically a password and the second is
        frequently tied to some physical device in the user's possession.

    verified
        A user whose credentials have been accepted by Django's authentication
        API and also by a registered OTP device is considered verified.

    verifier
        An entity that verifies tokens generated by a prover. For example, a web
        service that accepts OTPs as proof of identity.

----

.. rubric:: Footnotes

.. [#agents] If you'd like the second factor to persist across sessions, see
   `django-agent-trust`_ and `django-otp-agents`_. The former deals with
   assigning trust to user agents (i.e. browsers) across sessions and the latter
   includes tools to use OTPs to establish that trust.


.. _django-agent-trust: http://pypi.python.org/pypi/django-agent-trust
.. _django-otp-agents: http://pypi.python.org/pypi/django-otp-agents
.. _django-otp-yubikey: https://django-otp-yubikey.readthedocs.io
.. _django-otp-twilio: https://django-otp-twilio.readthedocs.io