Skip to content

Conversation

@Ishankoradia
Copy link
Contributor

@Ishankoradia Ishankoradia commented Oct 13, 2025

Summary by CodeRabbit

  • New Features

    • v2 login using httpOnly secure cookies; returns user/org info and sets session cookies.
    • Automatic access-token refresh via cookie; logout clears session cookies.
    • Short-lived iframe token for embedded views (2-minute expiry).
  • Security

    • Tightened CORS/CSRF: allowlisted origins, credentialed requests, secure/sameSite/httpOnly cookie defaults, and updated allowed headers.
    • Authentication now prefers cookie-based tokens (login endpoint still accepts header tokens).
  • Chores

    • Improved log messages to reference organization slugs.

@coderabbitai
Copy link

coderabbitai bot commented Oct 13, 2025

Walkthrough

Adds cookie-based v2 auth endpoints (login, refresh, logout, iframe token), updates v1 token response, changes JWT middleware to prefer cookies (except login_token path), tightens CORS/cookie/CSRF settings, and adds typing/log message tweaks in charts API.

Changes

Cohort / File(s) Summary
Auth endpoints (v2) & logout
ddpui/api/user_org_api.py
Adds v2 cookie-based endpoints: post_login_v2 (sets httpOnly access/refresh cookies), post_token_refresh_v2 (reads refresh cookie, issues new access cookie), get_iframe_token (short-lived iframe token, 2min). Reworks post_logout to read/blacklist refresh from cookies and always clear cookies. post_login_token now stringifies access token and includes refresh. Adds imports: JsonResponse, settings, timedelta, AccessToken.
JWT middleware token sourcing
ddpui/auth.py
Adds CustomJwtAuthMiddleware.__call__ to prefer Authorization header for /api/login_token/ and use cookie access_token elsewhere (fallback to header). authenticate signature changed to token=None and now raises 401 when token missing.
CORS / CSRF / Cookie settings
ddpui/settings.py
Disables wildcard CORS (CORS_ALLOW_ALL_ORIGINS=False), enables credentials (CORS_ALLOW_CREDENTIALS=True), defines CORS_ALLOWED_ORIGINS, expands CORS_ALLOW_HEADERS to include X-CSRFToken, moves CommonMiddleware after SecurityMiddleware, and adds cookie config flags COOKIE_SECURE, COOKIE_SAMESITE, COOKIE_HTTPONLY. Removes CORS_ORIGIN_WHITELIST.
Charts typing / logging
ddpui/api/charts_api.py
Adds explicit OrgUser type annotations and updates “no warehouse” warnings to reference org.slug instead of org.id.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant FE as Frontend
  participant API as /api/v2/login
  participant DB as User/Org Store
  participant Auth as Token Issuer

  FE->>API: POST /api/v2/login (username/password)
  API->>DB: Validate credentials
  DB-->>API: User, Org
  API->>Auth: Issue access_token & refresh_token
  Auth-->>API: Tokens
  API-->>FE: 200 {user/org data}
  Note right of API: Set httpOnly cookies: access_token, refresh_token (attrs from settings)
Loading
sequenceDiagram
  autonumber
  participant FE as Frontend
  participant API as /api/v2/token/refresh
  participant Auth as Token Issuer

  FE->>API: GET/POST (no body)
  Note right of API: Reads `refresh_token` from httpOnly cookie
  API->>Auth: Validate refresh, mint new access_token
  Auth-->>API: access_token
  API-->>FE: 200
  Note right of API: Set httpOnly access_token cookie
Loading
sequenceDiagram
  autonumber
  participant FE as Frontend
  participant API as /api/v2/iframe-token
  participant Auth as Token Issuer

  FE->>API: GET (authenticated)
  API->>Auth: Issue iframe_token (expires in 2 minutes)
  Auth-->>API: iframe_token
  API-->>FE: 200 {iframe_token, org_slug}
Loading
sequenceDiagram
  autonumber
  participant Client as Client
  participant MW as CustomJwtAuthMiddleware
  participant API as API View
  participant Auth as Token Validator

  Client->>MW: HTTP Request
  alt Path == /api/login_token/
    MW->>MW: Prefer Authorization header (Bearer)
  else Other paths
    MW->>MW: Try cookie `access_token`
    opt cookie missing
      MW->>MW: Fallback to Authorization header
    end
  end
  alt Token present
    MW->>Auth: authenticate(token)
    Auth-->>MW: user/orguser context
    MW->>API: Forward request with context
  else No token
    MW-->>Client: 401 Unauthorized
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • pradnk
  • fatchat

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title Check ❓ Inconclusive The title “Feature/cookies” is overly generic and simply echoes the branch name without clearly conveying the primary change, making it difficult for someone scanning PRs to understand the main update at a glance. Rename the title to a concise, descriptive sentence such as “Add cookie-based authentication endpoints and middleware” to clearly reflect the main change.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/cookies

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link

codecov bot commented Oct 13, 2025

Codecov Report

❌ Patch coverage is 20.28986% with 55 lines in your changes missing coverage. Please review.
✅ Project coverage is 52.53%. Comparing base (cf26ada) to head (a4ce9f0).
⚠️ Report is 11 commits behind head on main.

Files with missing lines Patch % Lines
ddpui/api/user_org_api.py 20.37% 43 Missing ⚠️
ddpui/auth.py 27.27% 8 Missing ⚠️
ddpui/api/charts_api.py 0.00% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1179      +/-   ##
==========================================
- Coverage   52.65%   52.53%   -0.12%     
==========================================
  Files          97       97              
  Lines       11415    11463      +48     
==========================================
+ Hits         6010     6022      +12     
- Misses       5405     5441      +36     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (5)
ddpui/settings.py (1)

125-137: Duplicate CommonMiddleware and ordering.

