5.2 KiB
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)
TODOs
- serialize / input request
- Remove hard coded JSON
- Implement registering of new entries
- Use Custom authentication (?) - set User
- Maybe we map name+realm == User (?)