The ungleich OTP service
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

57 lines
1.9 KiB

from django.db import models
from django.contrib.auth.models import AbstractUser
from rest_framework import exceptions
from rest_framework import authentication
import json
import logging
import pyotp
logger = logging.getLogger(__name__)
class OTPSeed(AbstractUser):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=128)
realm = models.CharField(max_length=128)
seed = models.CharField(max_length=128)
class Meta:
unique_together = (('name', 'realm'),)
def save(self, *args, **kwargs):
inject username to ensure it stays unique / is setup at all
if not self.is_superuser:
self.username = "{}@{}".format(, self.realm)
else: = self.username
self.realm = "ungleich-admin"
self.seed = pyotp.random_base32()
super().save(*args, **kwargs)
def __str__(self):
return "'{}'@{} -- {}".format(, self.realm, self.username)
from otpauth.serializer import TokenSerializer
class OTPAuthentication(authentication.BaseAuthentication):
def authenticate(self, request):
logger.debug("in authenticate {}".format(json.dumps(
serializer = TokenSerializer(
if serializer.is_valid():
instance, token =
logger.error("serializer is invalid")
raise exceptions.AuthenticationFailed()
# not dealing with admin realm -> can only be auth [see serializer]
if not instance.realm == "ungleich-admin":
if not request.path == "/ungleichotp/verify/":
logger.debug("request.path is not /ungleichotp/verify/")
raise exceptions.AuthenticationFailed()
logger.debug("AUTH DONE: {} - {}".format(request.path, instance))
return (instance, token)