CommonMiddleware appears twice (Lines 128 and 131). Keep one and ensure CorsMiddleware is as high as possible and before CommonMiddleware, per django-cors-headers docs.

 MIDDLEWARE = [
   "django_prometheus.middleware.PrometheusBeforeMiddleware",
   "corsheaders.middleware.CorsMiddleware",
-  "django.middleware.common.CommonMiddleware",
   "django.middleware.security.SecurityMiddleware",
   "django.contrib.sessions.middleware.SessionMiddleware",
-  "django.middleware.common.CommonMiddleware",
+  "django.middleware.common.CommonMiddleware",
   "django.middleware.csrf.CsrfViewMiddleware",
   ...
 ]
ddpui/api/user_org_api.py (3)

639-688: Set cookie expiries and review SameSite.

  • Tie cookie lifetime to token lifetime to avoid lingering stale cookies.
  • SameSite="Lax" works for same-site subdomains; if you ever need true cross-site (third-party) requests, switch to "None" + Secure and add CSRF protections accordingly.

Apply:

-    # Set access token cookie
+    # Set access token cookie (align with token lifetime)
+    access_max_age = int(settings.SIMPLE_JWT.get("ACCESS_TOKEN_LIFETIME", timedelta(hours=12)).total_seconds())
     response.set_cookie(
         "access_token",
         token_data["access"],
         httponly=True,
         secure=secure_cookies,
         samesite="lax",
         path="/",
+        max_age=access_max_age,
     )
 
-    # Set refresh token cookie
+    # Set refresh token cookie (align with token lifetime)
+    refresh_max_age = int(settings.SIMPLE_JWT.get("REFRESH_TOKEN_LIFETIME", timedelta(days=30)).total_seconds())
     response.set_cookie(
         "refresh_token",
         token_data["refresh"],
         httponly=True,
         secure=secure_cookies,
         samesite="lax",
         path="/",
+        max_age=refresh_max_age,
     )

690-715: Avoid bare except/pass; log non-TokenError cases.

Catching Exception and passing hides real issues. Limit exceptions and log.

-    if refresh_token:
-        try:
-            token = RefreshToken(refresh_token)
-            token_user_id = token.payload.get("user_id")
-            if request.user and request.user.id == token_user_id:
-                token.blacklist()
-        except (TokenError, Exception):
-            # Token is already invalid or expired, continue with logout
-            pass
+    if refresh_token:
+        try:
+            token = RefreshToken(refresh_token)
+            token_user_id = token.payload.get("user_id")
+            if request.user and request.user.id == token_user_id:
+                token.blacklist()
+        except TokenError:
+            # Already invalid/expired; continue with logout
+            pass
+        except Exception as err:
+            logger.warning("Logout v2: unexpected error while blacklisting token", exc_info=err)

717-748: Refresh v2: also set max_age on the access_token cookie.

Keep cookie expiry aligned with the new access token.

