Skip to main content

Overview

The API uses JWT (JSON Web Token) authentication powered by djangorestframework-simplejwt. All protected endpoints require a valid access token passed in the Authorization header. The frontend stores the access token in a cookie (accessToken) and silently refreshes it every 4 minutes using the refresh token (refreshToken). Access tokens expire after 1 day; refresh tokens expire after 7 days.

How to authenticate requests

Include the Authorization header with every request to a protected endpoint:
Authorization: Bearer <your_access_token>

Access levels

LevelPermission classDescription
PublicAllowAnyNo authentication required
AuthenticatedIsAuthenticatedValid access token required
Role-restrictedIsAuthenticatedAndRoleAuthenticated + member of a specific Django group
The IsAuthenticatedAndRole permission class checks request.user.groups. Set required_role on the view to restrict access to a named group. If required_role is not set, authentication alone is sufficient.

POST /api/auth/token/

Obtain a JWT access and refresh token pair. This is the primary login endpoint used by the frontend. Accepts username or email in the username field.
The frontend sends credentials to /api/auth/token/ (not /api/auth/login/) to receive JWT tokens. The /api/auth/login/ endpoint performs a Django session login and returns user data without issuing JWT tokens.

Request body

username
string
required
The user’s username. The custom token serializer looks up the user by exact username match.
password
string
required
The user’s password.

Response

access
string
required
Short-lived JWT access token. Valid for 1 day. Contains username and email claims in addition to standard JWT fields.
refresh
string
required
Long-lived JWT refresh token. Valid for 7 days. Use this to obtain a new access token without re-entering credentials.
curl --request POST \
  --url https://your-api.example.com/api/auth/token/ \
  --header 'Content-Type: application/json' \
  --data '{
    "username": "juanperez",
    "password": "secretpassword"
  }'
