Skip to content
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

Server-side oauth flow #107

Open
finomayato opened this issue Sep 2, 2019 · 6 comments
Open

Server-side oauth flow #107

finomayato opened this issue Sep 2, 2019 · 6 comments

Comments

@finomayato
Copy link

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

@dermesser
Copy link
Owner

Are you sure? I only skimmed it, but it sounded a lot like the InstalledFlow. Or are there some subtle differences?

@finomayato
Copy link
Author

finomayato commented Sep 3, 2019

@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:

  1. Get auth url we redirect user to
  2. 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.

@dermesser
Copy link
Owner

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 :)

@DavidS
Copy link
Contributor

DavidS commented May 14, 2022

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.

@dermesser
Copy link
Owner

dermesser commented May 16, 2022

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.

@DavidS
Copy link
Contributor

DavidS commented May 16, 2022

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

fn build_authentication_request_url<T>(

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants