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

Authorizing non-profile oauth scopes #477

Closed
samuelgoto opened this issue Jun 16, 2023 · 24 comments
Closed

Authorizing non-profile oauth scopes #477

samuelgoto opened this issue Jun 16, 2023 · 24 comments

Comments

@samuelgoto
Copy link
Collaborator

samuelgoto commented Jun 16, 2023

You can skip this section if you know how OAuth scopes work and go straight to The Problem

Identity Federation allows users to login to websites using their accounts from identity providers.

For consumers [1], for the most part (we estimate ~80% of the traffic), sharing your basic account profile covers the majority of the use cases, because it provides relying parties most of what they need to create an account: a signed and stable unique identifier and the user’s name, profile picture and email address.

[1] this isn’t true in enterprise and education deployments.

However, occasionally, websites need more information about the user to operate: e.g. their schedule, their friends list, their amazon prime status, etc. The need for the different aspects of the user’s data varies greatly across websites and identity providers (e.g. some identity providers know their user’s email addresses, some don’t), so Identity Providers offer them an enumeration that they can pick and choose from to give websites access to HTTP APIs. The enumeration, encapsulation and addressing of APIs are known as “OAuth scopes” (e.g. we use, informally, “The website is asking access for the user’s Google Drive’s OAuth Scope” ).

For example, on rp.com, after choosing an account, the website asks specifically for the user’s “date of birth”, in addition to the user’s basic profile. The Identity Provider is happy to provide to the relying party as long as it gets confirmation from their users that that’s strictly necessary for the task that they are trying to accomplish (create an account on the RP, which customizes their user experience depending on their user’s age).

The authorization flows are currently built using low level primitives, like top-level redirects / pop-up windows / iframes / cookies (as you can see above), which are hard to distinguish from the same practices that have been abused for tracking. For example, at the end of authorization flows, the website gets an OAuth “access token”, which can be refreshed/re-issued using iframes and third party cookies. As browser vendors impose constraints in these low level primitives, we expect parts of these flows to break.

The Problem

The problem is that, as browser vendors increasingly work to prevent tracking on the web, they struggle to distinguish federation from tracking, and so unintentionally affect these flows. We call this “The Classification Problem”: it is hard to distinguish federation from tracking.

Thankfully, through FedCM, browser vendors are starting to find ways to solve “The Classification Problem” for at least common parts of the flows: authorizing the access of the user’s basic profile information, which we’ll call Standard Basic Profile Authorization Process for now (Authentication is sometimes used here, but that seems overloaded and inconsistent to us).

This is currently accomplished by mediating two flows: (a) account choosing and (b) authorizing access to the user’s profile:

Screenshot 2023-06-16 at 4 22 19 PM

FedCM managed to do that because (a) there is a lot of uniformity in account choosing between IdPs (e.g. how to represent an account) and (b) there is a list of well-known attributes (OIDC’s standard claims and HTML’s autocomplete ontology) of the user’s identity that can be requested (names, email addresses, profile pictures, etc), so it was plausible to anticipate and build a mediated Authorization flow to exchange the user’s standard profile.

However, beyond account choosing and the standard profile, there is an unbounded number of APIs that an Identity Provider can provide to Relying Parties.

For example, the browser would never be able to know how to ask the user for permission to access “Social Graph” because it doesn’t know what a “Social Graph” is (whereas it can, and indeed does, know the fields of the user’s basic profile: “name, email and profile picture”).

So, what worked to mediate authorization of the user’s standard profile doesn’t seem like it would work for authorization of the user’s identity more generally: IdPs need an extensible mechanism that allows them to express the authorization using their own words.

So, how should the browser allow IdPs to share with the RP more of the user’s data, without lowering the privacy properties of FedCM?

@samuelgoto
Copy link
Collaborator Author

samuelgoto commented Jun 16, 2023

One proposal, is to extend the FedCM JS API (with a parameter called “scope”) to introduce a signal that the user agent can use to distinguish whether to mediate or delegate authorization. When it delegates authorization, after the existing browser mediated account chooser, the user agent uses a pop-up window to load content from the IdP that can be used to gather the user’s authorization.

