Auth Header Migration Findings
Date: 2026-07-16
This note captures a static code-inspection pass over api.user.m7.org to
understand whether the current API primarily authenticates through modern HTTP
headers or through request/body fields such as auth.token.
Summary
The repo currently looks mid-migration.
- Public docs and CORS settings already point callers toward
Authorization: Bearer .... - The actual request parser and shared auth path still primarily read token and
auth context from request/body fields such as
auth.token,token,auth.dpop, and related values. - Based on the code inspected here, header-only bearer auth does not appear to be the real primary contract today unless something outside this repo rewrites headers into request fields before PHP parses the request.
This was a static inspection only. No runtime verification was performed.
Key Findings
1. The live request parser is request-field driven
The endpoint stack calls lib\HTTP\Request::parse(...), which:
- starts from
$_REQUEST - merges JSON request bodies
- maps underscore field names like
auth_tokeninto dotted keys likeauth.token
It does not inspect Authorization, DPoP, or other auth headers.
Relevant code:
../config/general.json/Users/hr/personal/code/php/m7-php-platform/route/endpoint/v1/Endpoint.php/Users/hr/personal/code/php/m7-php-lib/HTTP/Request.php
2. Shared auth expects auth.token or token
The base API controller and the shared auth service both pull the token from the parsed request object rather than directly from headers.
Relevant code:
../lib/php/thisProject/api/Controller.php/Users/hr/personal/code/php/m7-php-platform/package/auth/user/registry/service/Auth.php
This affects both user and member validation flows.
3. Public docs are ahead of implementation
Several public-facing docs say callers should send:
Authorization: Bearer <ACCESS_TOKEN>
Example:
../markdown_docs/session-me.md
But the actual /session/me handler still reads:
auth.tokentoken
Relevant code:
../lib/php/thisProject/api/session/Me.php
4. CORS is header-ready, but auth ingestion is not
The public route entrypoint allows these request headers:
AuthorizationDPoPtoken
Relevant code:
../public_html/route.php
That means the surface is at least prepared to receive modern auth headers, but the parsed request object used by the app does not currently normalize those headers into the auth fields the handlers expect.
5. This is not just a token-transport migration
The auth.* shape is used for more than bearer access tokens.
Examples:
- login and root-session flows use
auth.user,auth.pass,auth.code, andauth.org - exchange flows use
auth.tokenortoken, plus a mix ofauth.dpop,auth.htu,auth.fingerprint, and body-sidedpop,htu,fingerprint - some authenticated routes still use body-side
auth.passfor current password checks
Relevant code:
../lib/php/thisProject/api/auth/user/RootSession.php../lib/php/thisProject/api/auth/member/RootSession.php../lib/php/thisProject/api/auth/user/Exchange.php../lib/php/thisProject/api/auth/member/Exchange.php../lib/php/thisProject/api/me/Password.php
Practical Read
Today, the safest working assumption is:
api.user.m7.orgstill primarily authenticates through parsed request/body fields- bearer-header auth is documented and partially surfaced, but not yet the dominant implementation path in this repo
Likely Migration Seam
If this cleanup is resumed later, the least-risk seam appears to be:
- normalize
Authorization,DPoP, and any other modern auth headers into the same internal request shape the app already expects - keep the existing
auth.*readers working during transition - then decide which legacy request/body auth fields should remain supported, and for which endpoint families
That would let the request boundary modernize first without forcing every downstream handler to change at once.
Mounted Input Classes
At a high level, the live mounted API surface does break down into two broad request classes:
- routes that use the base authenticated path (
credentialsenabled) - routes that opt out of base credentials (
credentials => false) and do their own relay, token, or request validation
That distinction is real, and it gives a useful migration heuristic:
- guarded endpoints
- prefer normalizing modern header inputs into the guarded request shape and let the existing auth path keep doing the work
- unguarded endpoints
- split these into relay/deadDrop-guarded versus truly naked/manual-token routes
In other words:
- guarded endpoints look easier to migrate centrally
- unguarded endpoints should not be treated as one bucket
Guarded endpoints
The credentials-enabled side is the normal authenticated surface.
For header migration purposes, this is the direct conversion class:
- convert callers to send modern HTTP auth headers instead of request/body auth fields where appropriate
- read
Authorization,DPoP, and related modern auth inputs at the request boundary - normalize them into the same internal request/auth shape the guarded path already expects
- let the existing controller/service validation path continue from there
Operationally, that likely means:
- migrate guarded endpoints first
- update frontend callers to send header-based auth inputs
- preserve internal compatibility by mapping those headers into the existing request/auth structure during the transition
Unguarded endpoints
The credentials => false side needs a second split:
- relay / deadDrop guarded
- truly naked or otherwise special-cased
Unguarded but relay / deadDrop guarded
These mounted routes all opt out of base credentials and then immediately require a consumed SSO relay or deadDrop payload before doing their work:
POST /auth/user/loginPOST /auth/user/root-sessionPOST /auth/user/exchangePOST /auth/member/loginPOST /auth/member/root-sessionPOST /auth/member/exchangePOST /auth/signupPOST /oauth/client_credentials/issue
Relevant code:
../lib/php/thisProject/targets/api/v2/AuthAPI.php../lib/php/thisProject/targets/api/v2/MemberAuthAPI.php../lib/php/thisProject/targets/api/v2/SessionAPI.php../lib/php/thisProject/targets/api/v2/OauthAPI.php../lib/php/thisProject/api/auth/user/Login.php../lib/php/thisProject/api/auth/user/RootSession.php../lib/php/thisProject/api/auth/user/Exchange.php../lib/php/thisProject/api/auth/member/Login.php../lib/php/thisProject/api/auth/member/RootSession.php../lib/php/thisProject/api/auth/member/Exchange.php../lib/php/thisProject/api/auth/Signup.php../lib/php/thisProject/api/oauth/client_credentials/Issue.php../lib/php/thisProject/helper/SsoBridgeGate.php../lib/php/thisProject/api/auth/Controller.php../lib/php/thisProject/api/auth/member/Controller.php../lib/php/thisProject/api/oauth/client_credentials/Controller.php
For migration planning, these look lower-risk:
- they are unguarded at the base-controller level
- but they are not truly naked
- they can likely be left alone initially if the relay/deadDrop contract itself is not changing
Unguarded and truly naked or otherwise special-cased
There are mounted exceptions that should be addressed individually:
POST /session/me- opts out of base credentials, but manually reads
auth.tokenortoken, identifies the token locally, and then calls user/member validation
- opts out of base credentials, but manually reads
POST /oauth/clients/connections/verify- opts out of base credentials, requires a
tokenrequest field, and calls upstreamverifyusing the service master key
- opts out of base credentials, requires a
So the best current read is:
- the live unguarded mint / exchange flows are almost entirely SSO relay or deadDrop guarded
- those relay/deadDrop-guarded unguarded routes can probably be left alone in a first-pass header migration
- the truly naked or special-cased unguarded routes should be handled individually
Current examples of the individually handled bucket:
POST /session/me- likely deprecated, but still a direct manual-token path today
POST /oauth/clients/connections/verify- not a tenant-auth/member-auth path; it behaves more like an app-to-app token/connection allowlist check
Inventory Of auth.* Request Reads
This section is meant as a change map.
It lists code-path reads of:
auth.*request fields- whole
authrequest objects - shared helpers on the active
api.userrequest path that still depend on those values
It intentionally does not list:
- comments or docstrings alone
- outbound payload writers that send
authto downstream services - unrelated field names such as
oauth.*
Request parser aliases
The parser currently maps underscore request keys into dotted auth keys:
auth_user->auth.userauth_code->auth.codeauth_id->auth.idauth_method->auth.methodauth_token->auth.tokenauth_pass->auth.passauth_org->auth.org
Relevant code:
../config/general.json
Shared readers on the main API path
../lib/php/thisProject/api/Controller.phpReadsauth.tokenortokenin the base controller constructor before route auth validation./Users/hr/personal/code/php/m7-php-platform/package/auth/user/registry/service/Auth.phpReadsauth.tokenortokenin both user and member validation paths, and also readsauth.dpop,auth.htu, andauth.fingerprint.
These are the two most important shared reads because many routes inherit them
without touching auth.* directly in the route file.
Relay and OAuth-forwarding helpers
../lib/php/thisProject/helper/SsoBridgeGate.phpReadsauth.client_idandauth.orgwhen matching route input against relay context for root-session flows.../lib/php/thisProject/api/auth/Controller.phpReadsauth.client_idinoauthForwardedContext(...)andrequestHasOauthForwardedOverrides(...).../lib/php/thisProject/api/auth/member/Controller.phpReadsauth.client_idinoauthForwardedContext(...)andrequestHasOauthForwardedOverrides(...).
These matter because some endpoint files only read a few auth.* fields
directly, but still inherit additional auth.client_id behavior through these
helpers.
Public and management endpoints
../lib/php/thisProject/api/session/Me.phpReadsauth.tokenortoken.../lib/php/thisProject/api/me/Password.phpReadsauth.passas one of the accepted sources for the current password.../lib/php/thisProject/api/client/ChangePass.phpReadsauth.fingerprint,auth.dpop, andauth.htu.../lib/php/thisProject/api/oauth/client_credentials/IssueWeb.phpReadsauth.fingerprintas a fallback source.../lib/php/thisProject/api/admin/user/Controller.phpReadsauth.tokenortoken, plusauth.fingerprint,auth.dpop, andauth.htuin remote-auth setup.../lib/php/thisProject/api/org/identity/Controller.phpReadsauth.tokenortoken, plusauth.fingerprint,auth.dpop, andauth.htuin remote-auth setup.
Consumer auth flows
../lib/php/thisProject/api/auth/Login_traditional.phpReadsauth.user,auth.id,auth.pass, andauth.fingerprint.../lib/php/thisProject/api/auth/user/RootSession.phpReadsauth.user,auth.id,auth.pass, andauth.code. Also inheritsauth.client_idchecking throughSsoBridgeGate.../lib/php/thisProject/api/auth/user/Login.phpReadsauth.user,auth.id,auth.pass,auth.code, andauth.fingerprint. Also inheritsauth.client_idhandling throughoauthForwardedContext(...).../lib/php/thisProject/api/auth/user/Exchange.phpReadsauth.dpop,auth.method,auth.token,auth.fingerprint, andauth.htu. Also inheritsauth.client_idhandling throughoauthForwardedContext(...).
Member auth flows
../lib/php/thisProject/api/auth/member/RootSession.phpReadsauth.user,auth.id,auth.pass,auth.org, andauth.code. Also inheritsauth.client_idand relay/org matching throughSsoBridgeGate.../lib/php/thisProject/api/auth/member/Login.phpReadsauth.user,auth.id,auth.pass,auth.org,auth.code, andauth.fingerprint. Also inheritsauth.client_idhandling throughoauthForwardedContext(...).../lib/php/thisProject/api/auth/member/Exchange.phpReadsauth.dpop,auth.method,auth.token,auth.fingerprint,auth.htu, andauth.org. Also inheritsauth.client_idhandling throughoauthForwardedContext(...).
Retained legacy handlers
../lib/php/thisProject/api/auth/user/legacy/DelegatedLogin.phpReads the wholeauthobject plusauth.id,auth.user,auth.pass, andauth.code.../lib/php/thisProject/api/auth/member/legacy/DelegatedLogin.phpReads the wholeauthobject plusauth.userandauth.pass.../lib/php/thisProject/api/auth/member/legacy/Exchange.phpReadsauth.tokenandauth.fingerprint.
Important implementation mismatches
Some routes still document or imply auth.* inputs, but the current code reads
bare fields instead for part of the request:
../lib/php/thisProject/api/auth/user/RootSession.phpReads baredpop,htu, andfingerprint, notauth.dpop,auth.htu, orauth.fingerprint.../lib/php/thisProject/api/auth/member/RootSession.phpReads baredpop,htu, andfingerprint, notauth.dpop,auth.htu, orauth.fingerprint.
That mismatch matters because a future migration cannot assume that every
binding-related input still lives under auth.* even within the same auth
family.
Open Question
The main unresolved question from this pass is whether some upstream layer outside this repo rewrites headers into request fields before PHP sees them. If that exists, the runtime behavior may be less broken than the local code alone suggests.