As usual, the devil is in the smallest detail.
In solving this problem, I used the wonderful Charles Proxy to keep track of the requests that my ruby script made through the oauth library.
As it turned out, there were two different problems with the way I tried to make requests from curl and from meteors, which I discovered while spying on a successful ruby request, looking around at my code and tinkering with the oauth_callback Authorization header parameter to reverse process the problem.
Either one of these problems, or in combination, caused an invalid and unrecognizable oauth token. Instead of returning 401, linkedin created a token! Alas, this token was useless.
Here are two different authorization request headers that fail (with reasons after each):
Authorization: OAuth oauth_body_hash="2jmj7l5rSw0yVb%2FvlWAYkK%2FYBwk%3D", oauth_callback="http%3A%2F%2Flocalhost%3A3000%2F_oauth%2Flinkedin%3Fclose", oauth_consumer_key="*********", oauth_nonce="SJ3DSTHLh0T4UcqzYOUOqubIeWN9FERCePm5ro35EY", oauth_signature="*********", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1359081520", oauth_version="1.0"
In this first example, the problem is that the oauth_callback parameter has an invalid request parameter. It turns out that with a query string parameter, like ?close without a value, like in ?close=true , forces the query to be completely fubared. I still don’t know exactly why, but it seems to be happening.
Authorization: OAuth oauth_body_hash="2jmj7l5rSw0yVb%2FvlWAYkK%2FYBwk%3D", oauth_callback="http%3A%2F%2Flocalhost%3A3000%2F_oauth%2Flinkedin%3Fclose", oauth_consumer_key="*********", oauth_nonce="SJ3DSTHLh0T4UcqzYOUOqubIeWN9FERCePm5ro35EY", oauth_signature="*********", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1359081520", oauth_version="1.0"
In this second example, the problem is that oauth_callback has localhost in its scope. It turns out that some oauth providers connected in one of them do not like host names in the oauth_callback parameter (with FQDN or IP addresses). Spares me why. We need to use 127.0.0.1 . There is no good reason, but it’s just . Lame, but true.
It works:
Authorization: OAuth oauth_body_hash="2jmj7l5rSw0yVb%2FvlWAYkK%2FYBwk%3D", oauth_callback="http%3A%2F%2F127.0.0.1%3A3000%2F_oauth%2Flinkedin%3Fclose%3Dtrue%26state%3D0e314d0d-a5ca-40ca-8fcd-caa1cfce3ed4", oauth_consumer_key="*********", oauth_nonce="0KBnMSMI8NNk1cXn0YyTRpUnPdnqAX7F06KEloh9bs", oauth_signature="*********", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1359082177", oauth_version="1.0"
Note. I replaced the values of oauth_consumer_key and oauth_signature with ********* for obvious reasons to protect my keys and remove the possibility of reverse engineering my secret.