Skip to main content

Overview

The RestAPI project uses JSON Web Token (JWT) authentication powered by Simple JWT. This provides a secure, stateless authentication mechanism for all API endpoints.
JWT authentication is configured in RestAPI/settings.py:203-214 with an 8-hour token lifetime.

How JWT Authentication Works

1

User Login

The client sends credentials (email/password) to obtain access and refresh tokens.
2

Token Storage

The client stores the access token securely (e.g., in memory or secure storage).
3

Authenticated Requests

The client includes the access token in the Authorization header for all subsequent requests.
4

Token Refresh

When the access token expires (after 8 hours), use the refresh token to obtain a new access token.

Configuration

The JWT authentication is configured in RestAPI/settings.py with the following settings:
RestAPI/settings.py
REST_FRAMEWORK = {
    'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema',
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ],
}

# Token validity configuration
from datetime import timedelta
SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(hours=8),
}

Key Features

  • Access Token Lifetime: 8 hours
  • Authentication Class: JWTAuthentication
  • Stateless: No server-side session storage required
  • Secure: Tokens are cryptographically signed

Obtaining Tokens

Note: The current implementation uses a custom user model with SHA-256 hashed passwords. You’ll need to implement token endpoints using Simple JWT’s views or create custom authentication endpoints.

Standard Simple JWT Endpoints

To enable JWT token endpoints, add the following to your RestAPI/urls.py:
RestAPI/urls.py
from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
)

urlpatterns = [
    path('admin/', admin.site.urls),
    path('Database/', include('task_aplication.urls')),
    
    # JWT Authentication endpoints
    path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
    path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
]

Obtain Access Token

curl -X POST http://localhost:8000/api/token/ \
  -H "Content-Type: application/json" \
  -d '{
    "username": "[email protected]",
    "password": "your_password"
  }'
Response:
{
  "access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Making Authenticated Requests

Include the access token in the Authorization header with the Bearer prefix:
curl -X GET http://localhost:8000/Database/usuarios/ \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."

Refreshing Tokens

When your access token expires (after 8 hours), use the refresh token to obtain a new access token:
curl -X POST http://localhost:8000/api/token/refresh/ \
  -H "Content-Type: application/json" \
  -d '{
    "refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  }'
Response:
{
  "access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

User Model

The custom Usuario model is defined in task_aplication/models.py:55-81 with the following key fields:
task_aplication/models.py
class Usuario(models.Model):
    id_usuario = models.AutoField(primary_key=True)
    rol_usuario = models.ForeignKey(RolUsuario, on_delete=models.CASCADE)
    email = models.CharField(max_length=50)
    placa = models.CharField(max_length=8, null=True, blank=True)
    contrasena = models.CharField(max_length=64)  # SHA-256 hash
    token_notificacion = models.TextField()
    fecha_creacion = models.DateField(auto_now_add=True)
    fecha_modificacion = models.DateField(auto_now=True)
    id_ciudad = models.ForeignKey(Ciudad, on_delete=models.CASCADE, default=1)
    id_pais = models.ForeignKey(Pais, on_delete=models.CASCADE, default=1)
    estado = models.IntegerField(choices=[
        (0, 'Inactivo'),
        (1, 'Activo'),
        (3, 'deshabilitado')
    ], default=0)

    def save(self, *args, **kwargs):
        # Hash password with SHA-256 on creation
        if not self.id_usuario:
            self.contrasena = self._hash_password(self.contrasena)
        super().save(*args, **kwargs)

    def _hash_password(self, password):
        sha256 = hashlib.sha256()
        sha256.update(password.encode('utf-8'))
        return sha256.hexdigest()
The current implementation uses SHA-256 for password hashing. For production use, consider migrating to Django’s built-in password hashers which use PBKDF2 with stronger security.

CORS Configuration

The API includes CORS support via django-cors-headers configured in RestAPI/settings.py:38-89:

Development Mode (DEBUG=True)

CORS_ALLOW_ALL_ORIGINS = True
ALLOWED_HOSTS = ['*']
CORS_ALLOW_HEADERS = ['*']

Production Mode (DEBUG=False)

CORS_ALLOW_CREDENTIALS = True
CORS_ALLOW_ALL_ORIGINS = False
CORS_ALLOWED_ORIGINS = [
    'migoadvs.pythonanywhere.com',
]
ALLOWED_HOSTS = [
    'localhost',
    '127.0.0.1',
    'migoadvs.pythonanywhere.com',
]

Allowed Methods and Headers

CORS_ALLOW_METHODS = (
    'GET',
    'OPTIONS',
    'PATCH',
    'POST',
    'PUT',
    'DELETE',
)

CORS_ALLOW_HEADERS = (
    'accept',
    'accept-encoding',
    'authorization',  # Required for JWT
    'content-type',
    'dnt',
    'origin',
    'user-agent',
    'x-csrftoken',
    'x-requested-with',
)

Error Responses

Invalid Credentials

{
  "detail": "No active account found with the given credentials"
}

Expired Token

{
  "detail": "Given token not valid for any token type",
  "code": "token_not_valid",
  "messages": [
    {
      "token_class": "AccessToken",
      "token_type": "access",
      "message": "Token is invalid or expired"
    }
  ]
}

Missing Token

{
  "detail": "Authentication credentials were not provided."
}

Security Best Practices

  • Never store tokens in localStorage or sessionStorage in web applications
  • Use httpOnly cookies or secure in-memory storage
  • For mobile apps, use secure storage mechanisms (Keychain on iOS, KeyStore on Android)
  • Always use HTTPS in production
  • Never send tokens in URL parameters
  • Always use the Authorization header with Bearer prefix
  • Implement token refresh logic before expiration (8-hour lifetime)
  • Handle token expiration gracefully with automatic refresh
  • Clear tokens on logout
  • Keep your DJANGO_KEY secret and unique
  • Use environment variables for sensitive configuration
  • Never commit .env files to version control
  • Rotate secrets regularly

Next Steps

API Reference

Explore authenticated API endpoints

User Management

Learn about user registration and management

Build docs developers (and LLMs) love