Draft: Proof-of-concept token authentication
Very rough-edged proof-of-concept demonstrating (some) of the required components to allow us to authenticate users and supply tokens from django-rest-knox
.
Testing Locally
Create a super user using the Django management command (or create a regular user manually via the database, the admin console is disabled here so will not work) with the username test
. It doesn't matter what you set the password to. Also create some test data in the database:
$ poe manage createsuperuser --username "test" --email "no-email@no.com" --noinput
$ poe manage
Request login:
$ curl -X POST --header "Authorization: Activate test" http://localhost:8000/authentication/login/
{"expiry":"2024-10-03T00:25:53.793661Z","token":"my-cool-token"}
Note the Activate
instead of e.g. Basic
here - this isn't basic auth.
You can then use this token to authenticate to any other endpoint (and this will be required!):
$ curl http://localhost:8000/v1alpha1/crsid-info/crsid1/
{"detail":"Authentication credentials were not provided."}
$ curl --header "Authorization: Token my-cool-token" http://localhost:8000/v1alpha1/crsid-info/crsid1/
{"isMember:true}
Tour of the Changes
This proof-of-concept does a number of things (and omits a few things that are important, but not for proof-of-concept). The first key thing is some settings removal, along with the removal of some cruft code that will need to be removed (and I removed here because it was simple).
One thing to note, the gateway emulator is gone! It's not needed for this project, and interferes with the login process. We should make sure we chop this when we do the actual changes to implement this.
activate_account_project/settings/base.py
- Removes authentication that we don't need (although possibly not exhaustive, don't take the changes in this PoC as definitive).
- Adds
knox
and required settings. - Adds
authentication
as a Django app. - Sets custom backend for authentication.
- Sets default permission for endpoints to
IsAuthenticated
.
Known Required Changes for production:
- Final checks that nothing is forgotten in here.
- Validate all
knox
settings are correct.
authentication/authentication.py
Responsibility: Retrieve credential information from a request, and pass to the authentication backend.
- This provides a HTTP-basic like authentication class, which reads the
Authorization
header for a request, expecting the header to have the content:Activate <username>
. - Does not expect to find a password!
- It then calls out to the configured backend using Django's
django.contrib.auth.authenticate
method. - This is used by the login view to authenticate users for initial login, allowing them to create a token.
Known Required Changes for production:
- Decide on how a
request
is processed (e.g.Authorization
header format, or something else) to extract credential set.- e.g. Do we expect something similar to
Basic
with b64 encoded information separated by:
?
- e.g. Do we expect something similar to
- Update call to
django_authenticate
to provide arguments as appropriate to authenticate to ourActivateAccountBackend
.
authentication/backend.py
Responsibility: Receive credential information from an authentication class, and return a "User" model that matches those credentials.
- Provides an authentication backend that functions similarly to a
ModelBackend
, except does not check passwords, and only accepts users with the usernametest
.
Known Required Changes for production:
- Change the
Model
being used.- We probably still want to use the
get_user_model()
function, and update theAUTH_USER_MODEL
setting.
- We probably still want to use the
- Expect to receive
crsid
/lastname
,dob
, andregistration_code
instead ofusername
. - Expect to return a Model matching credentials received.
-
get_user
expected to only be valid forcrsid
.
authentication/views.py
Responsibility: Provide an endpoint to return a token for a user to authenticate to the other API endpoints.
- Provides a custom
LoginView
for knox (as per their documentation. - This uses the
ActivateAccountAuthentication
class for authentication.
Known Required Changes for production:
- Hopefully none, once the
ActivateAccountAuthentication
class is ready this should work as-is.
User Models
Currently this is using the stock UserModel
for users. This will need to change, and when we do the follow on work for this activity we need to make sure the model we tie tokens to is suitable for use.
knox
(and normal DRF token authentication) ties each token created to a user model in the database. I would suggest that in order to work within Django's framework rather than fight against it, we make our database store the required information of crsid
, lastname
, dob
, and registration_code
as a model which inherits from AbstractBaseUser
.
This can then be set as the AUTH_USER_MODEL
setting, and gives us a lot of stuff out of the box for free. We could also forgo inheriting from the base user, and instead simply ensure that our Model meets the requirements specific for our use, this has a possible advantage of not accidentally including methods/functionality that are not appropriate for this application.
We do need to be wary, that this model expects to have a settable and usable password, which we do not. We need to ensure any password behaviours are completely disabled by our model. (Although users will have an initial_password
it can't be used to authenticate to this service, nor can it be changed. It's information for the user only!)