zowe / zowe/api-layer

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

Open
#4,791 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug Priority: Medium size/S
Dominant language
Java
Stars
92
Forks
81
Avg merge
1d 9h
Merged PRs (30d)
46

Description

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

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading AuthExceptionHandler.java and ReactivePATController.java to trace expired-token and /access-token/validate responses, then follow PATAuthSourceService.java and ZaasSchemeTransformApi.java for exception and message selection. Run the existing integration tests and add coverage for expired, wrong-scope, revoked, and validate failures. Done means deterministic, distinct diagnostic responses and AUTH_FAIL_HEADER on all relevant paths.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
api, authentication, authorization
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.