The existing browser mediated account chooser allows the browser to capture the user’s explicit confirmation / acknowledgement that the user’s intention is to sign-in/sign-up/continue to a relying party with an identity provider. This prevents a tracker from abusing this API, and maintains the privacy bar that FedCM has set.

The delegated IdP window allows the IdP to gather the user’s permission using its own words.

Architecturally, this is accomplished by the following algorithm changes to the FedCM execution:

FedCM_ AuthZ API Explainer

Specifically, we propose to:

  • Make a UX separation between account choosing and API authorization.
  • Make the browser mediate account choosing (as it currently does with FedCM) and
  • Extend FedCM to offer
    • An extension to the FedCM JS API that allows the RP to say that it wants to use the delegated authorization UX to allow the IdP to mint a (or many) token (an opaque string that can carry things like access/refresh tokens, but isn’t opinionated about what goes in it) or errors back to websites through the FedCM promise.
    • An extension to the FedCM UX to include a delegated authorization UX: a Window (i.e. a pop-up window with the (a) proper attribution (displays the URL of the content area) and (b) content from the IdP (i.e. HTML/JS/CSS)) that an IdP can control to gather the user’s consent using their own words.
    • An extension to the response in the id_assertion_endpoint that the IdP can use to pass back a URL that allows the browser to open the IdP Window
    • A JS API that RPs can use to refresh tokens using the IdPs cookies, that can only be called under the assumption that the user has gone through the mediated permission prompt that was gathered in step (b).

User Flows

In this proposal, our journey starts with a “sign-in with idp.com” button that is rendered by websites, in this case www.timeoff.com.

From this interaction, the website can use a newly introduced parameter to the FedCM API, called scopes, which is an array of opaque strings, to trigger the new flow:

partial dictionary IdentityProviderConfig {
  // A list of strings that represent what the Relying Party needs
  // from the Identity Provider.
  sequence<USVString> scope;
  // The types of tokens that the RP wants to get back from the IdP.
  // More than one can be requested, e.g. an id_token and an access code
  // token:
  // https://openid.net/specs/openid-connect-core-1_0.html
  // #code-id_token-tokenExample
  sequence<USVString> responseType;
  // A map of key-value pairs that are opaque to the browser and only
  // passed to the IdP after the user's acknowledgement.
  record<USVString, USVString> params;
};

For example:

