Skip to content

fix: correct decorator order in prompt-service so auth_required is enforced#1964

Open
alpakalee wants to merge 1 commit into
Zipstack:mainfrom
alpakalee:fix/prompt-service-auth-decorator-order
Open

fix: correct decorator order in prompt-service so auth_required is enforced#1964
alpakalee wants to merge 1 commit into
Zipstack:mainfrom
alpakalee:fix/prompt-service-auth-decorator-order

Conversation

@alpakalee
Copy link
Copy Markdown

What

Swaps the @auth_required / @route decorator order in three prompt-service controllers
so that Flask registers the authenticated wrapper, not the raw handler function.

Why

Python applies stacked decorators bottom-up. The previous order placed @auth_required
on top:

# Before (buggy)
@AuthHelper.auth_required      # applied 2nd — only updates the module-level name
@extraction_bp.route("/extract", methods=["POST"])   # applied 1st — Flask stores raw fn
def extract():

Flask finalises its URL map at import time when @route is applied. @auth_required is
applied afterward, updating only the module-level variable — the URL map already holds the
raw function. As a result, the auth wrapper is never invoked on any incoming request.

Correct order (route first, then auth):

# After (fixed)
@extraction_bp.route("/extract", methods=["POST"])
@AuthHelper.auth_required
def extract():

How

File Change
controllers/extraction.py swap decorator order
controllers/indexing.py swap decorator order
controllers/answer_prompt.py swap decorator order

Can this PR break any existing features. If yes, please list possible items. If no, please explain why.

No. The same auth logic is applied — this fix makes it actually run for the first time.
No behavioural changes beyond enforcing the token check that was already implemented.

Database Migrations

N/A

Env Config

N/A

Relevant Docs

N/A

Related Issues or PRs

N/A

Dependencies Versions

N/A

Notes on Testing

# Before fix: missing Authorization header returns 400/500 (handler-internal error)
# After fix:
curl -s -o /dev/null -w "%{http_code}" \
  -X POST http://localhost:3003/answer-prompt \
  -H 'Content-Type: application/json' -d '{}'
# Expected: 401

platform-service and x2text-service in this repo already use the correct order
(@route above auth). This change brings prompt-service in line with the same pattern.

Checklist

I have read and understood the Contribution Guidelines.

…forced

Python applies decorators bottom-up. Placing @auth_required above @route
caused Flask to register the raw handler function — the auth wrapper was
never invoked on incoming requests. Swap the order in all three affected
controllers so the authenticated wrapper is what Flask stores in its URL map.

Affected files:
- controllers/extraction.py (POST /extract)
- controllers/indexing.py   (POST /index)
- controllers/answer_prompt.py (POST /answer-prompt)
@CLAassistant
Copy link
Copy Markdown

CLAassistant commented May 13, 2026

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 13, 2026

Summary by CodeRabbit

  • Refactor
    • Internal code organization improvements across API controllers.

Walkthrough

Three Flask controller routes have their decorators reordered: the route decorator (@xxx_bp.route(...)) is moved before the @AuthHelper.auth_required decorator in answer_prompt.py, extraction.py, and indexing.py. Endpoint logic and signatures are unchanged.

Changes

Flask Route Decorator Order

Layer / File(s) Summary
Reorder route and auth decorators
prompt-service/src/unstract/prompt_service/controllers/answer_prompt.py, prompt-service/src/unstract/prompt_service/controllers/extraction.py, prompt-service/src/unstract/prompt_service/controllers/indexing.py
Route decorators (@xxx_bp.route(...)) now appear before @AuthHelper.auth_required instead of after, changing the order in which decorators wrap the endpoint functions. No handler logic changes.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~2 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: correcting decorator order in prompt-service controllers to enforce authentication, which is exactly what the changeset accomplishes.
Description check ✅ Passed The description covers all required template sections with comprehensive detail: What (decorator swap), Why (decorator application order), How (file table), feature impact, testing notes, and proper checklist confirmation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

@sonarqubecloud
Copy link
Copy Markdown

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 13, 2026

Greptile Summary

This PR fixes a long-standing security hole in prompt-service where @AuthHelper.auth_required was stacked above @blueprint.route(...), causing Flask to bind the raw (unauthenticated) handler to the URL map while the auth wrapper was silently discarded. Swapping the order to place @route outermost ensures the authenticated wrapper is what Flask registers.

  • All three controllers (extraction.py, indexing.py, answer_prompt.py) receive the same two-line decorator swap; no logic changes.
  • The underlying AuthHelper.auth_required decorator in helpers/auth.py is missing @functools.wraps(func), which means Flask now registers these endpoints under the name \"wrapper\" instead of the original function name — safe today since each blueprint has one route, but a latent crash risk if a second auth-guarded route is ever added to the same blueprint.

Confidence Score: 4/5

The decorator swap correctly enforces authentication on all three endpoints for the first time; the only follow-up needed is adding @functools.wraps(func) to AuthHelper.auth_required in helpers/auth.py to preserve Flask endpoint names.

The core change is correct and closes a real authentication bypass. The companion issue — auth_required not using functools.wraps — means Flask now stores these endpoints under the name wrapper, which is harmless today but will cause a startup crash if a second auth-guarded route is ever added to any of these blueprints.

helpers/auth.py (not in the diff) needs @functools.wraps(func) added to the wrapper inside auth_required to complete the fix safely.

Important Files Changed

Filename Overview
prompt-service/src/unstract/prompt_service/controllers/extraction.py Decorator order corrected so Flask registers the auth wrapper; endpoint is now named wrapper due to missing functools.wraps in auth_required.
prompt-service/src/unstract/prompt_service/controllers/indexing.py Decorator order corrected; same functools.wraps concern applies as in extraction.py.
prompt-service/src/unstract/prompt_service/controllers/answer_prompt.py Decorator order corrected; same functools.wraps concern applies as in extraction.py.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Incoming POST request] --> B{Flask URL Map}
    B --> C[auth_required wrapper]
    C --> D{Token present and valid?}
    D -- No --> E[Return 401]
    D -- Yes --> F[Original route handler]
    F --> G[Return response]

    style E fill:#f66,color:#fff
    style G fill:#6a6,color:#fff
Loading

Fix All in Claude Code

Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
prompt-service/src/unstract/prompt_service/controllers/extraction.py:21-23
**Missing `functools.wraps` in `auth_required` changes Flask endpoint names**

Now that `@route` is applied after `@AuthHelper.auth_required`, Flask registers the inner `wrapper` function (returned by `auth_required`) rather than the original handler. Because `auth.py`'s `auth_required` does not call `@functools.wraps(func)` on its wrapper, the three endpoints are now registered under the endpoint name `"wrapper"` instead of `"extract"`, `"index"`, and `"prompt_processor"`. Each lives in a separate blueprint so there is no immediate collision here, but if a second `@auth_required`-decorated route is ever added to any of these blueprints the app will crash at startup with a duplicate-endpoint `AssertionError`. Adding `@functools.wraps(func)` to the `wrapper` inside `AuthHelper.auth_required` in `helpers/auth.py` fixes this for all three controllers at once.

Reviews (1): Last reviewed commit: "fix: correct decorator order in prompt-s..." | Re-trigger Greptile

Comment on lines 21 to 23
@extraction_bp.route("/extract", methods=["POST"])
@AuthHelper.auth_required
def extract() -> Any:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Missing functools.wraps in auth_required changes Flask endpoint names

Now that @route is applied after @AuthHelper.auth_required, Flask registers the inner wrapper function (returned by auth_required) rather than the original handler. Because auth.py's auth_required does not call @functools.wraps(func) on its wrapper, the three endpoints are now registered under the endpoint name "wrapper" instead of "extract", "index", and "prompt_processor". Each lives in a separate blueprint so there is no immediate collision here, but if a second @auth_required-decorated route is ever added to any of these blueprints the app will crash at startup with a duplicate-endpoint AssertionError. Adding @functools.wraps(func) to the wrapper inside AuthHelper.auth_required in helpers/auth.py fixes this for all three controllers at once.

Prompt To Fix With AI
This is a comment left during a code review.
Path: prompt-service/src/unstract/prompt_service/controllers/extraction.py
Line: 21-23

Comment:
**Missing `functools.wraps` in `auth_required` changes Flask endpoint names**

Now that `@route` is applied after `@AuthHelper.auth_required`, Flask registers the inner `wrapper` function (returned by `auth_required`) rather than the original handler. Because `auth.py`'s `auth_required` does not call `@functools.wraps(func)` on its wrapper, the three endpoints are now registered under the endpoint name `"wrapper"` instead of `"extract"`, `"index"`, and `"prompt_processor"`. Each lives in a separate blueprint so there is no immediate collision here, but if a second `@auth_required`-decorated route is ever added to any of these blueprints the app will crash at startup with a duplicate-endpoint `AssertionError`. Adding `@functools.wraps(func)` to the `wrapper` inside `AuthHelper.auth_required` in `helpers/auth.py` fixes this for all three controllers at once.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code

Copy link
Copy Markdown
Contributor

@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.

🧹 Nitpick comments (1)
prompt-service/src/unstract/prompt_service/controllers/indexing.py (1)

35-36: ⚡ Quick win

Consider adding a test to verify auth enforcement.

To prevent regression and confirm the fix works as intended, consider adding an integration test that verifies these endpoints return 401 when called without a valid Authorization header.

Example test structure:

def test_endpoint_requires_auth(client):
    """Verify endpoint returns 401 without auth header."""
    response = client.post('/index', json={...})
    assert response.status_code == 401
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@prompt-service/src/unstract/prompt_service/controllers/indexing.py` around
lines 35 - 36, Add an integration test that exercises the indexing endpoint
handler decorated by indexing_bp.route("/index", methods=["POST"]) and protected
by AuthHelper.auth_required: create a test (e.g.,
test_index_endpoint_requires_auth) that uses the test client to POST to "/index"
without an Authorization header and asserts response.status_code == 401; also
add a complementary positive test that includes a valid Authorization header to
confirm successful auth handling. Ensure the test file imports the app/test
client fixture and targets the route guarded by AuthHelper.auth_required so
future regressions to auth enforcement are caught.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@prompt-service/src/unstract/prompt_service/controllers/indexing.py`:
- Around line 35-36: Add an integration test that exercises the indexing
endpoint handler decorated by indexing_bp.route("/index", methods=["POST"]) and
protected by AuthHelper.auth_required: create a test (e.g.,
test_index_endpoint_requires_auth) that uses the test client to POST to "/index"
without an Authorization header and asserts response.status_code == 401; also
add a complementary positive test that includes a valid Authorization header to
confirm successful auth handling. Ensure the test file imports the app/test
client fixture and targets the route guarded by AuthHelper.auth_required so
future regressions to auth enforcement are caught.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 24b979fc-c32d-4c8e-883d-37f365b29bf9

📥 Commits

Reviewing files that changed from the base of the PR and between a65d017 and 71c759e.

📒 Files selected for processing (3)
  • prompt-service/src/unstract/prompt_service/controllers/answer_prompt.py
  • prompt-service/src/unstract/prompt_service/controllers/extraction.py
  • prompt-service/src/unstract/prompt_service/controllers/indexing.py

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.

2 participants