PAT error responses are indistinguishable — expired vs wrong-scopes produce identical failures
Nobody has claimed this yet.
- 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: AddAUTH_FAIL_HEADERtohandleTokenExpirewith a descriptive message indicating token expiryReactivePATController.java: Add distinguishing diagnostic information to the/access-token/validateendpoint response (reason header or body)
Phase 2 — Exception specificity
PATAuthSourceService.java: ChangeisValid()to throw specific exception types instead of returningfalse:InvalidScopeException— when token scopes don't match requirementsTokenExpiredException— when token is expiredTokenInvalidatedException— 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"
- Expired →
Phase 4 — Consolidation
ZaasSchemeTransformApi.java: Consolidate the two near-identical error message methods (createMissingAuthenticationErrorMessageandcreateInvalidAuthenticationErrorMessage) into one, with error-type parameterization.
Acceptance Criteria
- 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-Failureheader handleTokenExpireconsistently setsAUTH_FAIL_HEADERon all code paths- The
/access-token/validateendpoint returns diagnostic information (header or body) indicating the reason for token rejection - The error message is deterministic — the same failure always produces the same header value regardless of modulith vs. microservices deployment
- 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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