let {token} = await navigator.credentials.get({
  identity: {
    providers: [{
      // A newly introduced "scope" attribute, a list of strings
      // Defaults to "name email photo"
      scope: [
        "drive.readonly",
        "calendar.readonly",
      ],
      
      clientId: "1234",
      nonce: "234234",
      loginHint: "previous@user.com",
      configURL: "https://idp.example/",
      responseType: ["id_token"],
      // A string with parameters that need to be passed from the
      // RP to the IdP but that don't really play any role with
      // the browser.
      params: {
        "IDP_SPECIFIC_PARAM": "1",
        "foo": "BAR",
        "ETC": "MOAR",
      }
    },
  }
  // If possible, return without prompting the user, if not possible
  // prompt the user.
  mediation: "optional",
});

With this newly introduced parameter, the browser then knows that it should not mediate authorization but rather delegate it. It starts by going through the mediated FedCM account choosing flow, which can be customized for this specific case (e.g. we can show it as a modal dialog, etc).

The (modified) account chooser serves to gather the user’s acknowledgement that the intent is to “sign-in to website.com with idp.com”. With the user’s permission, (a) the browser gets confirmation that this was indeed the user’s intent and (b) introduces to the IdP the user’s intent to sign-in to the relying party for the first time.

After account choosing, based on the JS request scope parameter, the browser makes a determination: should the browser mediate the authorization for exchanging the user’s profile (is the RP asking only for the user’s profile?) or delegate authorization to the IdP (is the RP asking for OAuth scopes that the browser wouldn’t know how to formulate a question to the user?).

AuthZ - RP - Google Modal - Not Logged In (6)

If the RP needs access to OAuth APIs, the browser makes an HTTP POST request to the IdP’s id_assertion_endpoint with all of the parameters passed by the RP. The IdP, with all of the information available (request parameters, etc), can make a determination on whether it has gathered sufficient acknowledgement from the user or not and it uses the response of the POST request to determine how the browser should proceed: if the response provided by the IdP resolves with a “token” parameter, the browser continues to resolve the promise without any extra necessary permissions (as per the usual FedCM flow), but, in the presence of a “continue_on” parameter in the response, the browser is commanded to ask the user for more information and opens a pop-up window with the value of the “continue_on” parameter.

For example, when the IdP gets the following POST request to id_assertion_endpoint:

POST /fedcm_assertion_endpoint HTTP/1.1
Host: idp.example
Origin: https://rp.example/
Content-Type: application/x-www-form-urlencoded
Cookie: 0x23223
Sec-Fetch-Dest: webidentity

account_id=123&client_id=client1234&nonce=Ct60bD&disclosure_text_shown=true&scope=google-drive.readonly

The IdP can now respond with:

{
  // In the id_assertion_endpoint, instead of returning a typical
  // "token" response, the IdP decides that it needs the user to
  // continue on pop-up window:
  // "token" : "eyJC...J9.eyJzdWTE2...MjM5MDIyfQ.SflV_adQssw....5c"
  "continue_on": "/oauth/authorize?scope=..."
}

With that response from IdP back, because the browser acknowledges that it doesn’t have the words to ask the user the question (i.e. the browser doesn’t know what the “scope” is), it delegates that task to the IdP by opening a pop-up window (the actual UX is user agent specific) with the right attribution (i.e. it shows the origin/url that is being served) by opening a pop-up window pointing the user to the “continue_on” URL. Because the browser has already gathered the user’s consent to release to the IdP who the RP is, it includes both the IdP’s first party cookies as well as the RP’s identity, which is then able to construct the necessary permission prompt using its own language:

When the IdP has gathered sufficient confirmation from the user that the operation is authorized, it calls a newly introduced JS API that tells the browser to hand it back to the RP (which the browser does by resolving the original promise).

// NOTE: this is only callable from the IDP's top level frame.
interface IdentityProvider {
    // Provides the token back to the Relying Party.
    static void resolve(token);
};

For example:

document.getElementById("allow").addEventListener("click", async () => {
  let accessToken = await fetch(`/generate_access_token.cgi`);
  // Closes the window and resolves the promise (that is still hanging
  // in the relying party's renderer) with the value that is passed.
  IdentityProvider.resolve(accessToken);
  // Closes the window.
  window.close();
});

With the access token back at the RP, and with the browser’s knowledge that the user has consented to signing-in to the RP with the IdP, the browser then has more context on the communication between the IdP and the RP.

For example, when a user returns to the relying party, the relying party has a choice of calling the FedCM API in “silent” mode, which refreshes the session if one was already granted, or fails otherwise:

let result = await navigator.credentials.get({
  // Returns a result without any UI in case it is possible
  // (specifically, only after the user has consented to the FedCM
  // prompt in the past). Otherwise, returns an error.
  mediation: "silent",
  identity: {
    providers: [{
      // newly introduced "scope" attribute, a list of strings
      scope: [
        "google-drive.readonly"
      ],
      configURL: "https://idp.example/",
      clientId: "1234",
    }]
  }
});

Which leads to the end of the user journey, signed-in to the website and having given the website all that it needs to operate.

Alternatives Considered

The most notable alternatives are on the opposite side of the spectrum:

  • Full mediation (browser UI) and
  • Existing Primitives (use existing low level primitives, e.g. window.open())

Which are covered in the next subsections.

Mediated API AuthZ

The most immediate idea that came to mind was to fully mediate the authorization flow. While that’s not entirely impossible, and shouldn’t be discarded, we quickly had an intuition that this approach would lead to (a) substantial amount of work to make it expressive enough within (e.g. a single IdP is already really complex) and across Identity Providers (i.e. make it representative of all of the IdPs product requirements) for (b) zero to no privacy/security benefits (e.g. with regards to keeping the privacy tracking bar) and, importantly, (c) ossification of the UI in the form of lack of extensibility (e.g. hold back the ecosystem to innovate).

Nonetheless, in this alternative, the browser would come up with a protocol to “get strings from IdPs” that can be embedded into templates in the browser UI. For example, in the following UI, the string “Calendar” is provided by the IdP (via resolving the scope parameter), but everything else is browser mediated.

While it is true that this could potentially make the browser more confident that the user’s consent was more explicit (the browser does control more of the UI, even though it does not know for sure what “Calendar” means – because that’s an IdP-specific concept), it suffers from a variety of extensibility and expressivity problems.

We haven’t ruled out this alternative, but we think the current proposal strikes a much better balance.

Existing Primitives

The second noteworthy alternative is to implement AuthZ entirely in userland with the existing primitives that are available, namely, window.open().

What’s most compelling about this alternative is that we’d reuse a lot of the existing APIs and not have to rethink the privacy/security properties of the new API surface. So, given the choice, this could be compelling because it would be the least amount of work that browsers would have to do.

In this formulation, it would be the website’s responsibility to request AuthZ after having asked for AuthN:

async function authZ() {
  return new Promise((resolve) => {
    const {token} = await navigator.credentials.get({
      identity: {
        providers: [{
          // No extra parameters (e.g. scopes) to AuthN introduced.
          configURL: "https://idp.example/",
          clientId: "1234",
        }]
      }
    });

    // AuthN gathered, kick off the AuthZ flow now

    // 1) Opens a pop-up window 
    const popup = window.open("[https://idp.example/authz](https://idp.example/authz?scopes=google-drive)");

    // 2) Requests an "age" assertion from it
    popup.postMessage("/request?scope=age");

    // 3) When the user accepts, resolve the promise
    window.addEventListener("message", ({data}) => {
      // Gets the access token that was given back
      resolve(data);
    });
  });
}

While not something that we have entirely ruled out yet, this example shows some of the challenges: in the current UX flows, the RP only gets the response AFTER the use goes through the AuthN AND the AuthZ flow, whereas here, the RP would get “a” response right after the AuthN flow but BEFORE the AuthZ flow, and then “another” response after the AuthZ flow. Some of this can be mitigated by perhaps only allowing the IdP to call the API in this fashion (e.g. only allow this to be called from an iframe), but it raises some of these questions.

Extensibility

Interestingly, because the IdP gets to ask for the user’s permission using their own words (i.e. their own HTML/JS/CSS), the browser is entirely blind about what and how and why it is being asked.

We think that’s more of a feature than a bug.

For example, there are IdPs that could choose to ask for the parent’s user’s permission for under aged users (even when only the user’s profile is shared), and could lead the under aged user to pass the phone to their parents, which is something that would be close to impossible for a mediated flow to anticipate.

We think that having space in which IdPs can innovate independently without being pulled back by browser vendors can give a massive amount of autonomy to the ecosystem.

Open Questions

There are at least a few corner cases worth noting here: (a) we expect most users to have a single account (as opposed to multiple), so we need to make sure that the account chooser isn’t awkward in that case, (b) we need to deal with the case that the user is signed-out of the IdP and needs to be able to sign-in and (c) we need to introduce the ability for the user to add a new session when already signed-in.

These are problems that are generally independent of AuthZ per-se, but are are currently often used in conjunction with.

Single accounts
Logged out users
Adding other accounts

So far, we think that these are solvable problems.

@samuelgoto samuelgoto changed the title Authorizing scopes broader than profile Authorizing non-profile scopes Jun 16, 2023
@samuelgoto samuelgoto changed the title Authorizing non-profile scopes Authorizing non-profile oauth scopes Jun 16, 2023
@martinthomson
Copy link
Contributor

Why does the browser need to see the scopes? It seems like this is only needs the modifications to how the IdP communicates its requirements back to the browser. The loginHint, scopes, and responseType fields are all just passed along to the IdP.

Standardizing the format of these parameters so that the browser understands them doesn't really serve to help. It only puts the browser in a position that might involve their definition; something that is best left to the IdP and RP to arrange between themselves.

@samuelgoto
Copy link
Collaborator Author

samuelgoto commented Jun 22, 2023

Why does the browser need to see the scopes?

So, in this proposal, we use the scope parameter to determine whether to delegate or mediate the authorization flow. If scope = profile or openid or email name picture (all of these following the OIDC spec) then we mediate (because we know how to), else we open a pop-up window and delegate the authorization (after a browser mediated account chooser).

It seems like this is only needs the modifications to how the IdP communicates its requirements back to the browser.

Yeah, that was one of the tensions that occurred to us early on. I'm not strongly convinced either way at the moment, but this is a fair area of tension that is worth investigating.

Just to be concrete, is the following more or less what you are suggesting?

let {token} = await navigator.credentials.get({
  identity: {
    providers: [{
      // We could introduce a parameter that controls whether to mediate the authorization prompt
      // or open a pop-up window to delegate the authorization
      authz: true, 
      // clientId is used by the client_metatadata_endpoint
      clientId: "1234",
      // loginHint is used by the browser to match up accounts
      loginHint: "previous@user.com",
      // configURL is used to fetch the endpoints
      configURL: "https://idp.example/",
      // nonce is NOT used by the browser, and is an opaque token passed to the id_assertion_endpoint
      // maybe it should be moved to "params" instead?
      nonce: "234234",
      // A grab bag of parameters that get passed from the
      // RP to the IdP but that don't really play any role with
      // the browser.
      params: {
         // scope is opaque to the browser 
        "scope": [
          "drive.readonly",
          "calendar.readonly",
        ],
        "responseType": ["id_token"],
        "IDP_SPECIFIC_PARAM": "1",
        "foo": "BAR",
        "ETC": "MOAR",
      }
    },
  }
  // If possible, return without prompting the user, if not possible
  // prompt the user.
  mediation: "optional",
});

Does this match more or less what you are suggesting?

A few things that occurred to me when we ran into this:

  1. nonce is in this weird state where it is much like scope (in that it isn't used by the browser at all), but lives in the top level rather than the grab bag.
  2. scope actually represent the things that browsers know how to mediate: authorization for the openid scope or profile scope or name email picture or anything enumerable as a standard claim.
  3. it is not clear to me if it should be up to the RP to have a say on whether to mediate or not the authorization prompt or the IdP (or the browser). If we introduce a JS parameter, it would lead to the RP making that choice, which doesn't seem trivially correct to me.

There is probably a few more considerations to take.

Your intuition matches some of mine, and we ran into this tension early on. Like I said, I'm not convinced either way here, but that's the kinda of question that we'll need to figure out as we go.

So far, our intuition has been that the sweet spot in terms of extensibility is to reuse concepts from OpenID (example) and SAML, and provide a grab bag of parameters for anything that the browser doesn't need to know.

@alanbuxey
Copy link

what will the browser do, intercepting the release of information from the IdP when that information is encrypted for the SP (RP) to consume? also, signed releases from the IdP or signed requests from the SP - will these be passed through intact?

@cboozar
Copy link

cboozar commented Jun 26, 2023

Something I am confused on, this proposal implies that yes the browser does want to get out of the way for providing ID, that part is still true, but the extra sharing enabled by this that would normally be shared by 3rd party cookies seems like it’s ripe for abuse because of the unlimited access implied. Is this off base? How does a user understand the difference between sharing their identity and sharing something as broad as ongoing medical history or social graph?

@martinthomson
Copy link
Contributor

@samuelgoto

nonce is in this weird state

So unweird it and move it to "params" too. Can you give me a good reason that this is something that the browser needs to care about? I can't see any.

scope actually represent the things that browsers know how to mediate

Sure, but the question is whether browsers need to mediate. We need to be sure that the cost of intermediation (which is not just the cost of specifying and implementing stuff, it's the externalities generated that affect RPs and IdPs) is justified.

it is not clear to me if it should be up to the RP to have a say on whether to mediate or not the authorization prompt or the IdP

It depends on how you see the interaction pattern here. From my perspective, I consider the browser's responsibility being limited to presenting a choice to the user in terms they understand and act on, then getting out of the way. Being able to present account details (pictures, names, etc...) is part of that, but scopes in general can't be subject to that sort of comprehension. Sure, some scopes are comprehensible, but those aren't the interesting ones.

My sense is that if we're inferring what needs to happen based on the parameter set, we've already given RPs this control. We just haven't acknowledged it.

@alanbuxey

what will the browser do, [...] when that information is encrypted for the SP

Nothing. That's an exchange that occurs well past the point that (the/any) privacy loss has occurred. More below.

@cboozar,

extra sharing enabled by this that would normally be shared by 3rd party cookies seems like it’s ripe for abuse

Concretely, the privacy loss occurs at the point at which a user decides to allow an RP to talk to an IdP. Beyond that, my interest in browser participation is limited, except to the extent that it can make IdP/SP/RP tasks easier.

Yes, there is the risk that there is a gap between what the IdP decides to share and what the user expects them to share. That's a constant in all of this. But from the perspective of a browser, we lost the ability to police that interaction at the point we allow any information to pass between RP and IdP. So, rather than pretend that we have control and attempt to rein things in, we should instead embrace that and help RPs and IdPs get their jobs done.

One of the things I've noticed about these APIs is that they tend to grow all sorts of capabilities. The identity space is complex and so the systems that support them naturally tend to become similarly complex. What I don't want to have happen (as this case demonstrates very nicely) is the browser API grows to match. It doesn't need to. Most of the complexity growth can be left to RPs and IdPs to manage. That is, "params" can grow in complexity, without our permission or involvement.

@bvandersloot-mozilla
Copy link
Collaborator

Reiterating my comment from the CG meeting yesterday:

This growth of the browser API, the unsolved challenge of a not-yet-logged-in user, and the introduction of the navigation here indicate to me that it may be time to re-visit the proposal I made before my parental leave as a "browser-out-of-the-way" version of FedCM.

@letitz
Copy link

letitz commented Jul 7, 2023

I'm curious how the popup will be treated in more detail:

  1. Can we rid it of its opener, i.e. open it in noopener mode? It seems that the IdentityProvider.resolve() API is enough cross-window communication.
  2. Does the opener window retain a handle to the popup in a way that would allow message-passing or scripting?

Basically, I'd like for the browser to be able to isolate the popup from the opener entirely if it so desires.

@cbiesinger
Copy link
Collaborator

Titouan -- yes, that's the intention, IdP.resolve should be the only way to communicate.

@letitz
Copy link

letitz commented Jul 18, 2023

Ok thanks! Could you clarify the lack of opener and any other cross-window communication mechanism in the explainer then?

@hlflanagan hlflanagan added the agenda+ Regular CG meeting agenda items label Aug 14, 2023
@samuelgoto samuelgoto removed the agenda+ Regular CG meeting agenda items label Aug 16, 2023
aarongable pushed a commit to chromium/chromium that referenced this issue Oct 2, 2023
In this CL, we expose - behind a flag - a "mode" parameter in the
FedCM API and plumb it to the browser process through the mojom
interface.

This starts allowing the FedCM API to degrade more gracefully when the
user is *not* logged-in to the IdP.

Specific feature request here from Mozilla:

https://github.com/fedidcg/FedCM/issues/442#issuecomment-1675007152

The "mode" (={button, widget}) parameter controls the internal
algorithms of FedCM to handle logged-out users. In mode=button, the
browser opens a pop-up window when users are logged-out, whereas in
mode=widget (the default) the prompt is just automatically dismissed.

Part (pre-requisite) for this larger feature:

w3c-fedid/FedCM#477 (comment)

Privacy and Security Early Reviews:

https://docs.google.com/document/d/1WtiG-JwlcuCJPI0EZdo61vxg6sjTAuxFFm5I2Jk4x6s/edit

The browser implementation, that will actually change based on this
flag, is being implemented in a separate CL (see gerrit CL dependency).

Bug: 1429083
Change-Id: If9f6b453cc5e16c479b89a16191d010892bd0377
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4779439
Commit-Queue: Sam Goto <goto@chromium.org>
Reviewed-by: Yi Gu <yigu@chromium.org>
Reviewed-by: Emily Stark <estark@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1204181}
@jonkoops
Copy link

jonkoops commented Apr 10, 2024

Hi, I am one of the developers currently evaluating implementation of FedCM in Keycloak (keycloak/keycloak#16834). And the issues outlined here are most certainly a blocker for us to have FedCM be a useful component of our stack.

Keycloak also needs to support more advanced OAuth features such as Rich Authorization Requests, Proof Key for Code Exchange (PKCE) and Demonstrating Proof-of-Possession (DPoP), which we don't really see a way forward for supporting these features under FedCM as things are now. It seems to us that with FedCM all these protocols will have to be sort of re-invented, rather than serving as a foundation for existing protocols such as OAuth.

We think the account account selection and in general handling things more in a browser-native manner are all great ideas, and those should serve as a gateway, but from there on there are established protocols that have been decades in the making should be able to take over.

I'd highly recommend to start some discussions in the OAuth and OIDC working groups, as they are aware of the problems with tracking and 3rd-party cookies, to which there is currently no workable solution. FedCM could be that solution, but I feel strongly it should serve as a facilitator, and not a whole new protocol to deal with that has to cherry-pick things from existing standards over time.

Especially when combining this with the aggressive time-line for the phase-out of cookies and the time it will take for applications to start switching to this new model, as well as the fact that Google Chrome is currently the only one supporting this, it's a recipe for disaster in terms of adoption.

@mitar
Copy link

mitar commented Apr 10, 2024

@jonkoops I completely agree. While thinking in the context of #58, I realized that maybe a much simpler solution can exist for dealing with 3rd-party cookies issue than whole new protocol, location.assign could be extended to support location.assign(url, {credentials: include}), which would be opt-in to send 3rd party-cookies alongside the request where browser would then use trusted UI to very the intent with the user (like asking for geolocation, etc.), optionally remembering the decision. Similarly, server-side redirect could include Sec-Credentials: include header to do the same. I think this would address most if not all issues with 3rd party cookies while allowing reuse of existing protocols.

@timcappalli
Copy link

I'd highly recommend to start some discussions in the OAuth and OIDC working groups

@jonkoops these conversations have been on and off for the past year or two (many folks from OpenID have been part of the W3C FedCM effort) but we plan to ramp this up more actively starting this week at OSW and IETF 120.

@jonkoops
Copy link

Great! Do let us know if there are any developments in this area we can act on, because we're very open to anything that could help us resolve issues around 3rd-party cookies.

@cbiesinger
Copy link
Collaborator

The proposal earlier in this issue is combining several items that are orthogonal to each other. Some of them have been split out into other issues already (e.g. button flow). I have split the remaining parts of the proposal into two issues:
w3c-fedid/custom-requests#1 for continue_on/continuation API
w3c-fedid/custom-requests#2 for specifying scopes and custom parameters

I think that will help with focusing the conversation, especially since some things seem more controversial than others.

I am leaving this issue open to talk more generally about use cases that are not addressed with the proposals (e.g. #477 (comment))

@samuelgoto
Copy link
Collaborator Author

Keycloak also needs to support more advanced OAuth features such as Rich Authorization Requests, Proof Key for Code Exchange (PKCE) and Demonstrating Proof-of-Possession (DPoP)

I'm not super familiar with all of these features, but wouldn't some (all?) of them be possible to be implemented with this existing proposal? At least "Rich Authorization Requests" seems to be possible to me, but skimming through the other ones, that seems like possible too?

https://github.com/fedidcg/FedCM/issues/556

  1. Rich Authorization Requests
  2. Proof Key for Code Exchange (PKCE)
  3. Demonstrating Proof-of-Possession (DPoP)

If so, this is currently implemented behind a flag in Chrome, so it would be great if you could give that a try. If not, can you expand which of these features wouldn't be able to be represented by params?

@elf-pavlik
Copy link

Have someone evaluated using OpenID Connect for Verifiable Presentations? I only noticed #49 and https://github.com/fedidcg/FedCM/issues/240#issuecomment-1100253585
As I understand, OIDC4VP uses DIF Presentation Exchange - Presentation Definition. I haven't looked yet to see if it could work together with OAuth 2.0 Rich Authorization Requests while at the same time allowing requesting other kinds of credentials.

@samuelgoto
Copy link
Collaborator Author

Have someone evaluated using OpenID Connect for Verifiable Presentations? I

Yes, but in the context of this API instead. Is that what you are looking for?

@elf-pavlik
Copy link

Thank you, @samuelgoto. I'm considering using OIDC4VP in the context discussed here, which involves authorizing access to arbitrary kinds of data. First of all, I would like to understand what type of token is expected to be returned by the IdP. TBH, I don't know if the role should still be called IdP once its responsibility is a more general authorization service. I'm hesitant to have it be an access token. Instead, I would rather see some credential, possibly VP, which later could be exchanged for an access token using OAuth 2.0 Token Exchange or User-Managed Access (UMA) 2.0 Grant for OAuth 2.0 Authorization claim pushing. The use cases I'm thinking about also include access delegation. For example, ACME Inc. shares a calendar with Alice, and later, Alice wants to grant RP access to her calendars and ACME's calendar she has access to. Of course, the "IdP" (user's authorization service) would deal with related complexity and issuing relevant credentials. But in that scenario, RS with Alice's calendars and RS with ACME's calendars would be in different security domains, and using the access tokens directly wouldn't be appropriate.

@cbiesinger
Copy link
Collaborator

For all of you who had comments on the "scope" part of this proposal, please see an updated proposal for that aspect in w3c-fedid/custom-requests#4 .

@elf-pavlik
Copy link

The original problem statement in this issue included:

However, beyond account choosing and the standard profile, there is an unbounded number of APIs that an Identity Provider can provide to Relying Parties.

w3c-fedid/custom-requests#4 seems to focus on limited extensibility, which still focuses on information that could be considered a part of the user profile.

Is it expected that w3c-fedid/custom-requests#1 will address that unbounded number of APIs?

There is also something that I would see as a special case related to the social graph; the original problem statement mentions it in:

For example, the browser would never be able to know how to ask the user for permission to access “Social Graph” because it doesn’t know what a “Social Graph” is (whereas it can, and indeed does, know the fields of the user’s basic profile: “name, email and profile picture”).

Right away, I want to reference Contact Picker API, which hopefully is aligned with the general information expected in a user profile.

The social graph seems like a special case since it is not something that I would consider a direct part of a user profile. Instead, it seems like a collection of user profiles, so at least the data model should be consistent.

@samuelgoto
Copy link
Collaborator Author

Is it expected that w3c-fedid/custom-requests#1 will address that unbounded number of APIs?

Yes.

@yi-gu
Copy link
Collaborator

yi-gu commented Jul 2, 2024

FYI this feature is in origin trial starting from Chrome 126: https://developers.google.com/privacy-sandbox/blog/fedcm-chrome-126-updates

@samuelgoto
Copy link
Collaborator Author

samuelgoto commented Aug 1, 2024

I'm going to resolve this issue, since we broke this into many smaller and more specific ones that are more tractable when looked at in isolation. Here they go:

Feel free to reopen if you feel like something that was discussed here isn't represented in these smaller / more granular issues, and I'll make sure I'll add them to the list.

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