What you describe is the explicit grant flow and the exchange of the code for an access token automatically protects against this vulnerability. The bugs here are in the implicit grant flow where the redirect doesn't include an opaque code but an actual access token. There the application is responsible for validating that the token is for that application, but there's nothing that requires it to do that.
I'm a little confused about this. The article seems to say that the application (the server that is the target of the attack) makes a call to FB where the application believes that a successful response indicates that the user's credentials are valid. It says that FB responds with success (and the user's email). But in fact FB would respond with success regardless of whether the supplied token was valid (for that application). This seems like either a serious bug in FB's implementation, or very bad API design. Presumably it's the latter because FB is responding correctly to a request that has nothing to do with auth -- application (server) asked for the email for one of its users, providing its (the server's) credentials for that request. The problem is that the API doc/design has fooled the application developer into believing that that call was an auth check. But it wasn't.
Is that it?
Also...why is this not wrapped in a library such that it wouldn't be possible to make such a mistake?
"Facebook" (or whoever) just sends a token that says the user authorized the use of its account for the application. What happens in this 'hack' is that the token is valid, but was not actually intended for that application, but for another one (e.g. a malicious one, that is now trying to reuse the token to access other services of the same Facebook user). I don't know how Facebook tokens look like, but JWTs have a property called aud/audience indicating who the token was intended for. The application developer must verify that the token was actually intended for their application. That is my understanding, sorry if I got it wrong.
I get that. You're not wrong. I'm asking why Facebook tells the application anything other than "nope, that's not valid for you"? It doesn't seem to be a case of the application will accept random noise as a valid token. It does send the token to Facebook and Facebook responds with something that looks like "OK". But when they said "OK" they really meant "we gave you some valid information about the target user, but forgot to mention that the token you provided was not valid".
I'm saying that Facebook should in all cases respond with "Nope, that's not a valid token". Their API just shouldn't have a way to get data on a user without providing a valid token (by valid, we mean valid for that purpose, not stolen from somewhere).
So you send the token only and the token is valid, and there's no way for Facebook to check if it was meant for you or not. As mentioned in other posts, this would not happen in other flows because you would send e.g. your client secret along with the code.
Failing sounds like its a requirement for them to do it, but this is not true. If you send them just a token, then how would they associate it with the application? Like I said, in another flow where you send your client secret and a code to exchange for the token, they need to verify it and it would be a failure on their part if they didn't.
They have the client id when the token is requested, so they can associate the generated token with it by either keeping some state or encoding it in the token, no?