Skip to main content

django-thumbmark

 License python versions PyPI version

Django app to transparently identify users with the ThumbmarkJS library.

This app includes a decorator that mimics the built-in login_required decorator provided by Django. Views using this decorator will have unidentified users transparently redirected through a page where the ThumbmarkJS library is used to generate a unique identifier for the client. This unique identifier is then mapped to a Django user and associated with the HttpRequest object.

Common use cases include public forms that should be limited to one submission per user, without requring a username/password login.

  • Survey Submissions
  • Feedback Forms
  • Anonymous Polls

Usage

Add the included decorator to your function- or class-based view:

from django.views.generic import TemplateView
from django.utils.decorators import method_decorator

from django_thumbmark.decorators import login_required_thumbmark

@login_required_thumbmark
def my_function_based_view(request):
    # ...
    print(request.user) # <-- The User attribute will be populated

### --- or --- ###

@method_decorator(login_required_thumbmark, name='dispatch')
class MyClassBasedView(TemplateView):
    # ...
    def get(self, request):
        print(request.user) # <-- The User attribute will be populated

Standard Installation

Install from PyPi:

python -m pip install django-thumbmark

Add django_thumbmark to your INSTALLED_APPS:

INSTALLED_APPS = [
    # ...
    'django_thumbmark',
]

To use the app as written (without customization), include its URLs in your project's urls.py file, the same way you would for any other reusable Django app:

# project/urls.py
from django.urls import include, path

urlpatterns = [
    # ...
    path("thumbmark/", include("django_thumbmark.urls")),
]

# project/views.py
from django.http import HttpResponse
from django.utils.decorators import method_decorator
from django.views import View

from django_thumbmark.decorators import login_required_thumbmark

@login_required_thumbmark
def index(request):
    # Do the thing with your now logged-in user
    return HttpResponse(f"Hello, {request.user}")

### --- or, for a class-based view --- ###

@method_decorator(login_required_thumbmark, name="dispatch")
class IndexView(View):
    def get(self, request):
        # Do the thing with your now logged-in user
        return HttpResponse(f"Hello, {request.user}")

The path prefix ("thumbmark/" above) is up to you and doesn't need to match anything; login_required_thumbmark finds the tm/tmlogin views by namespace (see Custom Namespace below), not by path, so it works no matter which app or namespace your protected views live in.

Installation with Customizations

To use the app with customization, overwrite the included DjTmScriptView as needed, then include both it and the stock DjTmLoginView from your own urls.py module. That module needs app_name = "django_thumbmark" (or a matching THUMBMARK_NAMESPACE) so login_required_thumbmark can still find it, and the URL names must still be tm and tmlogin.

# myapp/views.py
from django_thumbmark.views import DjTmScriptView
from django.utils import timezone # ... as an example of customized usage

    # ...
    class MyDjTmScriptView(DjTmScriptView):
        def get_first_name(self, request, *args, **kwargs):
            return "CustomFirstName"

        def get_username(self, request, *args, **kwargs):
            tmid = kwargs["tmid"]
            path = request.path
            time = timezone.now().strftime("%Y-%m-%dT%H:%M:%S")
            return f"{tmid}-{path}-{time}"


# myapp/urls.py
from django.urls import path

from myapp import views
from django_thumbmark.views import DjTmLoginView

app_name = "django_thumbmark"  # must match THUMBMARK_NAMESPACE (default shown here)

urlpatterns = [
    path("tm/", views.MyDjTmScriptView.as_view(), name="tm"), # The URL name is "tm"
    path("login/", DjTmLoginView.as_view(), name="tmlogin"), # The URL name is "tmlogin"
]


# project/urls.py
from django.urls import include, path

urlpatterns = [
    # ...
    path("thumbmark/", include("myapp.urls")),
]

# project/views.py
from django.http import HttpResponse
from django.utils.decorators import method_decorator
from django.views import View

from django_thumbmark.decorators import login_required_thumbmark

@login_required_thumbmark
def index(request):
    # Do the thing with your now logged-in user
    return HttpResponse(f"Hello, {request.user}")

### --- or, for a class-based view --- ###

@method_decorator(login_required_thumbmark, name="dispatch")
class IndexView(View):
    def get(self, request):
        # Do the thing with your now logged-in user
        return HttpResponse(f"Hello, {request.user}")

To modify the included HTML template, overwrite the django_thumbmark/login.html template within your app's templates directory.

├── manage.py
├── project_root
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── requirements.txt
└── myapp
    ├── __init__.py
    ├── admin.py
    ├── apps.py
    ├── forms.py
    ├── migrations
    │   ├── 0001_initial.py
    ├── models.py
    ├── templates
    │   ├── django_thumbmark ## Create this directory
    │   │   └── login.html   ## Add this file
    │   └── myapp
    │       ├── index.html
    │       └── success.html
    ├── tests.py
    ├── urls.py
    └── views.py

