# 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 Don’t forget to point AUTH_USER_MODEL to it. Do this before creating any migrations or running manage.py migrate for the first time. ## TODOs - [x] serialize / input request - [x] Make seed read only - [x] Implement registering of new entries - [x] OTPSerializer: allow to read seed for admin - [x] Implement deleting entry - [x] Include verify in ModelSerializer - [x] 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() (?) - [x] Use Custom authentication - needs to have a user! - [x] Implement creating new "User" - by POST / Model based - [x] - [ ] 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