yup-oauth2
yup-oauth2 copied to clipboard
Server-side oauth flow
Hi there!
There is no Server-side oauth flow implemented in this library. While implementing a custom solution for myself I wanted to ask if there were any discussions about implementing server-side flow?
If not - I can help with that. But overall library implementation won't fit for this flow (as far as I could understand from spending some time with sources of this library). If somebody is open to collaborating on it - I will be glad to contribute
Are you sure? I only skimmed it, but it sounded a lot like the InstalledFlow
. Or are there some subtle differences?
@dermesser Basically the same. But the main difference that in the installed flow we are ok with spinning up our own server. In the server-side flow - the server is already in use by the server side application. So, spinning up one more - will add unnecessary complexity. Hence, in order to implement it in this library we need to break “flow” in 2 parts:
- Get auth url we redirect user to
- Get the code from the url and fetch tokens
The first one should return redirect uri to the caller. The second one - consume the code generated when user logged in.
And that’s the problem. I don’t know how to “divide” the flow in this library on these two parts.
I see. I will take a look when I find some time - in the meantime you are of course welcome to try some things yourself, if it is time-sensitive :)
Having a ServerFlowAuthenticator
that can be used as component in a larger web app is of interest to me. I've started poking about in the code, and the big hurdle I see is that currently all implemented authenticators directly drive the user interface to completion, while for a server flow the driver would sit outside:
- when a login-to-google endpoint is hit, the web-app needs to ask the ServerFlowAuthenticator for the redirect URL
- that URL has a CSRF token that needs to be stored until the user completes the auth flow
- after the user has authenticated with Google, their browser is redirected back to the web-app, which then can hand off the querystring to the ServerFlowAuthenticator for verification and processing:
- load and verify the CSRF token
- use the authorization code from the querystring to exchange it for an actual token
- associate that token with the current session
Note that these two steps could very well happen in two separate processes, e.g. when the web-app is horizontally scaling.
The current TokenStorage implementation (of course) doesn't support storing the CSRF token, and doesn't support storing more than one user's worth of tokens.
Similarly constraining is the way flows are called, keeping the library user from interfering with the user interaction.
I've got two different ideas on how to implement this, and would love to get some guidance from @dermesser and anyone else with a stake in this to see which one is more acceptable to this project:
The neater (but more intrusive) idea is based on the observation that all current flows and the server flow do have the same "get auth code", "exchange code for token" structure. I could break apart token(scopes) -> TokenInfo
method into auth_code() -> AuthCode
and token(scopes, auth_code) -> TokenInfo
and provide some glue to hide this from library consumers for flows that can. The server flow would then replace the auth_code()
method with something like auth_redirect_url() -> RedirectUrl { url, csrf_token }
and auth_code_from_redirect_result(query_string, csrf_token) -> AuthCode`. The web app then can retrieve a TokenInfo based on the auth code and required scopes.
Alternatively I could create a separate Builder stack that creates an Authenticator
that already has the auth_code and initial token exchange done. This would require less changes to the existing code, but might introduce some duplication and maybe some confusion around the different ways to create authenticators.
I haven't yet fully thought through how the web-app would store and re-hydrate authenticators/TokenInfos on every request, so this might be missing something crucial.
Thank you @DavidS for your ideas. I'm open to accepting such changes in any case - however, I'm somewhat busy these days, so please give me a few more days to ponder on this :) I hope it is not a very pressing issue.
With that said, it is not surprising that you are hitting the limits of this library, as it originated in a somewhat simple client app (yup = youtube uploader) and is now being used for all sorts of different use cases. I hope we can join forces in adapting it to more demanding contexts like yours.
I'm certainly in no hurry, thanks for your encouragement.
I've started poking around the edges of the problem in code-form over the weekend, and the first step only needs a utility function very similar to https://github.com/dermesser/yup-oauth2/blob/29a72447f6289cd7f9c731b43d15a562cd77fba6/src/installed.rs#L26
That could be an easy wrap to expose that as fn build_authentication_request_url<T>(app_secret: &AppSecret, scopes: &[T], redirect_uri: Option<&str>) -> String
With that being so simple, a ServerFlow might just be called as
ServerFlowAuthenticator::builder(
app_secret,
ServerFlowQueryArgs.from_string("query args from the redirect back into the app"))
.build()
.await
.unwrap()
.token() // for example
.await
If I get something working I'll certainly report back.