The ungleich OTP service
Find a file
2018-11-17 22:15:17 +01:00
requests In between commit w/ serializer error 2018-11-17 17:46:16 +01:00
ungleichotp Cleanup, expose seed read only 2018-11-17 22:15:17 +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, expose seed read only 2018-11-17 22:15:17 +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

{
    status: "OK",
}

OR

{
    status: "FAIL",
}

POST /register

Register a new seed. Returns an app ID.

Request JSON object:

{
    version: "1",
    appuuid: "your-app-uuid",
    token: "current time based token",
    username: "user this app belongs to",
    appname: "name of your web app"
}

Response JSON object:

{
    status: "OK",
    appuuid: "UUID of your app",
}

OR

{
    status: "FAIL",
    error: "Reason for failure"
}

POST /app/register

Register a new app. Returns an app ID.

Request JSON object:

{
    version: "1",
    appuuid: "your-app-uuid",
    token: "current time based token",
    username: "user this app belongs to",
    appname: "name of your web app"
}

Response JSON object:

{
    status: "OK",
    appuuid: "UUID of your app",
}

OR

{
    status: "FAIL",
    error: "Reason for failure"
}

GET /app

List all registered apps for the current user.

Request JSON object:

{
    version: "1",
    appuuid: "your-app-uuid",
    token: "current time based token"
}

Response JSON object:

{
    status: "OK",
    apps: [
        {
            name: "name of your web app"
            appuuid: "UUID of your app",
        },
        {
            name: "name of your second web app"
            appuuid: "UUID of your second app",
        }
    ]
}

GET /app/UUID

Get seed for APP to be used as a token

Request JSON object:

{
    version: "1",
    appuuid: "your-app-uuid",
    token: "current time based token"
}

Response JSON object:

{
    status: "OK",
    seed: "seed of your app"
}

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
  • Remove hard coded JSON
  • Implement registering of new entries
  • Use Custom authentication (?) - needs to have a user
  • Maybe we map name+realm == User (?)
    • name == name@realm
    • no password
    • seed
    • custom auth method
  • Implement creating new "User"
    • by POST / Model based
  • Implement deleting "User"
  • OTPSerializer: allow to read seed for admin