Create custom user subclass OAuth2TokenUser
As raised in a comment on e978edd6, we have extra information we wish to associate with a user which could be expressed via a custom user model.
We need to make sure the user model can accommodate "plain ol' users" which can log into, e.g., the admin but also users created implicitly via API authentications. Something like:
from some.module import OAuth2TokenUser
# can create user as before
u1 = OAuth2TokenUser.objects.get_or_create_user(username="some_admin")
assert u1.scheme is None
assert u1.identifier is None
assert u1.username == "some_admin"
# can also create from token dictionary
token = {
"sub": "mock:test0001",
# ...
}
u2 = OAuth2TokenUser.objects.get_or_create_from_token(token)
# subject is a unique field
assert u2.subject == "mock:test0001"
assert u2.username is not None
# (scheme, identifier) as a pair are a unique index in the DB.
assert u2.scheme == "mock"
assert u2.identifier == "test0001"
Ideally the username should be some unique value which is derived from the subject, perhaps something like:
import json
from hashlib import sha256
def username_from_scheme_and_identifier(scheme, identifier):
h = sha256(json.dumps({"scheme": scheme, "identifier": identifier}))
return "{}-{}-{}".format(scheme, identifier, h.hexdigest()[:8])
def get_or_create_oauth2_token_user(token):
u = OAuth2TokenUser.objects.filter(subject=token['sub']).first()
if u is not None:
return u
scheme, identifier = token['sub'].split(":")
# this method *must* call create_user or, otherwise, ensure that set_unusable_password()
# is called on the object
return OAuth2TokenUser.objects.create_oauth2_token_user(
username=username_from_scheme_and_identifier(scheme, identifier),
subject=token['sub'], scheme=scheme, identifier=itentifier
)