Yet another 401 while requesting a token problem


#1

Hi,

Okay, I’m obviously missing whatever problem I seem to have here and I’ve validated most everything I can think of, I’m including basically all of the debugging information, what am I missing? This is my own oauth code, but telling me to just use XYZ library isn’t an option, nor is it a solution. If I had to guess the problem is somewhere in differences between local and remote URL encoding, but that’s just a stab in the dark. Other relevant info is that it’s being sent over port 80 vs 443 just for development/debugging purposes/lack of hassle for wireshark, let me know if I’ve missed any relevant info:

HTTP Headers:

POST /oauth/request_token HTTP/1.1

Authorization: OAuth oauth_nonce="0CLn1SD46TihThiMRzCM/vX/9/coufj79Rx8zMxMJAE=", oauth_callback="oob", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1340169062", oauth_consumer_key="8BpuepyGkukZn8R0ynXHMg", oauth_signature="QmRFGrYA8j1CPVx+pSkMqlxRQSI=", oauth_version="1.0"
Content-Length: 0
Connection: Keep-Alive
Accept-Encoding: gzip
Accept-Language: en-US,*
User-Agent: Mozilla/5.0
Host: api.twitter.com
Content-Type: application/octet-stream



HTTP/1.1 401 Unauthorized
Date: Wed, 20 Jun 2012 05:11:05 GMT
Status: 401 Unauthorized
Content-Type: text/html; charset=utf-8
Last-Modified: Wed, 20 Jun 2012 05:11:05 GMT
Cache-Control: no-cache, no-store, must-revalidate, pre-check=0, post-check=0
X-Runtime: 0.01177
X-Frame-Options: SAMEORIGIN
X-Transaction: 468e995a82c835ae
Pragma: no-cache
X-MID: 11c50e4d8b74b6cf69e0d64513f14bd2be928bab
Expires: Tue, 31 Mar 1981 05:00:00 GMT
Set-Cookie: k=10.34.124.104.1340169065673181; path=/; expires=Wed, 27-Jun-12 05:11:05 GMT; domain=.twitter.com
Set-Cookie: guest_id=v1%3A13401690656825658; domain=.twitter.com; path=/; expires=Fri, 20-Jun-2014 17:11:05 GMT
Set-Cookie: _twitter_sess=BAh7CCIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNo%250ASGFzaHsABjoKQHVzZWR7ADoPY3JlYXRlZF9hdGwrCNQUTQg4AToHaWQiJTgx%250AODE1M2RhMTJkNjhlYWMzYmNkOWIyNGFlM2JiMTM1--9eaab50cd31451ac409f4511f5f7a68785f15bbc; domain=.twitter.com; path=/; HttpOnly
Vary: Accept-Encoding
Content-Encoding: gzip
Transfer-Encoding: chunked
Server: tfe

Base String:

POST&http%3A%2F%2Fapi.twitter.com%2Foauth%2Frequest_token&oauth_callback=oob%26oauth_consumer_key=8BpuepyGkukZn8R0ynXHMg%26oauth_nonce=0CLn1SD46TihThiMRzCM%2FvX%2F9%2Fcoufj79Rx8zMxMJAE%3D%26oauth_signature_method=HMAC-SHA1%26oauth_timestamp=1340169062%26oauth_version=1.0

local timestamp:

1340169062

remote timestamp:

1340169115

“secret” hmac key (tried url escaped & otherwise, same result)

XXXXXX&

hmac-sha1 per openssl:

$ printf '%s' "POST&http%3A%2F%2Fapi.twitter.com%2Foauth%2Frequest_token&oauth_callback=oob%26oauth_consumer_key=8BpuepyGkukZn8R0ynXHMg%26oauth_nonce=0CLn1SD46TihThiMRzCM%2FvX%2F9%2Fcoufj79Rx8zMxMJAE%3D%26oauth_signature_method=HMAC-SHA1%26oauth_timestamp=1340169062%26oauth_version=1.0" | openssl dgst -sha1 -hmac "XXXX&" -binary | openssl base64
QmRFGrYA8j1CPVx+pSkMqlxRQSI=

variables in play in an easier to read format:

Oauth: cb: callback: 'oob'
ckey: '8BpuepyGkukZn8R0ynXHMg'
method: 'HMAC-SHA1'
nonce: '0CLn1SD46TihThiMRzCM/vX/9/coufj79Rx8zMxMJAE='
timestamp: '1340169062'
signature: 'QmRFGrYA8j1CPVx+pSkMqlxRQSI='

Any ideas? tyvm


#2

I’ve edited out your consumer secret; best not to post that on a public forum.

Few quick ideas here: In the authorization header, that “=” character from the signature should be URL-encoded. I would avoid characters like “=” and “/” in your nonce and try to keep it simpler to avoid encoding issues.

Any specific reason you’re setting “Content-Type: application/octet-stream” ? This is actually a “application/x-www-form-urlencoded” request, and the OAuth specification expects any other content-type to be handled a little differently. I also recommend setting a more distinct user agent.


#3

Hi,

Thanks; I said “secret” for a reason, it has to be included with every request, its a desktop app, therefore there is nothing secret about it (this actually holds true for mobile apps too). But okay, done.

I’ve tried with the equal signs encoded and not, but assuming you’re telling me how twitter does it, I’ve changed it to make sure they’re encoded.

The nonce has been modified to just include A-Z and not base64 encoded, so no need to escape it anymore. The content-type was just the default, at least in theory because there is no body to the post it should be irrelevant, but changed to urlencoded. User-agent is also just the default for library (QT), changed but should also be irrelevant.

In short, all things modified, same result:

POST /oauth/request_token HTTP/1.1 Content-Type: application/x-www-form-urlencoded User-Agent: blahblahblah Authorization: OAuth oauth_nonce="VDXVDZXFQICOHEOZYZZZESZZYCMUUHDA", oauth_callback="oob", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1340204986", oauth_consumer_key="8BpuepyGkukZn8R0ynXHMg", oauth_signature="YrQDtJ/1zKa9kzFg0amfUqqh1to=", oauth_version="1.0" Content-Length: 0 Connection: Keep-Alive Accept-Encoding: gzip Accept-Language: en-US,* Host: api.twitter.com

HTTP/1.1 401 Unauthorized
Date: Wed, 20 Jun 2012 15:09:47 GMT
Status: 401 Unauthorized

Base string:

POST&http%3A%2F%2Fapi.twitter.com%2Foauth%2Frequest_token&oauth_callback%3Doob%26oauth_consumer_key%3D8BpuepyGkukZn8R0ynXHMg%26oauth_nonce%3DVDXVDZXFQICOHEOZYZZZESZZYCMUUHDA%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1340204986%26oauth_version%3D1.0

Any other ideas? It’s got to be something in differences in the url escaping-- im either doing it or not doing it somewhere you guys are inversely. I just have no idea what because it at least seems like I’ve tried every combination of what could be going on?


#4

This is incredibly annoying, especially given there’s no valid reason to force oauth onto a desktop app except for security theater (there’s no way you can protect against me stealing their username/password if thats what I wanted to do).

That all said, utilizing a library and watching what it sends, this is what I’m seeing:

POST /oauth/request_token?oauth_consumer_key=8BpuepyGkukZn8R0ynXHMg&oauth_nonce=nWpJWsa1GKWEbGqDNh54nZuwffDZZ7rU&oauth_signature=cdS6A1fgyUkDTm5oOtzNkuhNAtQ%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1340300946&oauth_version=1.0 HTTP/1.1 TE: deflate,gzip;q=0.3 Connection: TE, close Host: api.twitter.com User-Agent: libwww-perl/6.04 Content-Length: 0 Content-Type: application/x-www-form-urlencoded

HTTP/1.1 200 OK
Date: Thu, 21 Jun 2012 17:49:29 GMT
Status: 200 OK

Note that its sending the parameters in the URI instead of the Authorization header, that its lacking the oauth_callback parameter (supposedly required per the documentation, but obviously not required per reality).

