-
Notifications
You must be signed in to change notification settings - Fork 624
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
🚧 OAuth - Client SDK #2434
🚧 OAuth - Client SDK #2434
Conversation
packages/xrpc-server/src/util.ts
Outdated
const transferEncoding = req.headers['transfer-encoding'] | ||
return (contentLength && parseInt(contentLength, 10) > 0) || transferEncoding | ||
export function hasBody(req: express.Request): boolean { | ||
// xrpc client will send a content-type header if, and only if, there is a body |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While our xrpc client will send a content-type header, I'm not sure it's formally a requirement, especially for any endpoint that receives application/octet-stream
(since servers may interpret a missing content type in this way). We want to ensure we're compatible with other implementations, and I think that means sticking close to HTTP in any cases of ambiguity.
Though I'm also curious, was there something wrong with the existing implementation that required this change? The only thing that stands out to me is that it's possible we should be checking for chunked encoding as opposed to any transfer encoding.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is currently not possible to upload an empty text file. I added a test for that.
I also changed this again since you reviewed to work more like it used to. I "just" added the following condition that will also considered as having a body:
// e.g. an empty txt file
if (req.headers['content-length'] === "0" && req.headers['content-length']) return true
The rest of the diff in this function is purely aestetical.
b9f67b1
to
0a1bbcc
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good 👍
f4ab44f
to
d326bb3
Compare
d6bbf1b
to
5b13cd5
Compare
5b13cd5
to
a9a428f
Compare
2d86132
to
3b9a883
Compare
3b9a883
to
742cd7f
Compare
742cd7f
to
eb41ab3
Compare
fb5f17c
to
3d83f50
Compare
8cbf61e
to
5d924cc
Compare
fea763f
to
5c96687
Compare
fix(xrpc-server): allow upload of empty files
5d924cc
to
60f4020
Compare
60f4020
to
69632ad
Compare
2c370d3
to
5c96687
Compare
reworked in #2483 |
Why
From a conceptual point of view, An XRPC client should not expose which "service" will be triggered under the hood when the
call()
method is invoked. All is should expose is a simplecall(method, ...)
signature.This separation of concerns is especially useful in Bluesky's architecture since in that particular case, two "services" are actually needed to properly route calls: The entryway (that provides credentials) and the PDS (that handles the actual HTTP request).
This separation of concerns becomes even more useful when we introduce the support for various authentication strategies (e.g. OAuth), which may introduce special behaviors (e.g. automatic session refresh, sixgning of HTTP requests, DPoP proofs).
In its current form, the
Client
andServiceClient
classes from@atproto/xrpc
do expose theuri
being triggered. This yields to users of that class having to know the internals of the XRPC Client and requires them to manually change thaturi
when needed:https://github.com/bluesky-social/social-app/blob/1e484c6318c0f1a2fa46c028d53b92d817293d11/src/state/session/index.tsx#L316
This PR inverts this logic by allowing a specialized class (
XrpcDispatcher
) to decide how to actually dispatch the HTTP call. That class can not only "decide" which service endpoint will be triggered but it can also perform additional tasks such as handling authentication. In particular, it can inject authorization headers and handle http errors in order to retry.This change was motivated by the fact that session management in case of OAuth is radically different to what exists now.
Details
A dispatcher has a signature:
A
Dispatcher
is responsible to:fetch()
)The dispatcher is used like so:
The default
XrpcDispatcher
can be used to perform basic calls:A more advanced use case would typically inject a dispatch function:
The
AtpDispatcher
extendsXrpcDispatcher
by adding:getDid()
: get thedid
of the currently logged in usergetServiceUrl()
: get the entry way urlThis abstraction is extended by either:
OAuthDispatcher
that will implement this interface using OAuth endpointsSessionDispatcher
where were moved the following itemsfetch()
call is performed herepdrUrl || serviceUrl
)Authorization
headerAtpAgent
related to session management):createAccount
,login
,resumeSession
&refreshSession
+ emitting of session changed related events (AtpPersistSessionHandler
). The logic here is exactly was was present inAtpAgent
.Because the
OAuthDispatcher
will no longer have the ability tologin()
,createAccount()
, etc. in the same fashion, those methods are no longer exposed on theAtpAgent
itself.The impact of this is that now, in order to use the conventional session management methods, one must use the
SessionDispatcher
instead.Before
After
Future: The end goal is to replace the
SessionDispatcher
by anoauthClient
like so: