ory/fosite

Support for assisted token flow

Closed this issue · 5 comments

mitar commented

Is your feature request related to a problem? Please describe.

For single page applications, current popular OAuth flows can be problematic because they still require one to have a server side component to redirect to. To address this, there is a new draft for "assisted token flow".

Describe the solution you'd like

That Fosite would provide also the assisted token flow. I am not sure if Fosite is interested in implementing draft standards, but I am opening an issue to at least request support once/if it gets standardized.

Describe alternatives you've considered

Custom flows which use iframe/postMessage, but the problem is that they are not standardized. For Google SSO, you can set redirect_uri to postmessage as an example of non-stanardized mainstream implementor providing this feature, but it is hard to find documentation for that feature, but if you search for it you can find some examples of people doing it. It seems Google tried to standardized it some time ago as well.

Additional context

For single page applications, current popular OAuth flows can be problematic because they still require one to have a server side component to redirect to.

This is not true! You can perform both auth code flows and implicit flows without server side components.

Additional explanation of the issue and description of this flow: https://nordicapis.com/assisted-token-flow-the-answer-to-oauth-integration-in-single-page-applications/

SameSite policies pretty much kill all of the iframe-based flows. I wouldn't rely on using them. OAuth2 Servers should typically enforce SameSite != Lax.

It's generally recommended to use the Auth Code flow with PKCE for SPAs.

mitar commented

This is not true! You can perform both auth code flows and implicit flows without server side components.

Sorry, let me clarify this. For client-side only apps. For example, an app hosted on static GitHub Pages, which does not have server-side state (no database). An example of such app is a P2P web application which obtains data from other peers. If you want to authenticate it against a trusted identity server to obtain JWT token one could use to identify yourself to other peers, you have a problem a bit, because you cannot store state between redirects to the server. You could use state and pass it through the OAuth flow, but that state can be pretty large. Another issue is that you might need to encrypt and integrity protect such state being passed around, but you cannot really because you cannot have a private key on such client. You maybe can use localStorage to persist some state, if it will work for Apple users. Moreover, redirects resets UI, so some complex UI states like selections and focus are lost.

If you can simply keep it in memory, while doing OAuth, so that your browser session never really exists, you do not have any of these problems. You can transparently in the background sign-in the user (once they already confirmed consent in a past session).

Introduction here explains well goals.

SameSite policies pretty much kill all of the iframe-based flows.

Why? The iframe is provided by Fosite. There is a new OAuth endpoint which exposes it and one just sets src of the iframe to it. Also, does Fosite use cookies at all?

OAuth2 Servers should typically enforce SameSite != Lax.

Why would SameSite == Lax be problematic for a cookie used in the iframe? I think if Fosite was using cookies then there would be problems with SameSite == Strict anyway because they would not be send through on HTTP redirects either, no? So regular OAuth flows would not work either.

Sorry, let me clarify this. For client-side only apps. For example, an app hosted on static GitHub Pages, which does not have server-side state (no database).

If you want to authenticate it against a trusted identity server to obtain JWT token one could use to identify yourself to other peers, you have a problem a bit, because you cannot store state between redirects to the server. You could use state and pass it through the OAuth flow, but that state can be pretty large.

I don't think the linked RFC aims to solve that. Instead it tries to implement a flow that doesn't redirect the user away from the app (as far as I understand). The RFC actually talks about the necessity of storing state later on:

Most client applications that use the assisted token flow will maintain the access token issued by the authorization server in a persisted state; this will commonly be an HTTP cookie or local storage. This is necessary, for instance, to create a pleasing user experience when a user navigates away from the application in their web browser and then returns. To ensure that the token is stored safely, the authorization server MAY provide application developers with guidance in the accompanying documentation on how to safely persist tokens. ...

I've seen the controversy over Apple's PWA move but that doesn't really affect your ability to perform an OAuth2 flow, right? 7 days of storage is longer than any authorize code or even access token expiry I have ever seen.

You will probably need to refresh the access token anyways after 7 days - which is possible without any user interaction using e.g. OpenID Connect hidden iframe.

Moreover, redirects resets UI, so some complex UI states like selections and focus are lost.

I think you could do the same with prompt=popup which is a more widely implemented OpenID Connect prompt. I think some PayPal integrations use it (or at least the flow type). Another option is to use OpenID Connect Silent Refresh which - sure - requires one redirect the first time you authorize the app but then does not need any redirects any more. It also uses a hidden iframe.

Why would SameSite == Lax be problematic for a cookie used in the iframe? I think if Fosite was using cookies then there would be problems with SameSite == Strict anyway because they would not be send through on HTTP redirects either, no? So regular OAuth flows would not work either.

Right, I confused Lax and Strict. But nevertheless, SameSite Lax does not work for iframes if the iframe source is not on the same domain as the main window. Only a value of None would work. The same problem applies with OpenID Connect Silent Refresh by the way.

I also checked over some of the other security considerations, and it seems like there are many implications from this spec including strict policies around CSP. Given that I think OpenID Connect solves most of this spec, the state this draft is in I don't believe Fosite is the right place for this.

mitar commented

I don't think the linked RFC aims to solve that.

It does exactly that. :-) I do not want it to redirect away so that my UI and other app state stays the same, inside the JavaScript execution. In this section:

This difference is important for many SPAs which take time to reload and may not be able to recreate the same state prior to the user being redirected to the authorization server.

This is exactly what I am trying to address.

The RFC actually talks about the necessity of storing state later on:

Yes, I would store the obtained token into local storage or cookie. But that is different from having to store whole existing UI state in it.

doesn't really affect your ability to perform an OAuth2 flow, right?

True. For OAuth2 flow 7 days is enough to get back from the redirect. :-)

Nothing currently prevents the OAuth2 flow for SPAs. You are right about that and I am not claiming that. I am just saying that experience is less than ideal because the app's state gets destroyed and you have to recreate it. At least this means a bit of UI/UX delay.

I think you could do the same with prompt=popup which is a more widely implemented OpenID Connect prompt.

So that is part of the Assisted Token flow as well. You can use popups instead of iframes. The difference is that the token is provided back to you from the popup through postMessage instead of other means. In fact, the SameSite might start interfering with those other means of how to get token back from the popup to the caller.

But nevertheless, SameSite Lax does not work for iframes if the iframe source is not on the same domain as the main window.

You are right. Hm. Some more information here: httpwg/http-extensions#762

I am marking this issue as stale as it has not received any engagement from the community or maintainers in over half a year. That does not imply that the issue has no merit! If you feel strongly about this issue

  • open a PR referencing and resolving the issue;
  • leave a comment on it and discuss ideas how you could contribute towards resolving it;
  • open a new issue with updated details and a plan on resolving the issue.

We are cleaning up issues every now and then, primarily to keep the 4000+ issues in our backlog in check and to prevent maintainer burnout. Burnout in open source maintainership is a widespread and serious issue. It can lead to severe personal and health issues as well as enabling catastrophic attack vectors.

Thank you for your understanding and to anyone who participated in the issue! 🙏✌️

If you feel strongly about this issues and have ideas on resolving it, please comment. Otherwise it will be closed in 30 days!