Skip to content

PAT error responses are indistinguishable — expired vs wrong-scopes produce identical failures #4791

Description

@balhar-jakub

Description

When a client sends a Personal Access Token (PAT) that is either expired or has wrong scopes, the API Mediation Layer produces the same error response with zero diagnostic distinction. A client cannot determine whether to refresh the token or request additional scopes. This creates a poor developer experience and makes automated token lifecycle management impossible.

Current Behaviors (the problems)

Problem 1 — Indistinguishable error header values. Both expired PAT and wrong-scopes PAT produce the exact same X-Zowe-Auth-Failure header value: "org.zowe.apiml.zaas.security.schema.missingAuthentication" (or sometimes "org.zowe.apiml.common.unauthorized"). A client can't tell the difference between "your token expired" and "your token doesn't have the right scopes."

Problem 2 — handleTokenExpire never sets AUTH_FAIL_HEADER. In AuthExceptionHandler.java:216, handleTokenExpire does not set the failure header at all. Compare with handleTokenNotValid (line 199) which does. If ExtractAuthSourceFilter.parse() catches an expired PAT first, the response has no failure header — a silent failure with no diagnostic information.

Problem 3 — /access-token/validate returns empty 401. The validate endpoint returns an empty 401 with zero body and zero diagnostic headers. A client calling /validate to check token status gets no useful information.

Problem 4 — Inconsistent error message selection. Two different methods produce different messages for the same failure scenario:

Method Message
createMissingAuthenticationErrorMessage() "org.zowe.apiml.zaas.security.schema.missingAuthentication"
createInvalidAuthenticationErrorMessage() "org.zowe.apiml.common.unauthorized"

Which one fires depends on whether isValid() or parse() fails first — an implementation detail that varies between modulith and microservices deployments.

Proposed Solution (4 phases)

Phase 1 — Immediate fixes (lowest effort, highest impact)

  • AuthExceptionHandler.java: Add AUTH_FAIL_HEADER to handleTokenExpire with a descriptive message indicating token expiry
  • ReactivePATController.java: Add distinguishing diagnostic information to the /access-token/validate endpoint response (reason header or body)

Phase 2 — Exception specificity

  • PATAuthSourceService.java: Change isValid() to throw specific exception types instead of returning false:
    • InvalidScopeException — when token scopes don't match requirements
    • TokenExpiredException — when token is expired
    • TokenInvalidatedException — when token has been revoked

Phase 3 — Distinct error messages

  • ZaasSchemeTransformApi.java: Map different exception types to distinct messages and AUTH_FAIL_HEADER values:
    • Expired → "expired" or "org.zowe.apiml.zaas.security.token.expired"
    • Invalid scope → "invalid_scope" or "org.zowe.apiml.zaas.security.token.invalidScope"
    • Revoked → "revoked" or "org.zowe.apiml.zaas.security.token.revoked"

Phase 4 — Consolidation

  • ZaasSchemeTransformApi.java: Consolidate the two near-identical error message methods (createMissingAuthenticationErrorMessage and createInvalidAuthenticationErrorMessage) into one, with error-type parameterization.

Acceptance Criteria

  1. A client receiving a 401 for an expired PAT can distinguish it from a 401 for a wrong-scopes PAT via the X-Zowe-Auth-Failure header
  2. handleTokenExpire consistently sets AUTH_FAIL_HEADER on all code paths
  3. The /access-token/validate endpoint returns diagnostic information (header or body) indicating the reason for token rejection
  4. The error message is deterministic — the same failure always produces the same header value regardless of modulith vs. microservices deployment
  5. Existing integration tests pass; new tests cover the distinct failure scenarios

Affected Files

File Change
zaas-service/src/main/java/org/zowe/apiml/zaas/security/.../AuthExceptionHandler.java Add AUTH_FAIL_HEADER to handleTokenExpire
apiml/src/main/java/org/zowe/apiml/controller/ReactivePATController.java Add diagnostic info to /validate endpoint
zaas-service/src/main/java/org/zowe/apiml/zaas/security/service/schema/source/PATAuthSourceService.java Throw specific exceptions from isValid()
apiml/src/main/java/org/zowe/apiml/ZaasSchemeTransformApi.java Map exceptions to distinct messages; consolidate error methods

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status
    Icebox

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions