It's not supposed to be. You put in time, use your brain to understand the system. Even a non-techie can easily understand OIDC and Oauth2, it's not that hard
As a techie, experienced in security, reading the OIDC spec... there are definitely some things I don't understand in there. I'm not sure the authors even understand what's going on.
On 2023-12-15 they published an update to OpenID Connect Core 1.0, called "errata set 2". Previously it said to verify an ID token in a token response, the client needs to
> * If the ID Token contains multiple audiences, the Client SHOULD verify that an azp Claim is present.
> * If an azp (authorized party) Claim is present, the Client SHOULD verify that its client_id is the Claim Value.
The new version is quite different. Now it says
> * If the implementation is using extensions (which are beyond the scope of this specification) that result in the azp (authorized party) Claim being present, it SHOULD validate the azp value as specified by those extensions.
> * This validation MAY include that when an azp (authorized party) Claim is present, the Client SHOULD verify that its client_id is the Claim Value.
So core parts of the security of the ID Token are being changed in errata updates. What was the old purpose of azp? What is the new purpose of azp? Hard to tell. Did all the OIDC implementations in existence change to follow the new errata update (which didn't update the version number)? I doubt it.
Or how about a more fundamental question: Why does the ID Token have a signature? What attack does that signature prevent? What use cases does the signature allow? The spec doesn't explain that.
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...
> Did all the OIDC implementations in existence change to follow the new errata update (which didn't update the version number)?
I mean, both the old and new version (at least, the parts quoted upthread) are exclusively SHOULD and MAY with no MUST, so (assuming, for the SHOULDs, the implementer had what they felt was sufficiently good reason) literally any behavior is possible while following the spec.
I've been thinking if a platform which connects techies to non-techies can help solve that, say like a systems integrator for individuals.
[1] https://needgap.com/problems/484-foss-are-not-accessible-to-...