DEV Community

Jakub Miazek
Jakub Miazek

Posted on • Edited on

Django REST API Protection via 2FA

What I built

In these days we need to be sure that data which we using and sharing is consistent and we can trust in it. One of methods is protect your api as best as possible. I decided to share my approach to protect Django REST Framework JWT Authentication with Twilio 2FA. In this sample project I presenting integration with Verify API and Authy API from Twilio for Python.

Demo Link

Can be tested by postman collection included here: https://github.com/grillazz/twofa_for_drf/blob/master/twilio.postman_collection.json

Link to Code

https://github.com/grillazz/twofa_for_drf

Link to Twilio Code Exchange submission

https://www.twilio.com/code-exchange/django-rest-api-protection-2fa

How it works

STEP 0A: Django Rest Framework JWT Authentication when 2FA disabled.

for below cURL

curl --location --request POST 'http://127.0.0.1:8000/api/token/' \
--header 'Content-Type: application/json' \
--data-raw '{
    "username": "twilio",
    "password": "twiliopass"
}'
Enter fullscreen mode Exit fullscreen mode

we receive response with HTTP code 200 with JSON body

{
    "refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTU4ODQ5NjY0OSwianRpIjoiMDcwNTJhNjc3OWIwNDJiMGE3ZmNkYzkxMmNiNTJkMTYiLCJ1c2VyX2lkIjo0fQ.h3KeHB29WiMQgdpsdJbmNy6mATGzTL4_MBWmQf1jZDE",
    "access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTg4NDEwNTQ5LCJqdGkiOiI5NWVlOWUxNDU0MTk0MDc3ODlhMzQ3N2VkNGI0NDEwZSIsInVzZXJfaWQiOjR9.XJO7d9qH3F0nKp9AQg9AIaySKLqBKPVzG-yvkxLhwOs"
}
Enter fullscreen mode Exit fullscreen mode

 

STEP 1: Phone verification view with Twilio Authy API.

This endpoint will check if user mobile phone number is valid.
If YES Twilio API send 4 digit verification token via SMS.

curl --location --request POST 'http://127.0.0.1:8000/api/2fa/phone-verify/' \
--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTg4NDEwOTE1LCJqdGkiOiJkYjNhYTgwYjVmYTg0ZTk5YTAyMTI5YzU0MjBkZTJlOCIsInVzZXJfaWQiOjJ9.aY2UQiDMON3X2Ibvlj0KyocTmc5RS7jeLP9RjO58ynk' \
--header 'Content-Type: application/json' \
--data-raw '{
    "authy_phone": "+48123456789"
}'
...
if SUCCESS we receive response with HTTP code 204 with no JSON body

Enter fullscreen mode Exit fullscreen mode

 

STEP 2: Phone registration view with Twilio Authy API

View will validate if 4 digit token sent to user phone number is valid.
If Twilio verification check pass in next step Twilio API call will register user for 2FA
If success: user instance will be updated with verified phone number and received from Twilio API authy_id

curl --location --request POST 'http://127.0.0.1:8000/api/2fa/phone-register/' \
--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTg4NDExNTMxLCJqdGkiOiJmMzFmN2IyNmI4MDM0NDRjOTA0M2Q3ODNmNGVjYTEzMyIsInVzZXJfaWQiOjJ9.j9rJjFpdM9arpn905bL45nyGQoMpJhkC0mmHRbUm8QA' \
--header 'Content-Type: application/json' \
--data-raw '{
    "authy_phone": "+48123456789",
    "token": "1234" 
}'
...
if SUCCESS we receive response with HTTP code 204 with no JSON body

Enter fullscreen mode Exit fullscreen mode

 

STEP 0B: Django Rest Framework JWT Authentication when 2FA enabled.

for below cURL

curl --location --request POST 'http://127.0.0.1:8000/api/token/' \
--header 'Content-Type: application/json' \
--data-raw '{
    "username": "twilio",
    "password": "twiliopass"
}'
Enter fullscreen mode Exit fullscreen mode

we receive response with HTTP code 206 with JSON body

{
    "message": "SMS request successful. Two Factor Token verification expected."
}
Enter fullscreen mode Exit fullscreen mode

 

STEP 3: User Authentication view supported by Twilo API Two Factor

This view verify if Twilio 2FA registered user entered correct 7 digit token.
Token will be requested by TwoFaTokenObtainPairView only for 2FA registered users
If SUCCESS: user receive refresh and access JWT.

curl --location --request POST 'http://127.0.0.1:8000/api/2fa/token-verify/' \
--header 'Content-Type: application/json' \
--data-raw '{
    "username": "twilio",
    "password": "twiliopass",
    "token": "7654321"
}'
Enter fullscreen mode Exit fullscreen mode

if SUCCESS we receive response with HTTP code 200 with 3JSON body

{
    "refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTU4ODQ5ODI3OCwianRpIjoiY2U5M2I5ZjExMTE1NGMxYThiZmEzNWJkZmE1NmMyNmEiLCJ1c2VyX2lkIjoyfQ.FZUeVVzPWl4dUjPEUa6yyfmOLPLpG5qK6nq5AyC6jY0",
    "access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTg4NDEyMTc4LCJqdGkiOiJlMGViZGU4Zjk1MDg0YWU2YmYxZmY4YWE0MDk2ODE2ZCIsInVzZXJfaWQiOjJ9.gU-onXzHKpc_jn9RyUVZS940_ivL7pQfDbU4ltv5w-c"
}
Enter fullscreen mode Exit fullscreen mode

 

Additional Resources/Info

My assumption here is that you have exp with Django and DRF.
If you don't please visit first:
https://www.djangoproject.com/ and https://www.django-rest-framework.org/
You can also read some good books about Django and DRF i.e. https://wsvincent.com/best-django-books/

What is left:

  • add rest api flows

Top comments (0)