The ID Token can be passed from the Identity Provider to the Relying Party (RP) in a few ways.
When `response_mode=id_token` is used, the ID Token can be passed in the front channel directly to the RP during a browser redirect. Since the ID Token is coming from the browser, it must be signed to ensure that a malicious actor can't tamper with it. Otherwise an actor could swap out a `sub` or an `email` claim and the RP would be none the wiser.
The ID Token can also be returned from the `/token` endpoint after exchanging an authorization code. Since the `/token` endpoint is a back channel call over HTTPS, the ID Token doesn't necessarily need to be signed here to avoid tampering. The RP can trust that TLS gets the job done. However, there are substantial benefits to having it be signed:
- If ID tokens were only signed sometimes, we'd have two different standards for how to construct and handle an ID Token, which would be quite confusing.
- Signed ID Tokens can be passed around to assert identity to other entities within the system. For example, there are some promising draft specifications that explore exchanging ID Tokens for access tokens in other systems. This is only possible because the ID Token cannot be tampered with.
>Since the ID Token is coming from the browser, it must be signed to ensure that a malicious actor can't tamper with it. Otherwise an actor could swap out a `sub` or an `email` claim and the RP would be none the wiser.
Are you referring to a login CSRF attack? Where an attacker causes a victim to visit a URL that forces the victim to log in to an account of the attacker's choosing? A signature on the token doesn't solve that AFAICT. The reason a token signature doesn't solve that is that an attacker can take the attacker's own ID Token that has a valid signature, and do the attack with that ID Token, forcing the victim to log in to the attacker's account.
Yes, the token signature does reduce the attacker's ability. Without a token signature, the attacker could force the victim to log in to any account. With a token signature the attacker can only force the victim to log in to the attacker's account. But this isn't a full solution. Some other anti-CSRF mechanism needs to exist. Once that anti-CSRF mechanism exists, the token signature is no longer useful.
Adding a signature to the token doesn't make sense to me as a login CSRF protection mechanism. If the designer's goal was to protect against login CSRF, the signature should be over something else, not just the token, and not part of the token. Then the token itself doesn't need the complexity of a signature (and the authorization code flow doesn't need the complexity of a signature), just the redirect flow needs it. Or even simpler, the designers could have mandated the authorization code flow, and not need any signature at all.
Or maybe you're referring to a completely different attack, where there isn't a victim user, just an attacker wanting to log in as someone else in the attacker's own browser. In that case, the signature doesn't solve that attack, because since everything happens in the attacker's browser, the attacker can modify the js locally to disable signature verification.
>- Signed ID Tokens can be passed around to assert identity to other entities within the system. For example, there are some promising draft specifications that explore exchanging ID Tokens for access tokens in other systems. This is only possible because the ID Token cannot be tampered with.
Yes, but the spec doesn't mention this. Just from reading the spec, a reader can wonder, "why is there all this unexplained complexity for no apparent reason?"
You're right that signing the token doesn't prevent login CSRF. Signatures protect against the second attack you mentioned. PKCE my favorite OAuth extension for preventing login CSRF as described.
> In that case, the signature doesn't solve that attack, because since everything happens in the attacker's browser, the attacker can modify the js locally to disable signature verification.
The frontend doesn't need to verify the ID Token - the backend of the relying part does. The backend can never trust the frontend. The signature is needed so the backend can verify that the ID Token credential was issued by the IDP.
> Just from reading the spec, a reader can wonder, "why is there all this unexplained complexity for no apparent reason?"
Yeah, this is true. Oftentimes the attacks are enumerated at the end of a spec, so it isn't clear when reading an earlier section. For example, the attack we've been discussing is described here: https://openid.net/specs/openid-connect-core-1_0.html#TokenM...
The ID Token can be passed from the Identity Provider to the Relying Party (RP) in a few ways.
When `response_mode=id_token` is used, the ID Token can be passed in the front channel directly to the RP during a browser redirect. Since the ID Token is coming from the browser, it must be signed to ensure that a malicious actor can't tamper with it. Otherwise an actor could swap out a `sub` or an `email` claim and the RP would be none the wiser.
The ID Token can also be returned from the `/token` endpoint after exchanging an authorization code. Since the `/token` endpoint is a back channel call over HTTPS, the ID Token doesn't necessarily need to be signed here to avoid tampering. The RP can trust that TLS gets the job done. However, there are substantial benefits to having it be signed:
- If ID tokens were only signed sometimes, we'd have two different standards for how to construct and handle an ID Token, which would be quite confusing.
- Signed ID Tokens can be passed around to assert identity to other entities within the system. For example, there are some promising draft specifications that explore exchanging ID Tokens for access tokens in other systems. This is only possible because the ID Token cannot be tampered with.
https://datatracker.ietf.org/doc/draft-parecki-oauth-identit...