DEV Community

Maximiliano Burgos
Maximiliano Burgos

Posted on

Diario de Python | #12. Color Choice: Autenticación por Token

El módulo de autenticación de Django ya viene incorcopado con el framework en si, y cuando lanzamos:

python manage.py migrate

Se migran las tablas que permiten el manejo de usuarios. Esto me resuelve gran parte del trabajo de sesiones, lo cual se agradece bastante.

Manos a la obra

Existe una clase AbstractUser, la cual podríamos heredar para generar nuestro propio usuario personalizado y agregar los campos que necesitemos. La voy a armar sin atributos, de momento, para que si en el futuro necesitamos agregar información especial al usuario, ya tengamos nuestra clase generada.

En models.py declaro el nuevo modelo User:

from django.contrib.auth.models import AbstractUser


class User(AbstractUser):
    pass
Enter fullscreen mode Exit fullscreen mode

Luego en settings.py le digo a Django que reconozca este modelo como su entidad User de su módulo de auth de la app api:

AUTH_USER_MODEL = 'api.User'
Enter fullscreen mode Exit fullscreen mode

Armo la migración y al ejecutarla me dice:

ValueError: The field admin.LogEntry.user was declared with a lazy reference to 'api.user', but app 'api' doesn't provide model 'user'.
The field api.Vote.user was declared with a lazy reference to 'api.user', but app 'api' doesn't provide model 'user'.
Enter fullscreen mode Exit fullscreen mode

Necesito declarar a Vote, el cual tiene una clave foranea de User, que se trata de mi usuario y no el del módulo de Django. El problema es que las migraciones ya corrieron anteriormente con la relación anterior, por lo cual no me las va a tomar.

Borro mi archivo de SQLite3.db y lo recreo con las migraciones, para que Django entienda las nuevas relaciones con mi usuario personalizado.

Auth Token y Auth User

Llegados a este punto, ya tengo un usuario personalizado, unido al módulo de autenticación de Django. Ahora voy a armar un endpoint de login en views.py, y lo voy a combinar con un componente de Auth Token que viene con DRF:

@api_view(['POST'])
def user_auth(request):
    username = request.data.get('username')
    password = request.data.get('password')

    if not username or not password:
        return Response({'error': 'Se debe indicar usuario y contraseña'}, status=status.HTTP_204_NO_CONTENT)

    user = authenticate(request, username=username, password=password)
    if user is None:
        return Response({'error': 'El usuario o la contraseña son incorrectos'}, status=status.HTTP_400_BAD_REQUEST)

    token, created = Token.objects.get_or_create(user=user)
    return Response({'token': token.key}, status=status.HTTP_200_OK)
Enter fullscreen mode Exit fullscreen mode

Luego defino la ruta en urls.py:

urlpatterns += [
    path('auth', user_auth, name='api-auth')
]
Enter fullscreen mode Exit fullscreen mode

A este método accedo mediante "POST /api/auth" con un JSON que contenga mi usuario y contraseña.

Pero, ¿qué hace mi vista user_auth?. Básicamente hace lo siguiente:

  • Toma del request el user y pass.
  • Si alguno de estos valores no existe, devuelve un error.
  • Luego, con la función authenticate corre el proceso de login.
  • Si el login es correcto, llama al método get_or_create del modelo Token, previamente migrado por haber importado el módulo authtoken.
  • Finalmente devuelve dicho token, que nos servirá para mantener una sesión y decirle a la API quiénes somos.

Vamos a probarlo en Postman haciendo un POST a la ruta anteriormente indicada, con un JSON Body como este:

{
 "username": "admin",
 "password": "123"
}
Enter fullscreen mode Exit fullscreen mode

Y nos devolverá lo siguiente:

Postman POST

Conclusiones

Quizá ahora la autenticación por Token parezca innecesaria, dado que no lo aplicamos a ningún endpoint, pero en el próximo capítulo van a ver la potencia de este simple string, y como impacta en nuestro objeto request.

¡Nos vemos en el próximo!

Top comments (0)