Skip to content

Public secure send endpoint is blocked by the JWT filter #109

Description

@DenizAltunkapan

Summary

The public download endpoint for secure sends is unreachable. Every unauthenticated request is rejected before it reaches the controller, so a share link can never be opened by a recipient.

SecurityConfig already permits the path:

"/api/public/secure-sends/**" is listed in permitAll

but JwtAuthFilter runs before the authorization rules and only skips these prefixes:

/api/auth/
/v3/api-docs
/swagger-ui

Every other path without a valid Bearer token is answered with sendError(401, "Missing Authorization header"). Because the resulting error dispatch to /error is itself not permitted, the caller sees a bare 403.

How to reproduce

Request the public endpoint without an Authorization header, directly against the running backend container:

GET http://cloud-page-backend:8090/api/public/secure-sends/<token>

Result: HTTP 403 with an empty body.

Evidence

  • The backend log stays completely silent for that request. Hibernate SQL logging is active, and no query against the secure send table appears, which shows that SecureSendService.resolve is never called.
  • A request to /v3/api-docs on the same instance returns 200, and a request to /api/auth/does-not-exist returns 403, which isolates the filter whitelist as the deciding factor.
  • Verified against the deployed artifact, not only the source. Extracting BOOT-INF/classes/cloudpage/security/JwtAuthFilter.class from /app/app.jar shows exactly three whitelisted path constants: /api/auth/, /v3/api-docs, /swagger-ui. There is no entry for the public prefix.

Expected behaviour

An anonymous GET on /api/public/secure-sends/{token} reaches the controller, and the service decides the outcome: the file for a valid token, 401 when a password is required or wrong, and 404 or 410 for an unknown, revoked or expired token.

Suggested fix

Let JwtAuthFilter skip the public prefix, in the same way the auth and documentation paths are skipped. A more robust variant is to derive the skip list from the same source as the permitAll matchers in SecurityConfig, or to override shouldNotFilter, so that the two lists cannot drift apart again.

Please also confirm that the error dispatch is permitted, otherwise a legitimate 404 for an unknown token still surfaces as 403.

Related

The share URL that the backend hands out is wrong in a second, independent way. See the separate issue about the generated link path.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions