Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion backend/backend/core/routers/api_tokens/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,43 @@ def regenerate_api_key(request: Request, key_id: str) -> Response:
@api_view([HTTPMethods.POST])
@handle_http_request
def generate_token(request: Request) -> Response:
"""Legacy token generation endpoint."""
"""Legacy token generation endpoint.

Now creates a proper APIToken record with vtk_ prefix, label, and expiry
to maintain consistency with the api-keys/create endpoint.
Uses upsert: updates existing auto-generated token or creates a new one.
"""
api_key = generate_api_key()
sig = generate_signature(api_key)
new_expiry = now() + timedelta(days=django_settings.API_KEY_EXPIRY_DAYS)

# Upsert: update existing auto-generated token if present, else create
existing = APIToken.objects.filter(
user=request.user, label="Default"
).order_by("-created_at").first()

if existing:
existing.token = api_key
existing.signature = sig
existing.is_disabled = False
existing.expires_at = new_expiry
existing.save(update_fields=["token", "signature", "is_disabled", "expires_at"])
token = existing
else:
token = APIToken.objects.create(
user=request.user,
token=api_key,
signature=sig,
label="Default",
expires_at=new_expiry,
)

logger.info(f"Legacy token generated: id={token.id}, user={request.user.email}")
log_api_key_event(
request, action="create", key_id=token.id,
key_label="Default", key_masked=token.masked_token,
)

return Response({
"message": "Token generated successfully.",
"token": api_key,
Expand Down
10 changes: 7 additions & 3 deletions backend/backend/core/user.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import logging
import uuid
from datetime import timedelta
from typing import Any, Optional

from django.conf import settings as django_settings
from django.db import IntegrityError
from django.db import transaction
from django.utils.timezone import now

from backend.core.models.api_tokens import APIToken
from backend.core.models.organization_model import Organization
from backend.core.models.user_model import User
from backend.core.services.api_key_service import generate_api_key, generate_signature

Logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -87,11 +88,14 @@ def get_or_create_valid_token(self, user: User, organization: Organization):
token.delete()
token = None
if token is None:
api_key = generate_api_key()
token = APIToken.objects.create(
user=user,
organization=organization,
token=str(uuid.uuid4().hex),
expires_at=now() + timedelta(days=90),
token=api_key,
signature=generate_signature(api_key),
label="Default",
expires_at=now() + timedelta(days=django_settings.API_KEY_EXPIRY_DAYS),
)
logging.info(f"A new api token for user: {user} and tenant: {organization} is created")
except Exception as e:
Expand Down
24 changes: 20 additions & 4 deletions backend/backend/core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
from backend.utils.tenant_context import get_current_tenant

from backend.core.models.api_tokens import APIToken
from backend.core.services.api_key_audit import log_api_key_event
from backend.core.services.api_key_service import generate_api_key, generate_signature
from django.conf import settings as django_settings
from django.utils.timezone import now
from datetime import timedelta

Expand Down Expand Up @@ -52,16 +55,29 @@ def update_user_profile(request: Request) -> Response:


def update_user_token(request, user):
new_token = request.data.get("token")
# token_value is sent back by the frontend — used only to detect "unchanged"
token_value = request.data.get("token")
existing_token: APIToken = APIToken.objects.filter(user=user).first()

if new_token:
if existing_token and existing_token.token == new_token:
if token_value:
# Skip regeneration if the token hasn't changed
if existing_token and existing_token.token == token_value:
return
if existing_token:
existing_token.delete()
Comment thread
greptile-apps[bot] marked this conversation as resolved.
Outdated

APIToken.objects.create(user=user, token=new_token, expires_at= now() + timedelta(days=90))
api_key = generate_api_key()
token = APIToken.objects.create(
user=user,
token=api_key,
signature=generate_signature(api_key),
label="Default",
expires_at=now() + timedelta(days=django_settings.API_KEY_EXPIRY_DAYS),
)
Comment thread
greptile-apps[bot] marked this conversation as resolved.
log_api_key_event(
request, action="create", key_id=token.id,
key_label="Default", key_masked=token.masked_token,
)
Comment thread
wicky-zipstack marked this conversation as resolved.
else:
if existing_token:
Comment thread
wicky-zipstack marked this conversation as resolved.
existing_token.delete()
Expand Down
Loading