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

Support for OAuth2 via iframes #2

Open
LGraber opened this issue Jan 11, 2022 · 9 comments
Open

Support for OAuth2 via iframes #2

LGraber opened this issue Jan 11, 2022 · 9 comments

Comments

@LGraber
Copy link

LGraber commented Jan 11, 2022

Use Case

This is in reference to fedidcg/protocol-library#22. One of the potential solutions to how to establish the identity of the user for RP2, in its iframe embedded in RP1, is to use a standard OAuth flow. However, iframes provide limited flexibility with regards to what information can be passed to the embedded page. Besides the src url, there are some attributes which can control very specific properties but no general access to things like http headers. As per the OAuth Bearer Token specification, in reference to passing access tokens via URI query parameters:

Because of the security weaknesses associated with the URI method (see Section 4), it SHOULD NOT be used unless it is the only feasible method. Resource servers MAY support this method.

In Section 4.3 (recommendations), the specification goes on to say:

Don't pass bearer tokens in page URLs:

Bearer tokens SHOULD NOT be passed in page URLs (for example as query string parameters). Instead, bearer tokens SHOULD be passed in HTTP message headers or message bodies for which confidentiality measures are taken. Browsers, web servers, and other software may not adequately secure URLs in the browser history, web server logs, and other data structures. If bearer tokens are passed in page URLs, attackers might be able to steal them from the history data, logs, or other unsecured locations.

Assumptions

  • iFrames are not evil. No proposals or specifications exist that deprecate the use of iFrames. Web Components / Shadow DOM do not provide the same level of security and isolation as iframes and are not meant to be a replacement for iframes
  • Many embedded content providers use iframes and in the set of solutions we have been proposing to help to support embedded content and federated identity, we have never indicated that part of the proposal is to no longer use iframes
  • Despite the existence of security standards for iframes like CSP frame-ancestors, that might not be enough to allow for in-frame auth flows like OIDC / SAML which rely on 'tracking cookies'

Proposal

We add a new attribute to the iframe called ‘accesstoken’ which maps well to the naming conventions used in the oauth2 specification. Usage would look like:

<iframe src="https://www.mea.com/dashboard" accesstoken="123"></iframe>

The accesstoken attribute would be used to populate the Authorization header as such

GET https://www.mea.com/dashboard HTTP/1.1
User-Agent: …

Authorization: Bearer 123

What does this buy us

Technically speaking you can already pass an access token via the uri to an iframe. However, the spec specifically recommends against doing this. Also, for those who go this route, extra effort is often needed to more aggressively prevent replay attacks, going as far as making all access tokens single use which can overly complicate systems.
The above proposal provides a secure way to pass authentication information on frame load as opposed to after the initial page load. This is often needed because while some authenticated information could be delay loaded, some, such as CSP frame-ancestors headers, must be returned as part of the page response. By providing this minimal attribute, it will ease the implementation burden on RP1 and also provide a secure framework that meets the guidelines of the OAuth specification.
By making it a more secure option, this could provide 1 mechanism to solve the problems explained in issue 22.

What are the risks

  • OAuth is a protocol and not a native browser feature like CSP headers. I am not aware of other places where we expose something as part of the core html specification that is related to a protocol like this. What is the precedent?
  • With this pattern, the access token will still likely be in the html document src. It is recommended that those going this route, for the top level parent page, use ‘Cache-Control no-store’ header to prevent caching of these tokens and potential access to them after the user has logged off.
  • Is there a risk in having a well-known attribute that holds a security token? Will this create an attack vector if malicious code can scan for iframes and look for values in this field? Is this more risky than what people do today by placing it in the url.

Open Questions

  • How can we have feature discovery so RP1 can know when the browser can support this?
  • Iframe is one use case but we have not imagined what happens if the content is embedded vai alternative elements like img or media tags. Should this proposal expand to cover other such elements? I have put very little thought into that but … maybe?

What are people doing today

(possibly include links) This is not a sanctioned list of approaches, just a representation of what I found when searching:

  • Passing the token via the url and including more aggressive replay attack prevention by requiring JTI claims and single-use tokens
  • Attempting to use the srcdoc attribute of the iframe and pre-load the actual content via a direct call and then assign it to the frame srcdoc
  • Indicate to the child frame in the url that it should ask the parent for some authentication via postMessage communication.
@hpsin
Copy link

hpsin commented Jan 14, 2022

@LGraber - you may be interested in the "Teams SSO" flow that implements this pattern using postMessage (behind an SDK). We've found that it's insufficient for proper use - the deeplinked note explains why. This flow works ok for webapps that use cookie auth, but SPAs (that have no backend) cannot use this flow to run, because they cannot get additional tokens. We are redesigning this flow in order to move away from it, as opposed to doubling down on it.