The options with which this overwritten template file can be modified are shown in the Customization section below.

Details

When a view uses this decorator

  1. If the user is already authenticated, nothing happens.
  2. If the user hasn't been authenticated they are redirected to a new login page named tmlogin. This login page contains the ThumbmarkJS script to generate a unique ID.
  3. The unique ID is sent by Javascript via a POST request (with a CSRF token) to a second view named tm.
  4. The second view uses the unique ID to search for an existing User object. A new User object is created if no object is found.
  5. The user is logged in and associated with the request.
  6. The user is redirected back to their original page.

Customization

New User First/Last Names

By default, if a new User object is created, the first and last names are set to "Test" and "User," respectively. These can be modified by overwriting the DjTmScriptView.get_first_name() and DjTmScriptView.get_last_name() functions.

New User Username

By default, if a new User object is created, the username is set to the unique ID generated by ThumbmarkJS. This can be modified by overwriting the DjTmScriptView.get_username() function.

New User Object

By default, Django's get_or_create() function is used to find/create a User object directly, which is then logged in and associated with the request. To modify how that user is discovered and/or created, you can overwrite the DjTmScriptView.get_user_object_() function.

Custom Namespace

login_required_thumbmark and DjTmLoginView locate the tm/tmlogin views by URL namespace, not by path — this is what lets protected views live anywhere in your project regardless of which app or namespace they're in. By default that namespace is django_thumbmark, matching the app_name set in django_thumbmark/urls.py. If you include() those URLs under a different namespace (or provide your own urls.py with a different app_name, per the customization example above), set:

# settings.py
THUMBMARK_NAMESPACE = "my-custom-namespace"

JavaScript Disabled Output

By default, if the browser doesn't have JavaScript enabled, a static text string will be shown to the user instructing them to enable Javascript to use this site. This text can be changed by overwriting the django_thumbprint/login.html template and modifying this block:

{% block no-js-message %}Please, pretty please, won't you enable JavaScript?{% endblock %}

ThumbmarkJS Source

By default the ThumbmarkJS library is loaded from cdn.jsdelivr.net, pinned to a specific released version. This can be changed (to reference a static, self-hosted copy, or a newer/older pinned version) by overwriting the django_thumbprint/login.html template and modifying this block:

{% block js-source %}<script src="https://cdn.jsdelivr.net/npm/@thumbmarkjs/thumbmarkjs@1.10.0/dist/thumbmark.umd.js"></script>{% endblock %}

ThumbmarkJS Script

By default the ThumbmarkJS script is run using the new ThumbmarkJS.Thumbmark().get() pattern documented in that project's README. The contents of that script can be changed by overwriting the django_thumbprint/login.html template and modifying this block:

{% block js-script %}<script>console.log('This is my new script')</script>{% endblock %}

Versioning

This package uses Semantic Versioning. TL;DR you are safe to use compatible release version specifier ~=MAJOR.MINOR in your pyproject.toml or requirements.txt.

Release process

python -m pip install build
python -m pip install twine
python -m build
python -m twine upload dist/*

Metadata

Release files for django-thumbmark 0.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for django-thumbmark 0.3
File Size Uploaded Provenance Actions
django_thumbmark-0.3.tar.gz 12.2 kB Without provenance Details for django_thumbmark-0.3.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for django-thumbmark 0.3
File Interpreter ABI Platform Actions
django_thumbmark-0.3-py3-none-any.whl Python 3 none any Details for django_thumbmark-0.3-py3-none-any.whl

Total release size: 21.7 kB

Release files / django_thumbmark-0.3.tar.gz

Download URL django_thumbmark-0.3.tar.gz
Size 12.2 kB
Tags Source
SHA-256 checksum
How to use checksums
f6fc6e65148b78df4cc137a4101c8782daf1342aa7c06105bbf7d45111552c34
BLAKE2b-256 checksum
How to use checksums
477f8c2a125a30dc3c8a0e46ebb5ca251437b097a1fd63347e8fb0d234871f3b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.12

Release files / django_thumbmark-0.3-py3-none-any.whl

Download URL django_thumbmark-0.3-py3-none-any.whl
Size 9.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8634657ca0705cfe8c3eb58dc27c917b1f614a6476e5a336387315c75c506568
BLAKE2b-256 checksum
How to use checksums
372e56ad6bf78b8fcf6475f7797b64705e8e6411e6264a80f36210587fa498e3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.12

Security for django-thumbmark 0.3

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.3 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page