Invalid / expired Token 401 message when trying to do twitter login to website


#1

I’m experiencing a 401 Unauthorized response from twitter’s oauth/access_token endpoint when I try to sign in via twitter from a drupal 7.x site.

I’ve been going through the code in the twitter module, and I’ve made some changes (POST instead of GET, add Authorization header for OAuth) to try to make my post data look like twitter’s example here: https://dev.twitter.com/docs/auth/implementing-sign-twitter

From what I can tell, I’m sending the data just how twitter wants it, but I’m still getting a 401 Unauthorized response from twitter. More specifically, the message in the response data is “Invalid / expired Token”.

Everything is working requesting the token, it’s just when trying to convert it into an access token that things aren’t working.

The POST that’s being sent to api.twitter.com/oauth/access_token is:

POST /oauth/access_token HTTP/1.0
Authorization: OAuth oauth_consumer_key="jPBEGdcURg1Yo7kAGFqnA",oauth_nonce="5b8e26b30924adc97ade983c40026040",oauth_signature="n2LjvqRXeY%2BIUVvdXvns%2FVAUhdg%3D",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1322669940",oauth_token="AhWLRtHQF7DChrW8bBRKKypsRlI9cNBlAhBOtAdE",oauth_version="1.0"
Accept: */*
Content-Type: application/x-www-form-urlencoded
User-Agent: Drupal
Host: api.twitter.com
Content-Length: 57

oauth_verifier=jupzwtNKmGXMLGGXgpQ2dGORe1ZEfYrGSBAR9fxh6A

(that “Accept:” header actually has asterisk/asterisk but they’re getting stripped from the post. but they’re there)

I’m not sure why twitter is rejecting this and I’m getting this invalid token message. I have checked and it’s the same token that I get in a response to the callback funtion:

callback query string:
(
    [oauth_token] => AhWLRtHQF7DChrW8bBRKKypsRlI9cNBlAhBOtAdE
    [oauth_verifier] => jupzwtNKmGXMLGGXgpQ2dGORe1ZEfYrGSBAR9fxh6A
)

Not sure why this is happening, as it appears to be following the steps outlined on that twitter “howto” page… ?


#2

So you’re getting a valid token from /oauth/request_token, redirecting the user to /oauth/authenticate, and the redirect works correctly?

Can you post the exact error message you’re getting? A full HTTP log of the entire request, or as much data as you can provide, would be helpful. Thanks.


#3

Yes, that’s correct. I get a token during the request portion, I get redirected to the twitter login / authorization page, and then when it redirects back to my site, my site tells me that twitter’s response to the access_token endpoint is “Invalid / expired Token”.

I have my site spitting out a bunch of debugging messages right now along each step of the way, so I can see where it’s going wrong. I know it’s not the raw HTTP requests like you wanted, but I think it’s providing the same data you were asking for (but after a little parsing into some arrays and objects).

(let me know if this isn’t useful to you and I can try to dig for more information, it’s just buried in there somewhere amongst different modules so it’s sometimes hard to find :slight_smile: )

Some explanation of what these messages are saying:
First, we make a POST request to oauth/request_token

auth_request method called from get_request_token
response:stdClass Object
(
[request] => POST /oauth/request_token HTTP/1.1
Authorization: OAuth oauth_consumer_key=“jPBEGdcURg1Yo7kAGFqnA”,oauth_nonce=“ba19f276ed50400136ef82347b1ffed6”,oauth_signature=“J%2FNUl5u%2F9rvoz3Xx3teog8nNdEc%3D”,oauth_signature_method=“HMAC-SHA1”,oauth_timestamp=“1322701894”,oauth_version="1.0"
Accept: /
Content-Type: application/x-www-form-urlencoded
User-Agent: Drupal
Host: api.twitter.com
Content-Length: 0

[data] => oauth_token=Mr4WyhTq64bebKqg4zStb2cB505PNyZ9Cv2t2o0ymI&oauth_token_secret=FTPXSuWaq3PV7FuXIfVcOeuiLsYjcjMTlaJL6v7Q&oauth_callback_confirmed=true
[protocol] => HTTP/1.1
[status_message] => OK
[headers] => Array
    (
        [date] => Thu, 01 Dec 2011 01:11:34 GMT
        [status] => 200 OK
        [x-transaction] => 2b2458c44a38e77c
        [etag] => "77133197d7261bc7e52e93db58b63405"
        [x-frame-options] => SAMEORIGIN
        [last-modified] => Thu, 01 Dec 2011 01:11:34 GMT
        [x-runtime] => 0.01411
        [content-type] => text/html; charset=utf-8
        [content-length] => 144
        [pragma] => no-cache
        [x-revision] => DEV
        [expires] => Tue, 31 Mar 1981 05:00:00 GMT
        [cache-control] => no-cache, no-store, must-revalidate, pre-check=0, post-check=0
        [x-mid] => 0d21a8391803f29b52e9f3b1f42e00d37dbe0a9a
        [set-cookie] => k=10.34.231.124.1322701894448652; path=/; expires=Thu, 08-Dec-11 01:11:34 GMT; domain=.twitter.com,guest_id=v1%3A132270189445747723; domain=.twitter.com; path=/; expires=Sat, 30-Nov-2013 13:11:34 GMT,_twitter_sess=BAh7CDoPY3JlYXRlZF9hdGwrCDozLfczAToHaWQiJWViMmZjNDUzNTFmOTAx%250AMjM5ZDY4Y2M2ZDJmYTJiMzM5IgpmbGFzaElDOidBY3Rpb25Db250cm9sbGVy%250AOjpGbGFzaDo6Rmxhc2hIYXNoewAGOgpAdXNlZHsA--3d65b3b590c61fdad11ff09fc80ca8869546a440; domain=.twitter.com; path=/; HttpOnly
        [vary] => Accept-Encoding
        [server] => tfe
    )

[code] => 200

)

You can see from the response that it gave me back an oauth_token which I then spit out in some more debugging messages:

post rv:oauth_token=Mr4WyhTq64bebKqg4zStb2cB505PNyZ9Cv2t2o0ymI&oauth_token_secret=FTPXSuWaq3PV7FuXIfVcOeuiLsYjcjMTlaJL6v7Q&oauth_callback_confirmed=true
token response: Array
(
[oauth_token] => Mr4WyhTq64bebKqg4zStb2cB505PNyZ9Cv2t2o0ymI
[oauth_token_secret] => FTPXSuWaq3PV7FuXIfVcOeuiLsYjcjMTlaJL6v7Q
[oauth_callback_confirmed] => true
)

Debugging message tells me the authenticate page url before it redirects to it (to make sure I was getting that right):

authenticate url:|https://api.twitter.com/oauth/authenticate?oauth_token=Mr4WyhTq64bebKqg4zStb2cB505PNyZ9Cv2t2o0ymI|

Debug message spits out the querystring from the callback function:

callback qs:Array
(
[oauth_token] => Mr4WyhTq64bebKqg4zStb2cB505PNyZ9Cv2t2o0ymI
[oauth_verifier] => sFg9aoRpZQhwzEGNOe4OOUVubjOwutHLVJv6mNRpvU
)
get_access_token get:Array
(
[q] => twitter/oauth
[oauth_token] => Mr4WyhTq64bebKqg4zStb2cB505PNyZ9Cv2t2o0ymI
[oauth_verifier] => sFg9aoRpZQhwzEGNOe4OOUVubjOwutHLVJv6mNRpvU
)

and then debug message spits out the results of the POST to oauth/access_token

auth_request method called from get_access_token
response:stdClass Object
(
[request] => POST /oauth/access_token HTTP/1.1
Authorization: OAuth oauth_consumer_key=“jPBEGdcURg1Yo7kAGFqnA”,oauth_nonce=“7bedcd2233a5d8a332d9c44a2cf4525b”,oauth_signature=“bZ2HuLVlHWpfE26HBnDloErpr80%3D”,oauth_signature_method=“HMAC-SHA1”,oauth_timestamp=“1322701946”,oauth_token=“Mr4WyhTq64bebKqg4zStb2cB505PNyZ9Cv2t2o0ymI”,oauth_version="1.0"
Accept: /
Content-Type: application/x-www-form-urlencoded
User-Agent: Drupal
Host: api.twitter.com
Content-Length: 57

oauth_verifier=sFg9aoRpZQhwzEGNOe4OOUVubjOwutHLVJv6mNRpvU
[data] =>

Invalid / expired Token
/oauth/access_token

[protocol] => HTTP/1.1
[status_message] => Unauthorized
[headers] => Array
    (
        [date] => Thu, 01 Dec 2011 01:12:27 GMT
        [status] => 401 Unauthorized
        [www-authenticate] => OAuth realm="https://api.twitter.com"
        [x-transaction] => bb806dde7b4f476f
        [x-frame-options] => SAMEORIGIN
        [last-modified] => Thu, 01 Dec 2011 01:12:27 GMT
        [content-type] => text/html; charset=utf-8
        [content-length] => 136
        [pragma] => no-cache
        [x-revision] => DEV
        [expires] => Tue, 31 Mar 1981 05:00:00 GMT
        [cache-control] => no-cache, no-store, must-revalidate, pre-check=0, post-check=0
        [x-mid] => 0acd28473b3bf6bf6badc7dd2188061d92e293ec
        [set-cookie] => k=10.34.231.124.1322701947125301; path=/; expires=Thu, 08-Dec-11 01:12:27 GMT; domain=.twitter.com,guest_id=v1%3A132270194713256380; domain=.twitter.com; path=/; expires=Sat, 30-Nov-2013 13:12:27 GMT,_twitter_sess=BAh7CDoPY3JlYXRlZF9hdGwrCP0ALvczAToHaWQiJWJiNmJjMjVkNjRjZTcy%250AYzQwZDhkMTYyMmY0ZmVmYjQwIgpmbGFzaElDOidBY3Rpb25Db250cm9sbGVy%250AOjpGbGFzaDo6Rmxhc2hIYXNoewAGOgpAdXNlZHsA--fee03a788fffdf1f754678ffce21815231232637; domain=.twitter.com; path=/; HttpOnly
        [vary] => Accept-Encoding
        [server] => tfe
    )

[code] => 401
[error] => Unauthorized

)

And that’s where I have the problem. You can see in the response I’m getting back from twitter that it’s saying “Invalid / expired Token” with a 401 Unauthorized… :frowning:


#4

Something else of interest to add here; If I try to login to the site using the twitter user that created the twitter app, it logs me in OK.
But any other twitter user cannot login to the site using their twitter account. Is this a permissions problem with the twitter app maybe? I have it set for read/write, and I get presented w/the authorization page when I try to login using my twitter account, so I’m not sure what might be missing(?)


#5

Do you mean that the entire flow works as expected if you use the app owner’s account, but you get the invalid token message for any other account?


#6

I guess there are two things I would suggest at this point: 1.) Go to your application settings and use the “Reset keys” tab to reset your consumer key and secret, then update those values in the app and verify that you still see the same behavior. 2.) Try passing oauth_callback in your request_token call. Honestly I don’t think this will make a difference, but I want to try and be as rigorous as I can here.

Thanks for your help in debugging!


#7

@kurrik, re: first response:
Yes, that’s exactly what’s happening. It works great if I login using the app owner’s account, but any other twitter user gets this invalid token / 401 message.

re: second response:

  1. I will try resetting the keys and see if that helps. I think I may have tried that once before but I’ll give it a whirl again.

  2. I tried that once since I noticed the docs say it’s “better to always pass the callback in the oauth header”, but when I did I got a “oh no, this page doesn’t exist” error from Twitter when it tried to redirect to my callback. It was trying to go to api.twitter.com/oauth/(url encoded version of my callback address). I’m not sure why that was happening either, but I took that back out and it seems to get the callback address from the twitter app configuration.

A more in-depth explanation of what I mean above… let’s say my callback was http://mysite.com/twitter/oauth.

So I would send oauth_callback=“http%3A%2F%2Fmysite.com%2Ftwitter%2Foauth” as part of the OAuth Authentication header.

When I did that, twitter didn’t appear to properly redirect back to my callback address… instead, the address it tried to goto was api.twitter.com/oauth/http%3A%2F%2Fmysite.com%2Ftwitter%2Foauth
which resulted in a page not found error from twitter. (that may not be the exact twitter url, but that was the general idea.)

I’ll try those 2 things again tomorrow though and I’ll post back what I find (and get the exact error message / url that’s being generated if I include oauth_callback ). I’m tired and done messing w/it for tonite :smiley:

Thanx for your assistance!

Hey, just out of curiousity, does it matter if I’m sending HTTP/1.0 or HTTP/1.1 in my requests to twitter? I noticed in the docs that the examples are always using 1.1 so I modified the code on my site to send 1.1 as well, but the default that drupal sends is 1.0. So if twitter will work both ways, I guess I’d rather go back to 1.0 just to be compliant w/the rest of drupal. :wink: thx!


#8

Is there any chance of getting a HTTP dump of the request you’re sending? If Twitter is trying to redirect to api.twitter.com/oauth/http%3A%2F%2Fmysite.com%2Ftwitter%2Foauth (which shouldn’t happen even in case of an error) then it sounds like you’re encoding an extra time somewhere. Perhaps the library you’re using to make the request completes an encoding step on its own?

It shouldn’t matter between HTTP/1.0 or 1.1, but please let me know if you see any differences.


#9

Has this issue been resolved? I’m experiencing the same problem, however mine doesn’t work with the app-creator account either. Everything seems okay until the final step of requesting access tokens after the user signs in and grants approval. I get the callback with the oauth_token and oauth_verifier but the POST request to access_token endpoint results in this response:

<?xml version="1.0" encoding="UTF-8"?> Invalid / expired Token /oauth/access_token

This is my POST request to https://api.twitter.com/oauth/access_token:

Twitter (Step 3): Requesting access tokens…
========== KQOAuthRequest has the following parameters:

  • “oauth_consumer_key” : “ZWjNcOF47xk3Whv5y2NuBw”
  • “oauth_nonce” : “843911286”
  • “oauth_signature_method” : “HMAC-SHA1”
  • “oauth_timestamp” : “1332347746”
  • “oauth_token” : “eNsYdJbWOP67v9h44znZjf3EBKfXW5dCj48lvs2VXso”
  • “oauth_verifier” : “NplHN099APTNdURBIWeZNJkMSbX7Rej7UWaMy8uVs8”
  • “oauth_version” : “1.0”

========== KQOAuthRequest has the following base string:
“POST&https%3A%2F%2Fapi.twitter.com%2Foauth%2Faccess_token&oauth_consumer_key%3DZWjNcOF47xk3Whv5y2NuBw%26oauth_nonce%3D843911286%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1332347746%26oauth_token%3DeNsYdJbWOP67v9h44znZjf3EBKfXW5dCj48lvs2VXso%26oauth_verifier%3DNplHN099APTNdURBIWeZNJkMSbX7Rej7UWaMy8uVs8%26oauth_version%3D1.0”

========== KQOAuthRequest has the following signature:

  • Signature : “JZLOnPSH1GD7u503HOahggg7UCQ%3D”

Any ideas would be appreciated, been pulling my hair over this for 3 days.


#10

Hi All,

I too faced the same problem, but after debugging a lot able to find the solution and it worked fine for me.


#11

What is the point of telling us that you found the solution if you are not sharing that solution ?


#12

@ProactiveTest
That’s my oppinion too. Why say that in this board. If you find a solution it would be very nice to let us know what was wrong, so that everybody can compare your solution with his code and maybe found the error in his/her code.

I’m facing this problem for an amount of months and it get’s me absolutly on the nerves and when then anyone has a solution and don’t share it my personal volcano explose.


#13

Hi,
Did you mention a callback url in the application settings?
Is the callback url and oauth_callback the same ?


#14

I’m experiencing the same using a python oath2 library the pyramid framework. The authentication works fine when I use the inbuilt web server - waitress, but I get the 401 invalid token error when I run the app via uwsgi behind an nginx web server. I suspect nginx is not sending some of the headers if it helps anyone witha similar setup.


#15

I had the same problem using Twitter4J. Here’s what solved the problem for me:

  1. Set the callback parameter in the script

    Twitter twitter = new TwitterFactory() …
    … twitter.getOAuthRequestToken(CALLBACK_URL)
  2. Set the callback parameter on the application developer dashboard

That’s it.

EDIT: I don’t know if this actually fixes the problem. Though my program experienced the same error, it was because I did not pass along the initial request token.


#16

Hello All

today we re dong some changes in code, we change the library and now we are getting authentication but after authentication we are getting another error which is as below.

stdClass Object ( [errors] => Array ( [0] => stdClass Object ( [message] => Invalid or expired token [code] => 89 ) ) )

Can anyone please help me to solve this issue ?


#17

I had the same problem using nginx, php-fpm and laravel.

Fixed by editing the nginx conf file:

try_files $uri $uri/ /index.php?$query_string;

instead of
try_files $uri $uri/ /index.php;