@dickhardt
Copy link

@hpsin I don't follow the limitations referenced in your link. Why would postMessage not be an alternative to the functionality that @LGraber is proposing for #22?

@LGraber would passing the access_token as a fragment in the URL for the iFrame work? (I have not tried that) -- it would have similar security properties to passing parameters from the AS to the client using a fragment.

@hpsin
Copy link

hpsin commented Jan 14, 2022

postMessage is an alternative - the issue is that the end state of both is insufficient to run a full-fledged application that has its own identity and session. This is a valid proposal to implement OAuth inside an embedded tag (iframe, img, etc) but it's insufficient for authentication, which is the limitation hit in the link.

@LGraber
Copy link
Author

LGraber commented Jan 14, 2022

@hpsin Hirsh ... if you have time, I would like to talk about it. Obviously you have some insight into more customers but there are some use cases I think that can / do work. My claim is that there is not one single magic bullet that we are going to solve but that we might need a set of mechanisms that can work well for different use cases and/or might be easier for some people to transition to. As Kris pointed out, I think we should have a recommendation on how this can work and it, ideally, should not require super heavy lifting for all of the developers.

We actually built a solution using the token in the frame. We had to do a bunch of work to protect it and essentially make them single use but it works. @dickhardt putting it the url of the frame is the security risk because request urls are logged all over the place (perhaps not in browser history). If it is in a header ... it is not logged (ideally). If what I had proposed existed, it would have significantly reduced the work we needed to do. We actually can switch to using PostMessage given how we built it without any changes to our customers but ... I would like to understand more other people's experiences. I don't think we got technical enough in the meeting today :)

@hpsin
Copy link

hpsin commented Jan 14, 2022

@LGraber , certainly - either here or hirsin @ my employer is good. I agree in principle - there are certainly things that this solves, that we've seen solved using postMessage (but in a cleaner way that is easy to parse, rather than bespoke protocols). I'm not opposed, and agree that is solves a subclass of #22. I would only argue that it doesn't provide "SSO" as described in the issue title - it provides an access token, that can sometimes (depending on IDP) be parlayed into a session on the RP2 back-end.

My intent here is not to say this is bad, only to provide additional backing to your statement that other options (alongside this one) may be necessary.

@dickhardt
Copy link

@LGraber my suggestion was putting the access token in a URL fragment. I understand the security issues of the access token in the URL that is sent to the server. The fragment is not sent to the server, and can be retrieved by the iFrame javascript using window.location.hash

This mechanism is common in OAuth 2 for sending the authorization response back to a client to prevent the authorization code, ID Token, access token from being logged in the clients server. It also works well for SPA apps. The diagram in 4.2 shows how it works.

OAuth 2 does not allow URL fragments per 3.1.2 so that the fragment is available for results.

@hpsin for authentication, why not pass an ID token to the iFrame? If passed as a fragment, it seems to have the same security properties as a full OIDC flow using:

    `response_type=id_token`
    `response_mode=query`

@LGraber
Copy link
Author

LGraber commented Jan 15, 2022

@dickhardt Sorry ... I see what you are saying. That is interesting. (I think) It is similar'ish to PostMessage but simpler. The resource server still needs to server up an initial, unauthenticated page, get the access_token from the fragment, and send it back to the resource server to AuthN/Z the user and determine if whatever resource is being requested should be loaded. I agree that seems simpler than postMessage (although I haven't thought through all the security questions but perhaps you have). I wonder how we think comparatively of that flow versus the flow which sends the token in the Auth header on the initial get request for the iframe src. For a lot of apps, I can see how what you are talking about would work quite well

@LGraber
Copy link
Author

LGraber commented Jun 21, 2023

@dickhardt I think I need to revive this as we continue to try and make things work with the planned cookie changes and ... in some cases it is simply not possible without drastic changes needed by us and our customers. If the iframe needs to be authenticated on page load, in order to for example set the CSP frame-ancestors headers to prevent click-jack attacks, then all of the other proposals are not valid. The only way to support setting the frame-ancestors is if the auth header is passed in on the frame load.

@LGraber
Copy link
Author

LGraber commented Dec 14, 2023

@hpsin you still around and have time to chat? I dropped this as I got caught up in a lot of other things but this is bubbling back up and I think I am going to try and restart this discussion. The link you included is now dead so I cant refresh on that. I feel like the current browser support make clickjack prevention really hard and while we have some workarounds, this is another tool that could make the flows easier for certain implementations. Would love to hear what you think

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