-    response.set_cookie(
+    access_max_age = int(settings.SIMPLE_JWT.get("ACCESS_TOKEN_LIFETIME", timedelta(hours=12)).total_seconds())
+    response.set_cookie(
         "access_token",
         token_data["access"],
         httponly=True,
         secure=secure_cookies,
         samesite="lax",
         path="/",
+        max_age=access_max_age,
     )
ddpui/auth.py (1)

97-106: Use suffix match for /login_token/ path check
Replace the hard-coded prefix equality with a suffix check to handle any mounted prefix:

-        if request.path == "/api/login_token/":
+        if request.path.endswith("/login_token/"):
             logger.info("CustomJwtAuthMiddleware: prioritizing Authorization header for /login_token/")
             return super().__call__(request)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between cf26ada and a6b8525.

📒 Files selected for processing (3)
  • ddpui/api/user_org_api.py (4 hunks)
  • ddpui/auth.py (1 hunks)
  • ddpui/settings.py (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
ddpui/auth.py (1)
ddpui/utils/custom_logger.py (1)
  • info (25-29)
ddpui/api/user_org_api.py (7)
ddpui/models/org_user.py (3)
  • LoginPayload (206-210)
  • OrgUserResponse (123-135)
  • OrgUser (66-86)
ddpui/auth.py (3)
  • CustomTokenObtainSerializer (199-236)
  • CustomTokenRefreshSerializer (239-253)
  • get_token (203-232)
ddpui/core/orguserfunctions.py (1)
  • lookup_user (42-73)
ddpui/models/org.py (3)
  • Org (61-84)
  • OrgWarehouse (125-146)
  • base_plan (82-84)
ddpui/models/org_preferences.py (1)
  • OrgPreferences (7-39)
ddpui/models/dashboard.py (1)
  • Dashboard (48-157)
ddpui/models/role_based_access.py (2)
  • Role (5-14)
  • RolePermission (28-38)
🪛 Ruff (0.13.3)
ddpui/api/user_org_api.py

639-639: Unused function argument: request

(ARG001)


703-705: try-except-pass detected, consider logging the exception

(S110)


703-703: Do not catch blind exception: Exception

(BLE001)


752-752: Redefinition of unused get_current_user_v2 from line 65

(F811)


752-752: PEP 484 prohibits implicit Optional

Convert to T | None

(RUF013)

🔇 Additional comments (1)
ddpui/api/user_org_api.py (1)

175-176: Casting request.token to str is correct.

Ensures consistent token type in response payload.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
ddpui/settings.py (1)

128-140: Remove duplicate CommonMiddleware.

django.middleware.common.CommonMiddleware appears twice (lines 132 and 134), which will cause it to execute twice per request, adding unnecessary overhead and potentially causing unexpected behavior.

Apply this diff:

 MIDDLEWARE = [
     "django_prometheus.middleware.PrometheusBeforeMiddleware",
     "corsheaders.middleware.CorsMiddleware",
     "django.middleware.security.SecurityMiddleware",
-    "django.middleware.common.CommonMiddleware",
     "django.contrib.sessions.middleware.SessionMiddleware",
     "django.middleware.common.CommonMiddleware",
     "django.middleware.csrf.CsrfViewMiddleware",
     "django.contrib.auth.middleware.AuthenticationMiddleware",
     "django.contrib.messages.middleware.MessageMiddleware",
     "django.middleware.clickjacking.XFrameOptionsMiddleware",
     "django_prometheus.middleware.PrometheusAfterMiddleware",
 ]
🧹 Nitpick comments (2)
ddpui/api/user_org_api.py (2)

668-685: Consider setting cookie max_age to align with token lifetimes.

The cookies are set without max_age, making them session cookies that clear on browser close. However, the JWT tokens have longer lifetimes (12 hours for access, 30 days for refresh from settings.SIMPLE_JWT). This means users must re-login after closing their browser, even though their tokens remain valid.

If persistent login across browser sessions is desired, apply:

     # Set access token cookie
     response.set_cookie(
         "access_token",
         token_data["access"],
+        max_age=int(settings.SIMPLE_JWT["ACCESS_TOKEN_LIFETIME"].total_seconds()),
         httponly=True,
         secure=secure_cookies,
         samesite="lax",
         path="/",
     )
 
     # Set refresh token cookie
     response.set_cookie(
         "refresh_token",
         token_data["refresh"],
+        max_age=int(settings.SIMPLE_JWT["REFRESH_TOKEN_LIFETIME"].total_seconds()),
         httponly=True,
         secure=secure_cookies,
         samesite="lax",
         path="/",
     )

Alternatively, if session-only cookies are intentional for security, document this behavior and ensure frontend handles re-authentication gracefully.


698-705: Log exceptions instead of silently passing.

Catching all exceptions without logging can hide real issues during token blacklisting, making debugging difficult.

Apply this diff:

         try:
             token = RefreshToken(refresh_token)
             token_user_id = token.payload.get("user_id")
             if request.user and request.user.id == token_user_id:
                 token.blacklist()
-        except (TokenError, Exception):
+        except TokenError as e:
+            # Token is already invalid or expired, continue with logout
+            logger.info(f"Token already invalid during logout: {e}")
+        except Exception as e:
             # Token is already invalid or expired, continue with logout
-            pass
+            logger.warning(f"Unexpected error during token blacklist: {e}")
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a6b8525 and 7ded57d.

📒 Files selected for processing (2)
  • ddpui/api/user_org_api.py (4 hunks)
  • ddpui/settings.py (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
ddpui/api/user_org_api.py (3)
ddpui/models/org_user.py (2)
  • LoginPayload (206-210)
  • OrgUser (66-86)
ddpui/auth.py (4)
  • CustomTokenObtainSerializer (199-236)
  • CustomTokenRefreshSerializer (239-253)
  • has_permission (30-51)
  • get_token (203-232)
ddpui/core/orguserfunctions.py (1)
  • lookup_user (42-73)
🪛 Ruff (0.13.3)
ddpui/api/user_org_api.py

639-639: Unused function argument: request

(ARG001)


703-705: try-except-pass detected, consider logging the exception

(S110)


703-703: Do not catch blind exception: Exception

(BLE001)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: checks (3.10, 6)
🔇 Additional comments (3)
ddpui/api/user_org_api.py (3)

175-176: LGTM!

Stringifying the token ensures consistent string representation in the response, aligning with the v1 login endpoint behavior.


770-776: Verify intended iframe token lifetime.

The current implementation consistently uses 2 minutes for the iframe token (comment, code, and expires_in value). However, a previous review suggested changing this to 5 minutes. Please confirm which lifetime is correct for iframe tokens.

If 5 minutes is the intended lifetime, apply:

-    # Override access token expiration to 2 minutes for iframe use
-    access_token.set_exp(lifetime=timedelta(minutes=2))
+    # Override access token expiration to 5 minutes for iframe use
+    access_token.set_exp(lifetime=timedelta(minutes=5))
 
     return {
         "success": True,
         "iframe_token": str(access_token),
-        "expires_in": 120,  # 2 minutes in seconds
+        "expires_in": 300,  # 5 minutes in seconds
         "org_slug": orguser.org.slug,
     }

638-640: False positive: request parameter is used by the framework.

The static analysis tool flags request as unused (ARG001), but it's actively used by the authentication middleware to populate request.user and request.orguser (accessed at lines 759-763). This is a standard Django/django-ninja pattern.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
ddpui/settings.py (1)

132-134: Duplicate CommonMiddleware and non-standard order

CommonMiddleware appears twice (Line 132 and Line 134). Also, recommended order is SecurityMiddleware → SessionMiddleware → CommonMiddleware. Apply:

 MIDDLEWARE = [
     "django_prometheus.middleware.PrometheusBeforeMiddleware",
     "corsheaders.middleware.CorsMiddleware",
     "django.middleware.security.SecurityMiddleware",
-    "django.middleware.common.CommonMiddleware",
-    "django.contrib.sessions.middleware.SessionMiddleware",
-    "django.middleware.common.CommonMiddleware",
+    "django.contrib.sessions.middleware.SessionMiddleware",
+    "django.middleware.common.CommonMiddleware",
     "django.middleware.csrf.CsrfViewMiddleware",
     "django.contrib.auth.middleware.AuthenticationMiddleware",
     "django.contrib.messages.middleware.MessageMiddleware",
     "django.middleware.clickjacking.XFrameOptionsMiddleware",
     "django_prometheus.middleware.PrometheusAfterMiddleware",
 ]
🧹 Nitpick comments (5)
ddpui/api/user_org_api.py (5)

639-639: Lint: unused request parameter

Rename to _request or add a small use (e.g., del request) to satisfy ARG001.


707-713: Don’t swallow exceptions silently; log and narrow catch

The bare except Exception: pass hides useful context. Limit to TokenError and log unexpected errors:

-    if refresh_token:
-        try:
-            token = RefreshToken(refresh_token)
-            token_user_id = token.payload.get("user_id")
-            if request.user and request.user.id == token_user_id:
-                token.blacklist()
-        except (TokenError, Exception):
-            # Token is already invalid or expired, continue with logout
-            pass
+    if refresh_token:
+        try:
+            token = RefreshToken(refresh_token)
+            token_user_id = token.payload.get("user_id")
+            if request.user and request.user.id == token_user_id:
+                token.blacklist()
+        except TokenError:
+            # Already invalid/expired; continue
+            logger.debug("Refresh token invalid/expired during logout")
+        except Exception as err:
+            logger.warning(f"Unexpected error blacklisting refresh token: {err}")

717-747: CSRF also applies to refresh; ensure header/cookie flow

POST /v2/token/refresh will also require X-CSRFToken under CsrfViewMiddleware. After adding a CSRF bootstrap endpoint, document that clients must send the header here too. Cookie setting looks fine; rotation of refresh isn’t attempted, which is acceptable given your serializer behavior.


752-752: PEP 604 typing for optional param

Use str | None for Optional per modern typing:

-def get_currentuser_v2(request, org_slug: str = None):
+def get_currentuser_v2(request, org_slug: str | None = None):

844-851: Iframe token TTL is consistent (2 minutes)

Comment and expires_in now align. Consider making TTL configurable via env for flexibility.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a6b8525 and 5fb2702.

📒 Files selected for processing (2)
  • ddpui/api/user_org_api.py (4 hunks)
  • ddpui/settings.py (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
ddpui/api/user_org_api.py (7)
ddpui/models/org_user.py (3)
  • LoginPayload (206-210)
  • OrgUserResponse (123-135)
  • OrgUser (66-86)
ddpui/auth.py (3)
  • CustomTokenObtainSerializer (199-236)
  • CustomTokenRefreshSerializer (239-253)
  • get_token (203-232)
ddpui/core/orguserfunctions.py (1)
  • lookup_user (42-73)
ddpui/models/org.py (4)
  • Org (61-84)
  • OrgWarehouse (125-146)
  • base_plan (82-84)
  • OrgType (8-18)
ddpui/models/org_preferences.py (1)
  • OrgPreferences (7-39)
ddpui/models/dashboard.py (1)
  • Dashboard (48-157)
ddpui/models/role_based_access.py (2)
  • Role (5-14)
  • RolePermission (28-38)
🪛 Ruff (0.13.3)
ddpui/api/user_org_api.py

639-639: Unused function argument: request

(ARG001)


703-705: try-except-pass detected, consider logging the exception

(S110)


703-703: Do not catch blind exception: Exception

(BLE001)


752-752: PEP 484 prohibits implicit Optional

Convert to T | None

(RUF013)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: checks (3.10, 6)
🔇 Additional comments (2)
ddpui/settings.py (2)

78-91: Good move to origin-allowlist and credentials enabled

Explicit CORS_ALLOWED_ORIGINS with CORS_ALLOW_CREDENTIALS=True is required for cookie auth. Looks correct.


100-103: CSRF settings align with cookie-based flow

Trusting explicit origins and allowing X-CSRFToken header is right. Tuple merge with default_headers is fine.

Please confirm frontend sends the X-CSRFToken header on unsafe requests, and that a CSRF cookie is issued prior to login (e.g., via an “ensure CSRF” GET endpoint).

Copy link
Contributor

@pradnk pradnk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you are taking the critical request from Coderabbit then I am good

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
ddpui/api/user_org_api.py (2)

700-731: Refresh endpoint: add max_age and consider CSRF bootstrap

  • Set access_token cookie max_age to ACCESS_TOKEN_LIFETIME, as above.
  • With CsrfViewMiddleware, POST to cookie-auth endpoints needs a CSRF cookie + X-CSRFToken; add a GET /v2/csrf/ bootstrap (see below).

Apply:

     # Determine if we should use secure cookies
     secure_cookies = not settings.DEBUG
+
+    # Align cookie lifetime with SIMPLE_JWT settings if available
+    cfg = getattr(settings, "SIMPLE_JWT", {})
+    access_max_age = int(cfg["ACCESS_TOKEN_LIFETIME"].total_seconds()) if cfg.get("ACCESS_TOKEN_LIFETIME") else None

     # Set new access token cookie
     response.set_cookie(
         "access_token",
         token_data["access"],
         httponly=True,
         secure=secure_cookies,
-        samesite="lax",
+        samesite="lax",
         path="/",
+        max_age=access_max_age,
     )

Add CSRF bootstrap endpoint (same module):

from django.views.decorators.csrf import ensure_csrf_cookie

@user_org_router.get("/v2/csrf/", auth=None)
@ensure_csrf_cookie
def get_csrf(_request):
    return JsonResponse({"success": True})

663-669: Response fields don’t match lookup_user; return all user fields

org_users and wtype aren’t provided by lookup_user, while real fields (email_verified, active, can_create_orgs, is_consultant, is_platform_admin) are omitted. Include the full payload.

Apply:

-    # Don't include tokens in response body
-    response_data = {
-        "success": True,
-        "email": retval.get("email"),
-        "org_users": retval.get("org_users"),
-        "wtype": retval.get("wtype"),
-    }
+    # Don't include tokens in response body; include all user data
+    response_data = {
+        "success": True,
+        **retval,
+    }
🧹 Nitpick comments (2)
ddpui/api/user_org_api.py (2)

648-649: Silence ARG001: unused request param

Ruff flags unused request. Since it’s not used, rename to _request (consistent with other endpoints using pylint disables).

Apply:

-@user_org_router.post("/v2/login/", auth=None, response={200: dict})
-def post_login_v2(request, payload: LoginPayload):
+@user_org_router.post("/v2/login/", auth=None, response={200: dict})
+def post_login_v2(_request, payload: LoginPayload):

677-695: Harden cookie settings: align lifetimes and verify SameSite

  • Set cookie max_age to JWT lifetimes so browser eviction aligns with token TTLs.
  • Verify samesite="lax" works with your frontend origin; cross-site XHR generally requires samesite="none" and secure=True.

Apply:

     # Create JsonResponse and set cookies
     response = JsonResponse(response_data)

     # Determine if we should use secure cookies (HTTPS only in production)
     secure_cookies = not settings.DEBUG
+
+    # Align cookie lifetimes with SIMPLE_JWT settings if available
+    cfg = getattr(settings, "SIMPLE_JWT", {})
+    access_max_age = int(cfg["ACCESS_TOKEN_LIFETIME"].total_seconds()) if cfg.get("ACCESS_TOKEN_LIFETIME") else None
+    refresh_max_age = int(cfg["REFRESH_TOKEN_LIFETIME"].total_seconds()) if cfg.get("REFRESH_TOKEN_LIFETIME") else None

     # Set access token cookie
     response.set_cookie(
         "access_token",
         token_data["access"],
         httponly=True,
         secure=secure_cookies,
-        samesite="lax",
+        samesite="lax",
         path="/",
+        max_age=access_max_age,
     )

     # Set refresh token cookie
     response.set_cookie(
         "refresh_token",
         token_data["refresh"],
         httponly=True,
         secure=secure_cookies,
-        samesite="lax",
+        samesite="lax",
         path="/",
+        max_age=refresh_max_age,
     )

To verify SameSite needs with your deployment, confirm whether the frontend origin differs from the API origin and if cross-site XHR is used.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 5fb2702 and 6f0e6d4.

📒 Files selected for processing (1)
  • ddpui/api/user_org_api.py (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
ddpui/api/user_org_api.py (3)
ddpui/models/org_user.py (3)
  • LogoutPayload (213-216)
  • LoginPayload (206-210)
  • OrgUser (66-86)
ddpui/auth.py (4)
  • CustomTokenObtainSerializer (199-236)
  • CustomTokenRefreshSerializer (239-253)
  • has_permission (30-51)
  • get_token (203-232)
ddpui/core/orguserfunctions.py (1)
  • lookup_user (42-73)
🪛 Ruff (0.14.0)
ddpui/api/user_org_api.py

202-204: try-except-pass detected, consider logging the exception

(S110)


202-202: Do not catch blind exception: Exception

(BLE001)


649-649: Unused function argument: request

(ARG001)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: checks (3.10, 6)
🔇 Additional comments (2)
ddpui/api/user_org_api.py (2)

175-176: Good fix: ensure token is a string

Casting request.token to str prevents serialization issues. LGTM.


753-760: Iframe token TTL consistent

Comment, token lifetime, and expires_in all set to 2 minutes. LGTM.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (3)
ddpui/api/user_org_api.py (3)

186-208: Don’t swallow logout errors; log and narrow exceptions. Also align delete_cookie attrs

Catching Exception and passing masks real issues; log and split excepts. Ensure delete_cookie uses the same samesite as when set.

-    if refresh_token:
-        try:
-            token = RefreshToken(refresh_token)
-            token_user_id = token.payload.get("user_id")
-            if request.user and request.user.id == token_user_id:
-                token.blacklist()
-        except (TokenError, Exception):
-            # Token is already invalid/expired or other error, continue with logout
-            pass
+    if refresh_token:
+        try:
+            token = RefreshToken(refresh_token)
+            token_user_id = token.payload.get("user_id")
+            if request.user and request.user.id == token_user_id:
+                token.blacklist()
+        except TokenError as exc:
+            # Token already invalid/expired; continue
+            logger.info("post_logout: refresh token invalid/expired: %s", exc)
+        except Exception:
+            # Unexpected error; continue but record it
+            logger.exception("post_logout: unexpected error during token blacklist")

Tip: if cookies were set with samesite="None", delete_cookie(samesite="lax") won’t match. Use the same samesite value for deletion.


638-646: CSRF will 403 on POST /v2/login without a prior CSRF cookie

With CsrfViewMiddleware, clients need a CSRF cookie + X-CSRFToken. Add a GET /v2/csrf/ endpoint to issue the cookie (preferred), or exempt login (less secure).

from django.views.decorators.csrf import ensure_csrf_cookie

@user_org_router.get("/v2/csrf/", auth=None)
@ensure_csrf_cookie
def get_csrf(_request):
    return JsonResponse({"success": True})

653-659: Response fields don’t match lookup_user; returns incomplete/incorrect data

lookup_user returns email, email_verified, active, can_create_orgs, is_consultant, is_platform_admin. You’re returning org_users/wtype which aren’t present.

-    response_data = {
-        "success": True,
-        "email": retval.get("email"),
-        "org_users": retval.get("org_users"),
-        "wtype": retval.get("wtype"),
-    }
+    response_data = {
+        "success": True,
+        **retval,  # include all fields from lookup_user
+    }
🧹 Nitpick comments (4)
ddpui/api/user_org_api.py (4)

15-15: Remove unused AccessToken import

AccessToken isn’t used; drop it to appease linters.

-from rest_framework_simplejwt.tokens import RefreshToken, TokenError, AccessToken
+from rest_framework_simplejwt.tokens import RefreshToken, TokenError

664-685: Cookie attributes: consider settings-driven samesite/secure and add max_age

SameSite=lax may block set-cookie/send on cross-site flows; many SPAs require SameSite=None; also align cookie lifetime with token TTL.

-    # Determine if we should use secure cookies (HTTPS only in production)
-    secure_cookies = not settings.DEBUG
+    # Use settings to drive cookie attributes
+    secure_cookies = getattr(settings, "SESSION_COOKIE_SECURE", not settings.DEBUG)
+    samesite = getattr(settings, "SESSION_COOKIE_SAMESITE", "lax")  # set to "None" for cross-site
+    # Optional: align cookie lifetime with SIMPLE_JWT
+    sj = getattr(settings, "SIMPLE_JWT", {})
+    access_max_age = int(sj.get("ACCESS_TOKEN_LIFETIME", timedelta(minutes=5)).total_seconds())
+    refresh_max_age = int(sj.get("REFRESH_TOKEN_LIFETIME", timedelta(days=1)).total_seconds())

     # Set access token cookie
     response.set_cookie(
         "access_token",
         token_data["access"],
         httponly=True,
-        secure=secure_cookies,
-        samesite="lax",
+        secure=secure_cookies,
+        samesite=samesite,
+        max_age=access_max_age,
         path="/",
     )

     # Set refresh token cookie
     response.set_cookie(
         "refresh_token",
         token_data["refresh"],
         httponly=True,
-        secure=secure_cookies,
-        samesite="lax",
+        secure=secure_cookies,
+        samesite=samesite,
+        max_age=refresh_max_age,
         path="/",
     )

Please confirm your deployment is same-site (Lax OK). If cross-site, switch to SameSite=None and ensure HTTPS.


639-639: Unused parameter in handler

Rename to _request or silence linter; framework still injects the request.

-def post_login_v2(request, payload: LoginPayload):
+def post_login_v2(_request, payload: LoginPayload):  # pylint: disable=unused-argument

710-718: Mirror cookie attrs and TTLs on refresh response

Use the same samesite/secure and add max_age for access cookie to match SIMPLE_JWT ACCESS_TOKEN_LIFETIME.

-    # Determine if we should use secure cookies
-    secure_cookies = not settings.DEBUG
+    # Mirror login cookie settings
+    secure_cookies = getattr(settings, "SESSION_COOKIE_SECURE", not settings.DEBUG)
+    samesite = getattr(settings, "SESSION_COOKIE_SAMESITE", "lax")
+    sj = getattr(settings, "SIMPLE_JWT", {})
+    access_max_age = int(sj.get("ACCESS_TOKEN_LIFETIME", timedelta(minutes=5)).total_seconds())

     # Set new access token cookie
     response.set_cookie(
         "access_token",
         token_data["access"],
         httponly=True,
-        secure=secure_cookies,
-        samesite="lax",
+        secure=secure_cookies,
+        samesite=samesite,
+        max_age=access_max_age,
         path="/",
     )
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 6f0e6d4 and 43742dd.

📒 Files selected for processing (1)
  • ddpui/api/user_org_api.py (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
ddpui/api/user_org_api.py (3)
ddpui/models/org_user.py (2)
  • LoginPayload (206-210)
  • OrgUser (66-86)
ddpui/auth.py (3)
  • CustomTokenObtainSerializer (199-236)
  • CustomTokenRefreshSerializer (239-253)
  • get_token (203-232)
ddpui/core/orguserfunctions.py (1)
  • lookup_user (42-73)
🪛 Ruff (0.14.0)
ddpui/api/user_org_api.py

196-198: try-except-pass detected, consider logging the exception

(S110)


196-196: Do not catch blind exception: Exception

(BLE001)


639-639: Unused function argument: request

(ARG001)

🔇 Additional comments (2)
ddpui/api/user_org_api.py (2)

174-176: Good fix: ensure token is string

Coercing request.token to str avoids serialization issues.


743-750: Iframe token TTL is consistent

2-minute lifetime matches comment and expires_in. Looks good.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
ddpui/settings.py (2)

77-99: Add missing CSRF settings for cookie-based authentication.

While CORS is correctly configured for credentials, the CSRF protection settings are still missing. Django's CsrfViewMiddleware (enabled at line 131) requires these settings to safely handle cross-origin POST requests with cookies.

Add after line 90:

 ]
+
+# CSRF settings for cookie-based authentication
+CSRF_TRUSTED_ORIGINS = CORS_ALLOWED_ORIGINS
+CSRF_COOKIE_SECURE = not DEBUG
+CSRF_COOKIE_SAMESITE = "Lax"
+CSRF_COOKIE_HTTPONLY = False  # Must be False so JavaScript can read the token

Note: CSRF_COOKIE_HTTPONLY must be False because the frontend needs to read the CSRF token from the cookie and send it in the X-CSRFToken header. This is different from the session cookies (access_token, refresh_token) which should remain httpOnly.

Based on past review comments.


124-136: Remove duplicate CommonMiddleware entry.

CommonMiddleware appears twice (lines 128 and 130), which will cause it to run twice per request.

Apply this diff to remove the duplicate at line 128:

 MIDDLEWARE = [
     "django_prometheus.middleware.PrometheusBeforeMiddleware",
     "corsheaders.middleware.CorsMiddleware",
     "django.middleware.security.SecurityMiddleware",
-    "django.middleware.common.CommonMiddleware",
     "django.contrib.sessions.middleware.SessionMiddleware",
     "django.middleware.common.CommonMiddleware",
     "django.middleware.csrf.CsrfViewMiddleware",
     "django.contrib.auth.middleware.AuthenticationMiddleware",
     "django.contrib.messages.middleware.MessageMiddleware",
     "django.middleware.clickjacking.XFrameOptionsMiddleware",
     "django_prometheus.middleware.PrometheusAfterMiddleware",
 ]

Based on past review comments.

♻️ Duplicate comments (2)
ddpui/api/user_org_api.py (2)

662-668: Fix response data to match lookup_user return values.

The response references org_users and wtype fields that are not returned by orguserfunctions.lookup_user(). According to the code snippet from ddpui/core/orguserfunctions.py (lines 41-72), lookup_user returns: email, email_verified, active, can_create_orgs, is_consultant, is_platform_admin.

This will cause a runtime error when accessing these non-existent fields.

Apply this diff to return the correct fields:

     # Don't include tokens in response body
     response_data = {
         "success": True,
-        "email": retval.get("email"),
-        "org_users": retval.get("org_users"),
-        "wtype": retval.get("wtype"),
+        **retval,  # Include all fields from lookup_user
     }

Based on past review comments.


199-207: Log exceptions instead of silently swallowing them.

Catching all exceptions with pass masks real errors and makes debugging difficult. The refresh token might fail for expected reasons (invalid/expired) or unexpected errors (database issues, etc.).

Apply this diff to add proper logging:

+import logging
+
+logger_auth = logging.getLogger(__name__)
+
     # Try to blacklist the refresh token if we have one
     if refresh_token:
         try:
             token = RefreshToken(refresh_token)
             token_user_id = token.payload.get("user_id")
             if request.user and request.user.id == token_user_id:
                 token.blacklist()
-        except (TokenError, Exception):
-            # Token is already invalid/expired or other error, continue with logout
-            pass
+        except TokenError as exc:
+            # Token already invalid/expired; continue with logout
+            logger_auth.info("post_logout: refresh token invalid/expired: %s", exc)
+        except Exception:
+            # Unexpected error; log but continue with logout
+            logger_auth.exception("post_logout: unexpected error during token blacklist")

Based on past review comments and static analysis hints (S110, BLE001).

🧹 Nitpick comments (3)
ddpui/settings.py (1)

294-297: Consider renaming for clarity and note the CSRF cookie distinction.

These settings control application cookies (access_token, refresh_token), but the naming could be clearer.

Consider renaming to make the purpose explicit:

-# Cookie settings
-COOKIE_SECURE = os.getenv("ENVIRONMENT", "") in ["production", "staging"]
-COOKIE_SAMESITE = "Lax"
-COOKIE_HTTPONLY = True
+# Application cookie settings (for access_token and refresh_token)
+SESSION_COOKIE_SECURE = os.getenv("ENVIRONMENT", "") in ["production", "staging"]
+SESSION_COOKIE_SAMESITE = "Lax"
+SESSION_COOKIE_HTTPONLY = True

Then update references in ddpui/api/user_org_api.py to use these renamed settings (e.g., settings.SESSION_COOKIE_SECURE instead of settings.COOKIE_SECURE).

Important: Keep these separate from CSRF cookie settings. CSRF cookies must have httponly=False so JavaScript can read the token, while session cookies should remain httponly=True for security.

ddpui/api/user_org_api.py (2)

674-674: Remove unused variable.

The variable secure_cookies is assigned but never used.

Apply this diff:

-    # Determine if we should use secure cookies (HTTPS only in production)
-    secure_cookies = not settings.DEBUG
-
     # Set access token cookie

Based on static analysis (F841).


716-718: Remove unused variable.

The variable secure_cookies is assigned but never used.

Apply this diff:

-    # Determine if we should use secure cookies
-    secure_cookies = not settings.DEBUG
-
     # Set new access token cookie

Based on static analysis (F841).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between fab4331 and bea7a07.

📒 Files selected for processing (2)
  • ddpui/api/user_org_api.py (4 hunks)
  • ddpui/settings.py (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
ddpui/api/user_org_api.py (2)
ddpui/auth.py (3)
  • CustomTokenObtainSerializer (199-236)
  • get_token (203-232)
  • CustomTokenRefreshSerializer (239-253)
ddpui/core/orguserfunctions.py (1)
  • lookup_user (42-73)
🪛 Ruff (0.14.0)
ddpui/api/user_org_api.py

205-207: try-except-pass detected, consider logging the exception

(S110)


205-205: Do not catch blind exception: Exception

(BLE001)


648-648: Unused function argument: request

(ARG001)


674-674: Local variable secure_cookies is assigned to but never used

Remove assignment to unused variable secure_cookies

(F841)


717-717: Local variable secure_cookies is assigned to but never used

Remove assignment to unused variable secure_cookies

(F841)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: checks (3.10, 6)
🔇 Additional comments (1)
ddpui/api/user_org_api.py (1)

752-758: Verify comment matches implementation.

The implementation sets a 2-minute expiration (line 753) with expires_in: 120 seconds (line 758), which is consistent. However, if the comment at line 752 still mentions "5 minutes" (as noted in past reviews), it should be updated to match the implementation.

If the comment says "5 minutes", update it:

-    # Override access token expiration to 5 minutes for iframe use
+    # Override access token expiration to 2 minutes for iframe use
     access_token.set_exp(lifetime=timedelta(minutes=2))

Based on past review comments.

Comment on lines 647 to 696
@user_org_router.post("/v2/login/", auth=None, response={200: dict})
def post_login_v2(request, payload: LoginPayload):
"""Login endpoint that sets httpOnly cookies instead of returning tokens in response"""
serializer = CustomTokenObtainSerializer(
data={
"username": payload.username,
"password": payload.password,
}
)
serializer.is_valid(raise_exception=True)
token_data = serializer.validated_data

# Get user data (same as v1)
retval = orguserfunctions.lookup_user(payload.username)

# Don't include tokens in response body
response_data = {
"success": True,
"email": retval.get("email"),
"org_users": retval.get("org_users"),
"wtype": retval.get("wtype"),
}

# Create JsonResponse and set cookies
response = JsonResponse(response_data)

# Determine if we should use secure cookies (HTTPS only in production)
secure_cookies = not settings.DEBUG

# Set access token cookie
response.set_cookie(
"access_token",
token_data["access"],
httponly=settings.COOKIE_HTTPONLY,
secure=settings.COOKIE_SECURE,
samesite=settings.COOKIE_SAMESITE,
path="/",
)

# Set refresh token cookie
response.set_cookie(
"refresh_token",
token_data["refresh"],
httponly=settings.COOKIE_HTTPONLY,
secure=settings.COOKIE_SECURE,
samesite=settings.COOKIE_SAMESITE,
path="/",
)

return response
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add CSRF protection handling for v2 login endpoint.

The POST /v2/login/ endpoint has auth=None (line 647), but Django's CsrfViewMiddleware is enabled (settings.py line 131). This means:

  1. Current state: The endpoint will reject POST requests without a valid CSRF token, which clients won't have before logging in.
  2. Solution options:
    • Option A (Recommended): Add a public CSRF token endpoint that clients call before login
    • Option B: Exempt the login endpoint with @csrf_exempt (less secure but acceptable for login endpoints)

Recommended approach (Option A):

Add a CSRF bootstrap endpoint:

from django.views.decorators.csrf import ensure_csrf_cookie

@user_org_router.get("/v2/csrf/", auth=None)
@ensure_csrf_cookie
def get_csrf_token(request):
    """Bootstrap CSRF token for cookie-based authentication"""
    return JsonResponse({"success": True})

Then update client flow to:

  1. GET /v2/csrf/ to receive CSRF cookie
  2. POST /v2/login/ with X-CSRFToken header

Alternative approach (Option B):

from django.views.decorators.csrf import csrf_exempt

@user_org_router.post("/v2/login/", auth=None, response={200: dict})
@csrf_exempt
def post_login_v2(request, payload: LoginPayload):
    # ... existing code

Based on past review comments.

🧰 Tools
🪛 Ruff (0.14.0)

648-648: Unused function argument: request

(ARG001)


674-674: Local variable secure_cookies is assigned to but never used

Remove assignment to unused variable secure_cookies

(F841)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
ddpui/api/user_org_api.py (2)

202-207: Log and narrow exception handling.

This broad exception handling (catching both TokenError and Exception with pass) masks real errors and was flagged in a previous review but remains unaddressed.

Apply this diff to log exceptions while preserving logout behavior:

-        try:
-            token = RefreshToken(refresh_token)
-            token_user_id = token.payload.get("user_id")
-            if request.user and request.user.id == token_user_id:
-                token.blacklist()
-        except (TokenError, Exception):
-            # Token is already invalid/expired or other error, continue with logout
-            pass
+        try:
+            token = RefreshToken(refresh_token)
+            token_user_id = token.payload.get("user_id")
+            if request.user and request.user.id == token_user_id:
+                token.blacklist()
+        except TokenError as exc:
+            # Token already invalid/expired; continue with logout
+            logger.info("post_logout: refresh token invalid/expired: %s", exc)
+        except Exception:
+            # Unexpected error; continue but record it
+            logger.exception("post_logout: unexpected error during token blacklist")

Based on past review comments.


647-685: CSRF protection required for v2 login endpoint.

This POST endpoint with auth=None will be rejected by Django's CsrfViewMiddleware (enabled in settings). Clients won't have a CSRF token before logging in. This critical issue was flagged in a previous review but remains unaddressed.

Recommended approach (Option A): Add a CSRF bootstrap endpoint:

from django.views.decorators.csrf import ensure_csrf_cookie

@user_org_router.get("/v2/csrf/", auth=None)
@ensure_csrf_cookie
def get_csrf_token(request):
    """Bootstrap CSRF token for cookie-based authentication"""
    return JsonResponse({"success": True})

Then update client flow to:

  1. GET /v2/csrf/ to receive CSRF cookie
  2. POST /v2/login/ with X-CSRFToken header

Alternative approach (Option B): Exempt the endpoint:

from django.views.decorators.csrf import csrf_exempt

@user_org_router.post("/v2/login/", auth=None)
@csrf_exempt
def post_login_v2(request, payload: LoginPayload):
    # ... existing code

Based on past review comments.

🧹 Nitpick comments (2)
ddpui/api/user_org_api.py (2)

648-648: Remove unused request parameter or use it for logging.

The request parameter is unused. If you don't need it for logging or other purposes, remove it from the signature.

-def post_login_v2(request, payload: LoginPayload):
+def post_login_v2(payload: LoginPayload):

706-706: Remove unused variable.

The secure_cookies variable is assigned but never used.

-    # Determine if we should use secure cookies
-    secure_cookies = not settings.DEBUG
-
     # Set new access token cookie
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between bea7a07 and a4ce9f0.

📒 Files selected for processing (1)
  • ddpui/api/user_org_api.py (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
ddpui/api/user_org_api.py (3)
ddpui/auth.py (3)
  • CustomTokenObtainSerializer (199-236)
  • get_token (203-232)
  • CustomTokenRefreshSerializer (239-253)
ddpui/core/orguserfunctions.py (1)
  • lookup_user (42-73)
ddpui/models/org_user.py (2)
  • LoginPayload (206-210)
  • OrgUser (66-86)
🪛 Ruff (0.14.0)
ddpui/api/user_org_api.py

205-207: try-except-pass detected, consider logging the exception

(S110)


205-205: Do not catch blind exception: Exception

(BLE001)


648-648: Unused function argument: request

(ARG001)


706-706: Local variable secure_cookies is assigned to but never used

Remove assignment to unused variable secure_cookies

(F841)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: checks (3.10, 6)
🔇 Additional comments (2)
ddpui/api/user_org_api.py (2)

167-186: LGTM!

The token generation logic correctly creates a new session token with standard expiry. The use of CustomTokenObtainSerializer.get_token() ensures custom claims (like orguser_role_key) are included in the token.


721-749: LGTM!

The iframe token implementation correctly:

  • Generates a short-lived token (2 minutes) for iframe communication
  • Uses the same token generation logic as login to include custom claims
  • Returns consistent expiry information (120 seconds matches 2-minute lifetime)
  • Includes org_slug for context

@Ishankoradia Ishankoradia merged commit b79234d into main Oct 14, 2025
1 of 5 checks passed
@Ishankoradia Ishankoradia deleted the feature/cookies branch October 14, 2025 11:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants