The ungleich OTP service
Find a file
2018-11-18 13:42:16 +01:00
requests In between commit w/ serializer error 2018-11-17 17:46:16 +01:00
ungleichotp Cleanup dev code 2018-11-18 13:42:16 +01:00
.gitignore Update .gitignore and README 2018-10-26 22:00:47 +02:00
nameko1.py OTP get seed && verify token 2018-10-26 18:30:15 +02:00
README.md Cleanup dev code 2018-11-18 13:42:16 +01:00
requirements.txt Begin integration of django rest framework 2018-11-17 01:11:17 +01:00

ungleich-otp

ungleich-otp is a full blown authentication and authorisation service made for micro services.

The basic idea is that every micro service has a (long term) seed and creates time based tokens (See python pyotp, RFC4226, RFC6238).

Setup instructions

This is a standard django project and thus can be easily setup using

pip install -r requirements.txt

To bootstrap the application, you need your very first trusted seed to access the application. You can generate it using

to be filled in

After that, you can run the application using

python manage.py runserver

The usual instructions on how to setup an https proxy should be followed.

Realms

Access is granting/denied based on realms. There are two reserved realms, all other realms can be used by the users:

Reserved realms

Conceptually the realms "ungleich-admin" and "ungleich-auth" are reserved for higher priviliged applications.

Usually there is only 1 entry in ungleich-admin that is used to bootstrap and manage ungleich-otp.

All micro services that are trusted to authenticate another micro service should have an entry in the ungleich-auth realm, which allows them to verify a token of somebody else.

| Name | Capabilities | |------------------+--------------------------------------------| | ungleich-admin | authenticate, create, delete, list, update | | ungleich-auth | authenticate | | all other realms | NO ACCESS |

Usage: REST

  • Use an existing token to connect to the service
  • All REST based messages: JSON

POST: /ungleichotp/verify

Request JSON object:

{
    version: "1",
    name: "your-name",
    realm: "your-realm",
    token: "current time based token",
    verifyname: "name that wants to be authenticated",
    verifyrealm: "realm that wants to be authenticated",
    verifytoken: "token that wants to be authenticated",
}

Response JSON object:

Either HTTP 200 with

{
    status: "OK",
}

OR return code 403:

  • If token for authenticating is wrong, you get
{"detail":"Incorrect authentication credentials."}
  • If token that is being verified is wrong, you get
{"detail":"You do not have permission to perform this action."}

GET, POST, ... /ungleichotp/

Standard django rest framework behaviour for updating / listing objects.

Usage: OTP

The seeds that you receive can be used for TOTP to authenticate your apps.

Database

The database saves a list of appuuids with their seeds and the user assignments as well as whether the appuuid might use the BUS interface.

Fields:

  • appuuid (a random UUID)
  • appname (name chosen by the user)
  • username (who this appuuid belongs to)
  • seed (a random base32 string)
  • trusted (boolean, whether app is allowed to use the BUS and the verify method)

Environment / Configuration

  • POSTGRES_USERNAME
  • SECRET_KEY -- random

Random notes / stuff

django.db.backends.postgresql django.contrib.admin

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'mydatabase',
        'USER': 'mydatabaseuser',
        'PASSWORD': 'mypassword',
        'HOST': '127.0.0.1',
        'PORT': '5432',
    }
}

Custom auth

from django.contrib.auth.models import User
from rest_framework import authentication
from rest_framework import exceptions

class ExampleAuthentication(authentication.BaseAuthentication):
    def authenticate(self, request):
        username = request.META.get('X_USERNAME')
        if not username:
            return None

        try:
            user = User.objects.get(username=username)
        except User.DoesNotExist:
            raise exceptions.AuthenticationFailed('No such user')

        return (user, None)

Custom user

Dont forget to point AUTH_USER_MODEL to it. Do this before creating any migrations or running manage.py migrate for the first time.

TODOs

  • serialize / input request
  • Make seed read only
  • Implement registering of new entries
  • OTPSerializer: allow to read seed for admin
  • Implement deleting entry
  • Include verify in ModelSerializer
  • Maybe we map name+realm == User (?)
    • name == name@realm
    • password is used for admin login (?)
    • seed
    • custom auth method
  • [n] try to fake username for django based on name+realm (?)
  • [n] maybe overwrite get_username() (?)
  • Use Custom authentication - needs to have a user!
  • Implement creating new "User"
    • by POST / Model based
  • Add tests for verify
  • Add tests for authentication
  • Add proper documentation
  • move totp constants into settings
  • move field lengths into settings
  • make settings adjustable by environment (?)
  • Remove hard coded JSON (?)

To document

  • Login via username password interactively
  • Login via name/realm/token rest

Changelog

0.6, 2018-11-18

  • Reuse TokenSerializer for VerifySerializer logic

0.5, 2018-11-18

  • Require authentication on all rest endpoints by token