Follow-up to #119 (PR #120), part of the auth-redesign cleanup tracked under #105.
Background
#119 / PR #120 fixed the immediate problem — the native Basic-auth popup that re-introduced browser-side Basic credentials and defeated token logout — by suppressing WWW-Authenticate: Basic when the request carries the z/OSMF CSRF marker (X-CSRF-ZOSMF-HEADER). That is a client-driven heuristic: it works because our SPA and Zowe always send the marker, but it is our own convention, not a server-declared property of the route.
Empirical note from #119: real z/OSMF does not use this header — it sends WWW-Authenticate: Basic on its REST API 401s regardless. So the heuristic is a deliberate "better than stock z/OSMF" choice, not a compatibility contract.
The deeper design smell
The AUTH=BASIC mode conflates two independent things:
- credential source — "I accept
Authorization: Basic"
- challenge presentation — "I advertise
WWW-Authenticate: Basic"
An API route wants the former without the latter, deterministically, regardless of what the client sends.
Proposal
Introduce a server-declared, per-route API marker so an operator can mark a route as API → its 401 is always a bare 401 (no WWW-Authenticate), independent of request headers. Candidate spellings (decide during design):
Note the parser (httpprm.c parse_kv_tail) currently accepts only NONE|FORM|BASIC — #98 documented AUTH=BASIC,TOKEN but that comma-list / TOKEN keyword was never built. This issue finishes that piece.
Scope
Not blocking
#120 already unblocks the SPA token model. This is the clean, deterministic long-term form.
Follow-up to #119 (PR #120), part of the auth-redesign cleanup tracked under #105.
Background
#119 / PR #120 fixed the immediate problem — the native Basic-auth popup that re-introduced browser-side Basic credentials and defeated token logout — by suppressing
WWW-Authenticate: Basicwhen the request carries the z/OSMF CSRF marker (X-CSRF-ZOSMF-HEADER). That is a client-driven heuristic: it works because our SPA and Zowe always send the marker, but it is our own convention, not a server-declared property of the route.Empirical note from #119: real z/OSMF does not use this header — it sends
WWW-Authenticate: Basicon its REST API 401s regardless. So the heuristic is a deliberate "better than stock z/OSMF" choice, not a compatibility contract.The deeper design smell
The
AUTH=BASICmode conflates two independent things:Authorization: Basic"WWW-Authenticate: Basic"An API route wants the former without the latter, deterministically, regardless of what the client sends.
Proposal
Introduce a server-declared, per-route API marker so an operator can mark a route as API → its 401 is always a bare 401 (no
WWW-Authenticate), independent of request headers. Candidate spellings (decide during design):AUTH=BASIC,TOKEN(parse the comma list; presence ofTOKEN⇒ API ⇒ bare 401), orAUTH=TOKEN.Note the parser (
httpprm.cparse_kv_tail) currently accepts onlyNONE|FORM|BASIC—#98documentedAUTH=BASIC,TOKENbut that comma-list /TOKENkeyword was never built. This issue finishes that piece.Scope
MOD=/LOC=.auth_required_response(): an API route emits a bare 401 unconditionally; theX-CSRF-ZOSMF-HEADERheuristic from Suppress Basic challenge on z/OSMF API 401s for XHR clients (#119) #120 can remain as an additional suppressor forBASIC/DEFAULTroutes.samplib/httpprm0: describe source-vs-challenge and mark/zosmf/*as API.FORM/DEFAULTroute get a401instead of the HTML login form?Not blocking
#120 already unblocks the SPA token model. This is the clean, deterministic long-term form.