Despite replicating this in my code I’m still getting a 401, but I must be giving it a different base string to start off with, so I’m still investigating.

I’m mostly posting here because this question comes up a lot and it seems like more often than not little to no actual help is given, and what help is given is not terribly helpful.


#5

Hi,

Okay, I’ve figured it all out. My problems with the URI based parameter passing was that QT was double-encoding. My overall problems were not related to how I passed the parameters though, as both the query string and authorization header aspects work.

My problems can be summarized as two things:

  1. The parameters as passed in the query string/authorization header HAVE to be in alphabetical/lexicographical sorted order. Twitter does not sort these and takes it as you passed it.

  2. The callback parameter that is required WILL cause the request to fail if you pass it, so mentally read the ‘required’ as ‘required to not be passed’ in the API documentation. Note that all of the requests below are exactly the same sans the one with the callback added.

sorting proof:

POST /oauth/request_token HTTP/1.1 Content-Type: application/x-www-form-urlencoded User-Agent: blahblahblah Authorization: OAuth oauth_nonce="VDXVDZXFQICOHEOZYZZZESZZYCMUUHDA", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1340309081", oauth_consumer_key="8BpuepyGkukZn8R0ynXHMg", oauth_signature="h3FHUnmB9JlxbFe7zmWQwxn1C1A=", oauth_version="1.0" Content-Length: 0 Connection: Keep-Alive Accept-Encoding: gzip Accept-Language: en-US,* Host: api.twitter.com

HTTP/1.1 401 Unauthorized
Date: Thu, 21 Jun 2012 20:07:42 GMT

POST /oauth/request_token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
User-Agent: blahblahblah
Authorization: OAuth oauth_consumer_key=“8BpuepyGkukZn8R0ynXHMg”, oauth_nonce=“VDXVDZXFQICOHEOZYZZZESZZYCMUUHDA”, oauth_signature=“h3FHUnmB9JlxbFe7zmWQwxn1C1A%3D”, oauth_signature_method=“HMAC-SHA1”, oauth_timestamp=“1340309081”, oauth_version="1.0"
Content-Length: 0
Connection: Keep-Alive
Accept-Encoding: gzip
Accept-Language: en-US,*
Host: api.twitter.com

HTTP/1.1 200 OK
Date: Thu, 21 Jun 2012 20:08:37 GMT

Callback proof:

POST /oauth/request_token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
User-Agent: blahblahblah
Authorization: OAuth oauth_callback="ooboauth_consumer_key=“8BpuepyGkukZn8R0ynXHMg”, oauth_nonce=“VDXVDZXFQICOHEOZYZZZESZZYCMUUHDA”, oauth_signature=“BEL0ixeTHpgGLjjaC55GVrNGnmk%3D”, oauth_signature_method=“HMAC-SHA1”, oauth_timestamp=“1340309081”, oauth_version="1.0"
Content-Length: 0
Connection: Keep-Alive
Accept-Encoding: gzip
Accept-Language: en-US,*
Host: api.twitter.com

HTTP/1.1 401 Unauthorized
Date: Thu, 21 Jun 2012 20:11:18 GMT
POST /oauth/request_token HTTP/1.1
Content-Type: application/x-www-form-urlencoded

User-Agent: blahblahblah
Authorization: OAuth oauth_consumer_key=“8BpuepyGkukZn8R0ynXHMg”, oauth_nonce=“VDXVDZXFQICOHEOZYZZZESZZYCMUUHDA”, oauth_signature=“h3FHUnmB9JlxbFe7zmWQwxn1C1A%3D”, oauth_signature_method=“HMAC-SHA1”, oauth_timestamp=“1340309081”, oauth_version="1.0"
Content-Length: 0
Connection: Keep-Alive
Accept-Encoding: gzip
Accept-Language: en-US,*
Host: api.twitter.com

HTTP/1.1 200 OK
Date: Thu, 21 Jun 2012 20:12:19 GMT

I can already tell, this code is going to be ‘fun’ to write…

TYVM for the attempted help, cheers. case closed.