Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AWS Cognito for Django3 + DRF Authentication

I'm trying to set up an AWS Cognito backend I have a React frontend already working with it, now I need my DRF API to authenticate using the Cognito as backend.

I have found a few Python packages for that, none of them seem to be actively maintained django-warrant doesn't work with Django3 and is pretty much dead

Django Cognito JWT seems to be my best bet, but also not actively maintained, the documentation is very poor, and there is a medium post on how to use, not very detailed, but better than nothing.

So, I tried to follow the documentation

Added the env vars on my settings

COGNITO_AWS_REGION = 'us-east-1'
COGNITO_USER_POOL = 'us-east-1_xxxxxxx'  # same user pool id I'm using on the React app
COGNITO_AUDIENCE = 'XXXXXXXXXXXXXXXXXXXXXX' # the same client id I'm using on the React app

Then on my DRF authentication classes:

    'DEFAULT_AUTHENTICATION_CLASSES': [
        'django_cognito_jwt.JSONWebTokenAuthentication',
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ],

And finally the user model:

AUTH_USER_MODEL = 'accounts.MyUser'
COGNITO_USER_MODEL = "accounts.MyUser"

My custom User Model:

class MyUser(AbstractUser):
    """User model."""

    username = None
    email = models.EmailField(_('email address'), unique=True)

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    objects = UserManager()

I'm also using DRF JWT package, and if I try to login with a Cognito user, something like:

curl -X POST -H "Content-Type: application/json" -d '{"email":"[email protected]","password":"secretpassword"}' http://localhost/api-token-auth/

I get an error

{"non_field_errors":["Unable to log in with provided credentials."]}

On another hand, if I try to login with a local Django user via Django Rest Framework JWT it works fine and I get the JWT token as a response, so I guess the issue is the Cognito integration.

Any idea what I'm missing? or how can I debug in order to figure out what is happening?

UPDATE

After digging a bit more on the code, I found out a few things:

Even doing a DRF JWT authentication, the code end up at: django/contrib/auth/init.py Where it loops through all the authentication backends for Django, not for DRF:

for backend, backend_path in _get_backends(return_tuples=True):

Still using the ModelBackend to authenticate the user.

So, I guess I also need to add some Cognito authentication backend for Django. I checked if I could just use the same backend used on DRF, but then I got an error of invalid argument: TypeError: authenticate() got an unexpected keyword argument 'email'

UPDATE 2 It seems that one of the issues is because I use email instead of username to authenticate, and none of the packages seems to support it

like image 745
dfranca Avatar asked Feb 19 '20 21:02

dfranca


1 Answers

Basically, there are 2 steps to achieve your goal.

  1. Get IdToken and AccessToken from Cognito by Boto3 library.
  2. Apply the IdToken in this Pattern Authorization Bearer IdToken to call API via curl.

Unlike rest_framework_jwt , django_cognito_jwt only deals with JWT token at header. django_cognito_jwt does not cover step 1. That is why you are getting this error TypeError: authenticate() got an unexpected keyword argument 'email'.

So, solution is use boto3 to get IdToken and apply curl by passing Authorization:Bearer IdToken header.

like image 73
Saiful Azad Avatar answered Oct 21 '22 08:10

Saiful Azad