200 response
{
  "access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
400 response — invalid credentials
{
  "detail": "No active account found with the given credentials"
}

POST /api/auth/token/refresh/

Exchange a valid refresh token for a new access token. The frontend calls this endpoint automatically every 4 minutes to keep sessions alive.

Request body

refresh
string
required
A valid, unexpired refresh token obtained from /api/auth/token/.

Response

access
string
required
A new JWT access token.
curl --request POST \
  --url https://your-api.example.com/api/auth/token/refresh/ \
  --header 'Content-Type: application/json' \
  --data '{"refresh": "<your_refresh_token>"}'
200 response
{
  "access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
401 response — expired or invalid refresh token
{
  "detail": "Token is invalid or expired",
  "code": "token_not_valid"
}
When the refresh token is missing or invalid, the frontend calls logout() and clears all stored tokens. Users must re-authenticate.

POST /api/auth/login/

Authenticates a user via Django session login and returns structured user data. Supports login with either username or email in the username field.
This endpoint does not issue JWT tokens. For JWT-based auth (the default frontend flow), use POST /api/auth/token/ instead. Use this endpoint when you need the full user object (groups, flags) without a separate profile fetch.

Request body

username
string
required
The user’s username or email address. The serializer searches by username first, then by User.email, then by UserProfile.email.
password
string
required
The user’s password.

Response

message
string
required
"Login exitoso" on success.
user
object
required
# Login with username
curl --request POST \
  --url https://your-api.example.com/api/auth/login/ \
  --header 'Content-Type: application/json' \
  --data '{"username": "juanperez", "password": "secretpassword"}'

# Login with email
curl --request POST \
  --url https://your-api.example.com/api/auth/login/ \
  --header 'Content-Type: application/json' \
  --data '{"username": "[email protected]", "password": "secretpassword"}'
200 response
{
  "message": "Login exitoso",
  "user": {
    "user_id": 1,
    "username": "juanperez",
    "email": "[email protected]",
    "first_name": "Juan",
    "last_name": "Pérez",
    "is_staff": false,
    "is_superuser": false,
    "groups": ["client"]
  }
}
400 response — invalid credentials
{
  "error": "Credenciales inválidas",
  "details": {
    "non_field_errors": ["Credenciales inválidas. Verifica tu usuario/email y contraseña."]
  }
}
400 response — account disabled
{
  "error": "Credenciales inválidas",
  "details": {
    "non_field_errors": ["Esta cuenta está desactivada."]
  }
}

POST /api/auth/register/

Creates a new user account with an optional extended profile. Accepts multipart/form-data to support profile picture uploads. The role field (roles) is sent at the root level — not nested under profile. The frontend defaults new registrations to the client role.

Request body

username
string
required
Unique username for the new account.
email
string
required
Email address for the account.
password
string
required
Password for the account.
first_name
string
User’s given name.
last_name
string
User’s family name.
roles
integer[]
List of Django group IDs to assign to the user. Defaults to client role if omitted.
profile.phone
string
Contact phone number (max 20 characters).
profile.address
string
Street address (max 255 characters).
profile.birth_date
string
Date of birth in YYYY-MM-DD format.
profile.province
integer
ID of the user’s province.
profile.bio
string
Short biography.
profile.profile_picture
file
Profile image file. Accepted formats: any image type. Stored under profile_pics/.

Response

Returns the created user object (RegisterSerializer output).
username
string
required
email
string
required
first_name
string
last_name
string
roles
integer[]
List of group IDs assigned to the user.
profile
object
The extended profile object if profile data was provided.
curl --request POST \
  --url https://your-api.example.com/api/auth/register/ \
  --form 'username=juanperez' \
  --form '[email protected]' \
  --form 'password=secretpassword' \
  --form 'first_name=Juan' \
  --form 'last_name=Pérez' \
  --form 'roles=2' \
  --form 'profile.phone=+506 8888-1234' \
  --form 'profile.birth_date=1990-05-15' \
  --form 'profile.province=1' \
  --form 'profile.profile_picture=@/path/to/photo.jpg'
201 response
{
  "username": "juanperez",
  "email": "[email protected]",
  "first_name": "Juan",
  "last_name": "Pérez",
  "roles": [2],
  "profile": {
    "username": "juanperez",
    "email": "[email protected]",
    "phone": "+506 8888-1234",
    "birth_date": "1990-05-15",
    "province": 1
  }
}

POST /api/auth/logout/

Closes the current Django session. The frontend also calls this endpoint when clearing JWT cookies, sending the refresh_token in the request body.

Request body

refresh_token
string
The current refresh token. Included by the frontend when logging out to allow server-side token invalidation if implemented.

Response

message
string
required
"Logout exitoso" on success.
curl --request POST \
  --url https://your-api.example.com/api/auth/logout/ \
  --header 'Authorization: Bearer <your_access_token>' \
  --header 'Content-Type: application/json' \
  --data '{"refresh_token": "<your_refresh_token>"}'
200 response
{
  "message": "Logout exitoso"
}

POST /api/auth/forgot-password/

Looks up a user by email and returns a password reset token and UID. The client uses these values to call POST /api/auth/reset-password-confirm/.
The current implementation returns the reset_link, uid, and token directly in the response body rather than sending an email. Do not expose this response payload to end users in a production environment without adding email delivery.

Request body

email
string
required
The email address associated with the account to reset.

Response

message
string
"Password reset link generated."
Frontend path with uid and token query parameters: /reset-password-confirm/?uid=<uid>&token=<token>
uid
string
URL-safe base64-encoded user primary key. Pass this to /api/auth/reset-password-confirm/.
token
string
Django password reset token. Single-use; expires after Django’s default token timeout.
curl --request POST \
  --url https://your-api.example.com/api/auth/forgot-password/ \
  --header 'Content-Type: application/json' \
  --data '{"email": "[email protected]"}'
200 response
{
  "message": "Password reset link generated.",
  "reset_link": "/reset-password-confirm/?uid=MQ&token=bpe4fs-...",
  "uid": "MQ",
  "token": "bpe4fs-9e83fa..."
}
400 response — email not found
{
  "error": "No user found with this email."
}

POST /api/auth/reset-password-confirm/

Resets a user’s password using the uid and token received from /api/auth/forgot-password/.
This token is single-use. Once the password is reset successfully, the token cannot be reused.

Request body

uid
string
required
URL-safe base64-encoded user primary key from the forgot-password response.
token
string
required
The password reset token from the forgot-password response.
new_password
string
required
The new password to set for the account.

Response

message
string
required
"Password has been reset successfully."
curl --request POST \
  --url https://your-api.example.com/api/auth/reset-password-confirm/ \
  --header 'Content-Type: application/json' \
  --data '{
    "uid": "MQ",
    "token": "bpe4fs-9e83fa...",
    "new_password": "newStrongPassword!"
  }'
200 response
{
  "message": "Password has been reset successfully."
}
400 response — invalid or expired token
{
  "error": "Invalid or expired token."
}
400 response — missing fields
{
  "error": "Missing required fields."
}

Build docs developers (and LLMs) love