ClientFactory/ClientService
Apps that want to call external APIs on behalf of a user need an
AuthenticatedHttpClient (PSR-18 decorator from horde/oauth) wired with the
user's stored token and the provider's TokenRefresher config. Assembling
this from OAuthTokenService, OAuthProviderConfigRepository and PSR
factories is boilerplate every consumer would repeat.
Why not just provide the client?
That would be a H5ish model, deriving the "correct" client from all kinds of global and implicit context and binding it to the interface directly. While this works reasonably well for "act on behalf of the authenticated user's identity" it gets messy and hard to reason about as soon as we want users to own resources or services distinct from their own identity, stand in / do delegated tasks on behalf of other users etc. Let's be explicit without being verbose.
What do we need to build?
A Core-level method on OAuthTokenService that takes
($userId, $providerId) and returns a ready-to-use
AuthenticatedHttpClient. I think we should not frame it as a factory and reserve that term for deterministic, autowireable producers.
- Load the
TokenSet from the token repository.
- Build a
TokenRefresher from the provider config (token endpoint,
client_id, client_secret).
- Wrap the base PSR-18
ClientInterface in AuthenticatedHttpClient. In practice we need a Horde\Http\Client
Unlocked Capability
- After the caller is done, the caller or a middleware checks
getTokenSet() and persists back if changed.
The service should type-hint the result as AuthenticateHttpClient (is a: PSR HTTP ClientInterface) and throw a reasonably specific exception if no such service is available.
ClientFactory/ClientService
Apps that want to call external APIs on behalf of a user need an
AuthenticatedHttpClient(PSR-18 decorator from horde/oauth) wired with theuser's stored token and the provider's
TokenRefresherconfig. Assemblingthis from
OAuthTokenService,OAuthProviderConfigRepositoryand PSRfactories is boilerplate every consumer would repeat.
Why not just provide the client?
That would be a H5ish model, deriving the "correct" client from all kinds of global and implicit context and binding it to the interface directly. While this works reasonably well for "act on behalf of the authenticated user's identity" it gets messy and hard to reason about as soon as we want users to own resources or services distinct from their own identity, stand in / do delegated tasks on behalf of other users etc. Let's be explicit without being verbose.
What do we need to build?
A Core-level method on
OAuthTokenServicethat takes($userId, $providerId)and returns a ready-to-useAuthenticatedHttpClient. I think we should not frame it as a factory and reserve that term for deterministic, autowireable producers.TokenSetfrom the token repository.TokenRefresherfrom the provider config (token endpoint,client_id, client_secret).
ClientInterfaceinAuthenticatedHttpClient. In practice we need a Horde\Http\ClientUnlocked Capability
getTokenSet()and persists back if changed.The service should type-hint the result as AuthenticateHttpClient (is a: PSR HTTP ClientInterface) and throw a reasonably specific exception if no such service is available.