-
Notifications
You must be signed in to change notification settings - Fork 23.1k
Add a federated identity guide #41779
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
Conversation
files/en-us/web/security/authentication/federated_identity/index.md
Outdated
Show resolved
Hide resolved
files/en-us/web/security/authentication/federated_identity/index.md
Outdated
Show resolved
Hide resolved
|
Preview URLs Flaws (3)URL:
External URLs (12)URL:
(comment last updated: 2025-11-29 04:14:31) |
…ex.md Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…ex.md Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…ederated-identity-guide * origin/federated-identity-guide: Update files/en-us/web/security/authentication/federated_identity/index.md Update files/en-us/web/security/authentication/federated_identity/index.md
Is there any benefit to using a particular specific provider or are they all "much of a muchness". If there is a real benefit then it is absolutely necessary to mention them. So depending on the answer above you might say something like.
|
files/en-us/web/security/authentication/federated_identity/index.md
Outdated
Show resolved
Hide resolved
| - The RP passes the code verifier in the _code_verifier_ parameter. | ||
| - The IdP hashes the code verifier, and compares the result with the stored code challenge: if they do not match, then the token request is denied. | ||
|
|
||
| This defends against two attacks: CSRF against the RP's redirect URL, and authorization code injection. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Link to attack page for CSRF
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-> 412b8ef
files/en-us/web/security/authentication/federated_identity/index.md
Outdated
Show resolved
Hide resolved
|
|
||
| 1. The RP makes a {{httpmethod("POST")}} request to the token endpoint. This request includes the following parameters: | ||
| - `client_id`: Identifies this RP to the IdP. | ||
| - `client_secret`: The secret used to authenticate the RP to the token endpoint. The RP could use some alternative mechanism for client authentication, such as TLS client authentication. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Presumably this is just a number right? Presumably this is all done via HTTPs so it can never leak?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, just an opaque value. And yes, everything over HTTPS. I didn't say that because it feels like the default. Worth spelling out do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Worth implying it can be any value maybe, but not that it needs to be HTTPs.
So maybe "The secret used to authenticate the RP to the token endpoint; this can be any value agreed between the RP and IdP."
In "The RP could use some alternative mechanism for client authentication, such as TLS client authentication." The "RP could use" reads as though the RP is doing the authentication and deciding what the authentication is. If that is correct then cool, but I assumed that this was to identify the RP to the IdP - so it is the IdP doing the authentication of the client.
Does this make sense as a question/ambiguity?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have tried using different/more words in f97f769, I'm not sure if it is clearer or not.
files/en-us/web/security/authentication/federated_identity/index.md
Outdated
Show resolved
Hide resolved
|
|
||
| 1. The attacker makes an authentication request to the IdP for themselves, and gets back an authorization code for their own tokens. | ||
|
|
||
| 2. The attacker tricks the user's browser into making an HTTP request to the RP's redirect URL, including the attacker's authorization code. To the RP, this looks like a response from the IdP to an authentication request originating from the user. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be worth numbering the point of injection on the preceding flow so it is easier to see what is happening (just thinking aloud)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure exactly what change is needed here.
files/en-us/web/security/authentication/federated_identity/index.md
Outdated
Show resolved
Hide resolved
| - Because the IdP's stored code challenge is associated with the authorization code, it will be the challenge for the user. | ||
| - Because the `code_verifier` in the token request is part of the attacker's flow, it will be the verifier for the attacker. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is meant by ", it will be the challenge for the user." and " it will be the verifier for the attacker.". I can tell this is "the point", but I don't get it.
I think you're saying that
- when the authorization request was made for the stolen auth code it included a code_challenge, (which is a hash of the RP code verifier) is stored by the idp and associated with the code.
- the auth code gets passed into the flow just before the token request. My understanding is that the RP would include the code_verifier which is a hash of its original challenge
- But what happens now is a mystery, because the RP only knows its original request, so presumably that is the correct code_verifier for the original challenge.
I suspect that "t will be the challenge for the user" means that somehow the RP associated the code_verifier that it sends with the token with the user, so when the attacker injects itself into the flow it will have some (or no) code_verifier associated with it.
Whatever the case, I'm not sure the relationships are clear enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps it's clearer if we start earlier, with the user's flow. And lets look at how it works without PKCE, first.
- the legitimate user tries to sign in
- the RP makes an authentication request to the IdP
- the IdP authenticates the user and sends the authorization code
- BUT the user has installed a malicious extension, that steals the code, sends it to the attacker, and terminates the user's flow. The user just thinks "something went wrong".
- now the attacker has the authorization code. But they can't just make a token request directly, because of client authentication (the attacker can't impersonate the RP).
- so instead, the attacker starts their own authentication flow with the RP.
- when the IdP sends them the authorization code, they intercept the redirect and replace their code with the the user's code that they stole. Remember, the user's code is for the user's tokens.
- next, the RP processes the next step of the attacker's flow, which is the token request. But they're sending the user's code, so it returns the user's tokens to the attacker's flow. Result: the attacker is signed into the user's account
Basically the attack is just the attacker signing in, but splicing in the user's auth code.
(It's the inverse of CSRF, because with CSRF it's the user signing in, but splicing in the attacker's auth code.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, so in the token request the code_verifier cannot be hashed to the code challenge, because the code challenge was produced in the users original authoriseation flow thingy?
Thanks you. Makes sense.
Now you're going to have to think "did Hamish need me to go through that because it was unclear or because he is an idiot". I think there is room for improvement. But if you want me to suggest something might need to wait until Friday.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With PKCE:
- the legitimate user tries to sign in
- the RP makes an authentication request to the IdP. -> the RP generates a code verifier, and sends the hashed verifier (i.e. the challenge) to the IdP.
- the IdP authenticates the user and sends the authorization code ->the IdP stores the code challenge with the user's code
- BUT the user has installed a malicious extension, that steals the code, sends it to the attacker, and terminates the user's flow. The user just thinks "something went wrong".
- now the attacker has the authorization code.
- the attacker starts their own authentication flow with the RP. ->the RP generates a new code verifier, and the IdP stores this new code challenge next to the attacker's code
- When the IdP sends them the authorization code, they intercept the redirect and replace their code with the the user's code that they stole. Remember, the user's code is for the user's tokens.
- next, the RP processes the next step of the attacker's flow, which is the token request. ->the RP sends the attacker's code verifier and the user's code. The IdP looks up the user's code, finds the user's verifier, and it doesn't match the code verifier it was passed. So it returns an error.
Why can't the attacker steal the user's code verifier too? Because it's never exposed to the front end, it only ever lives in the RP, server-side.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"did Hamish need me to go through that because it was unclear or because he is an idiot"
It's hard. It took me a while. One challenge is that using more words and taking it step by step helps, but I don't want to spend too many words on this. Another is that once you understand it, it makes sense, and it's hard to know if your explanation makes sense to someone else who doesn't understand it yet! I do think I have probably tried to compress it too much here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However, I'm pretty sure that omitting the user's initial flow is a mistake, and perhaps including that is enough for this to make sense?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK I have tried in 412b8ef to clarify the auth code injection attack and defense.
BTW there is a great presentation on this attack here: https://www.youtube.com/watch?v=moQidjdV5cw , probably I should include this too. But I'd appreciate if you can see if the words make sense before watching this :).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've made some whiny comments, that don't take away from the fact that this is IMO crystal clear now.
| - The RP generates a value that is hard to guess and is specific to this authentication request. This value is called the _code verifier_. | ||
| - The RP creates a {{glossary("hash", "cryptographic hash")}} of the code verifier, and uses it as the `code_challenge` parameter in the authentication request. | ||
| - The IdP stores the code challenge, and associates it with the access code that it returns to the RP. | ||
| - The IdP stores the code challenge, and associates it with the authorization code that it returns to the RP. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mabye "user's authorization code" or similar. Just so that later we already have in our minds that this is associated with the user
| - In step 1, the RP generates a code verifier for the attacker's request, and sends the hashed code verifier (the code challenge) to the IdP. | ||
| - In step 2, the IdP stores the code challenge alongside the attacker's authorization code. | ||
| - In step 5, the RP won't be able to find a code verifier for the user that matches the challenge the IdP stored, so the token request will fail. | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If possible, it might be nice to capture a flavour of your comment below here:
Why can't the attacker steal the user's code verifier too? Because it's never exposed to the front end, it only ever lives in the RP, server-side.
|
I've removed my review flag and requested a review from Hamish, as it looks like he is handling this nicely. Let me know if you do, in fact, need input from me. |
files/en-us/web/security/authentication/federated_identity/index.md
Outdated
Show resolved
Hide resolved
|
|
||
| In general, when an RP decides to use a particular IdP for federated login, the RP will register with the IdP, and as part of this process the IdP should explain to the RP exactly which arguments it expected to be given, how it should handle the objects that the IdP returns, and any other behavior it expects the RP to implement. | ||
|
|
||
| ## Choosing IdPs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I accidentally added some comments to one of your linked commits - so linking in case that doesn't notify properly 443cec7#r171001071
The main useful point was to consider adding info about the costs of having more idps.
files/en-us/web/security/authentication/federated_identity/index.md
Outdated
Show resolved
Hide resolved
files/en-us/web/security/authentication/federated_identity/index.md
Outdated
Show resolved
Hide resolved
files/en-us/web/security/authentication/federated_identity/index.md
Outdated
Show resolved
Hide resolved
files/en-us/web/security/authentication/federated_identity/index.md
Outdated
Show resolved
Hide resolved
files/en-us/web/security/authentication/federated_identity/index.md
Outdated
Show resolved
Hide resolved
files/en-us/web/security/authentication/federated_identity/index.md
Outdated
Show resolved
Hide resolved
files/en-us/web/security/authentication/federated_identity/index.md
Outdated
Show resolved
Hide resolved
files/en-us/web/security/authentication/federated_identity/index.md
Outdated
Show resolved
Hide resolved
|
|
||
| The [Federated Credential Management API (FedCM API)](/en-US/docs/Web/API/FedCM_API) provides built-in browser support for federated identity. The API does not yet have cross-browser support and is still being actively developed, so we can't fully recommend its use, but it promises several benefits over implementing a protocol like OpenID Connect directly: | ||
|
|
||
| - In the OIDC flow we've previously described, the website using OIDC (that is, the RP) has to coordinate the interactions between itself, the user, and the IdP. As we've seen, this is complicated and error-prone. With FedCM, the browser takes care of this interaction: as an RP, you call a browser API, and the browser locates the IdP, asks the user to authenticate, and returns a token from the IdP that the RP can use to sign the user in. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This means the token itself is available in the browser before being returned to the RP. Do we need to worry about the token be stolen or otherwise being misused?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, I see further down the token is not "the token" we already discussed - it might be the authentication code. This overloading of terms feels a bit confusing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah it is confusing. These are unfortunately the terms used in the spec (I feel like when the spec was written it was assumed that this might be an actual ID token). We could use something other than "token" here though - 'object", "value". I don't think we have to use the spec language.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yuck. I'd be tempted to keep token but make it clear that it is not the same token - because the terminology is both accurate and also is probably used in the API description and would be hard to purge. So "something like"
| - In the OIDC flow we've previously described, the website using OIDC (that is, the RP) has to coordinate the interactions between itself, the user, and the IdP. As we've seen, this is complicated and error-prone. With FedCM, the browser takes care of this interaction: as an RP, you call a browser API, and the browser locates the IdP, asks the user to authenticate, and returns a token from the IdP that the RP can use to sign the user in. | |
| - In the OIDC flow we've previously described, the website using OIDC (that is, the RP) has to coordinate the interactions between itself, the user, and the IdP. As we've seen, this is complicated and error-prone. With FedCM, the browser takes care of this interaction: as an RP, you call a browser API, and the browser locates the IdP, asks the user to authenticate, and returns an opaque token from the IdP that the RP can use to sign the user in. In an OIDC implementation this token might be the authentication code from the IdP. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got to get onto my queue, so pretending I didn't see your FWIW in #41779 (comment)
Co-authored-by: Hamish Willee <hamishwillee@gmail.com>
|
That last round of fixes LGTM. Who else are you going to get look at this? |
Thank you Hamish! I'm hoping to get some SWAG CG-affiliated people to have a look. |
|
|
||
| - The next most secure pattern is one in which the website uses a web server to handle all OAuth/OIDC interactions, but then returns the access token to the front end, and the front end then makes API requests directly. In this scenario the website can be a confidential client but malicious code running in the browser (for example, through an XSS attack) can potentially steal access tokens. However, the front end doesn't have to store access tokens long-term: it can retrieve them from the backend when it needs them. | ||
|
|
||
| - The least secure pattern is one in which OAuth/OIDC interactions and interactions with APIs both take place in the front end. This, for example, would be the natural architecture for a {{glossary("SPA", "Single-page app")}}, where the entire application executes in the browser. In this architecture the RP can't be a confidential client, because it can't reliably keep a client secret. This means that it can't authenticate itself to the IdP. It also has to persistently store tokens, which increases the risk of malicious code stealing them. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This, for example, would be the natural architecture for a {{glossary("SPA", "Single-page app")}}
I would avoid making this claim, because a lot of SPAs, if they have user data at all, do have a server side, in the form of REST APIs. SPA, CSR, etc., exclusively refer to the frontend architecture and don't really say much beyond "how the page is rendered". Perhaps: "This, for example, would be the natural architecture for an application that executes entirely in the browser."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
-> 8e7a48f
Elchi3
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Amazing work, thank you @wbamberg! 👍
|
This has been open for a while and has had some review, so I'm merging it. Happy to handle any more comments in follow-ups. Thanks for your work on the review @hamishwillee , much appreciated. |
* Draft a federated identity guide * Update files/en-us/web/security/authentication/federated_identity/index.md Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update files/en-us/web/security/authentication/federated_identity/index.md Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * back-channel logout example sentence * OP->IdP * Clearer language * ... * ... * Clarify authorization code injection * Reword client id preamble * Update CSRF description * Add a section on FedCM * Link to BCP * Mention PKCE downgrade attacks * Recommend client auth based on public-key crypto * Add sections on choosing idps and a summary * Update wording for client secret * clarify that PKCE is not used when the attacks work * Apply suggestions from code review Co-authored-by: Hamish Willee <hamishwillee@gmail.com> * Hint that client===RP * Review feedback --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Hamish Willee <hamishwillee@gmail.com>
This PR adds a guide to federated identity.
Note that preview images are broken because of mdn/fred#904.