401 Not Authorized Exception when calling GetCredentialsFromVerifierCode

Oct 22, 2014 at 9:55 AM
Hi,

I trying to complete the Pin-Based authorization process with a call to GetCredentialsFromVerifierCode.

I've copied the code in below where I've tried setting the various credentials that I have at this stage in as many places as I can think of. I've also checked the time on my computers clock and it is in sync to the second with http://www.timeanddate.com/worldclock/uk/london.

Any other suggestions greatly appreciated.
Russell
TwitterCredentials.SetCredentials(accessToken, "", oauth_consumer_key, oauth_consumer_secret);


            var applicationCredentials = new TemporaryCredentials(oauth_consumer_key, oauth_consumer_secret);
            applicationCredentials.VerifierCode = authorisationPinCode;
            applicationCredentials.AuthorizationKey = accessToken;
            applicationCredentials.AuthorizationSecret = "";
            var result = CredentialsCreator.GetCredentialsFromVerifierCode(authorisationPinCode, applicationCredentials);
Coordinator
Oct 22, 2014 at 10:15 AM
Edited Oct 22, 2014 at 10:17 AM
Hello,

I have just tested the following code and this is working perfectly well:
// Consumer Key and Secret are specific to your application, you can get these on apps.twitter.com
var applicationCredentials = CredentialsCreator.GenerateApplicationCredentials(consumerKey, consumerSecret);
var url = CredentialsCreator.GetAuthorizationURL(applicationCredentials);

// Open the URL to authenticate yourself on Twitter
Process.Start(url);
Console.WriteLine("Please enter the captcha : ");
var captcha = Console.ReadLine();

// Here are the new credentials!
var newCredentials = CredentialsCreator.GetCredentialsFromVerifierCode(captcha, applicationCredentials);
Linvi
Oct 22, 2014 at 10:25 AM
Hi Linvi,

Thanks for your quick reply.

I've adjusted my code as follows, to match what you've done above, but I still got the Exception - Additional information: Operation is not valid due to the current state of the object., which when I look a bit closer, is a 401 WebException again.

There's not too much going on here, so I must admit i'm kind of stuck, what else could I look at that might help?

var applicationCredentials =
            CredentialsCreator.GenerateApplicationCredentials(oauth_consumer_key, oauth_consumer_secret);

        var result = CredentialsCreator.GetCredentialsFromVerifierCode(authorisationPinCode, applicationCredentials);
Many thanks,
Russell
Oct 22, 2014 at 10:29 AM
One thought, I'm running this process in 2 separate methods, because the details are linked to a website. This means first requesting the accessToken, which is stored. Then a page s displayed waiting for the user to enter the captcha pin, so it happens in 2 stages, rather than in the one process as you are showing above.

Is there something else that I need to set up on POST-back to the server for the second stage?
Coordinator
Oct 22, 2014 at 10:40 AM
Edited Oct 22, 2014 at 10:40 AM
Hi,

Are you calling the GetAuthorizationURL?
CredentialsCreator.GetAuthorizationURL(applicationCredentials);
This is important because by calling it, it will generate an authorization token that will be stored in the application credentials. This authorization token will be unique per URL generated for the authentication.

Try to copy and paste my code and check if this is working.

Linvi
Oct 22, 2014 at 10:57 AM
Hi,

I am indeed. I call this method in the first call back to the server, both methods copied in below.

I have run your code above and it works fine for me, so that's good.

It seems to me that the big difference here is your example is running in the same thread, where my code is taking place over 2 separate calls to the server. Is there therefore some kind of environment that I need to re-create for the second call to succeed?

Many thanks,
Russell
public OAuthDetails GenerateAuthorisationRequest()
        {



            var applicationCredentials = CredentialsCreator.GenerateApplicationCredentials(oauth_consumer_key, oauth_consumer_secret);
            var url = CredentialsCreator.GetAuthorizationURL(applicationCredentials);
            Console.WriteLine("Go on : {0}", url);
            Console.WriteLine("When redirected to your website copy and paste the URL: ");

            // Enter a value like: https://tweeetinvi.codeplex.com?oauth_token={tokenValue}&oauth_verifier={verifierValue}

                        
            var details = new OAuthDetails
                (
                    applicationCredentials.AuthorizationKey, 
                    "",
                    url
                );

            return details;


        }

        public OAuthDetails ConfirmAuthorisationRequest(string accessToken, string authorisationPinCode)
        {



            var applicationCredentials =
                CredentialsCreator.GenerateApplicationCredentials(oauth_consumer_key, oauth_consumer_secret);

            var result = CredentialsCreator.GetCredentialsFromVerifierCode(authorisationPinCode, applicationCredentials);
            Console.WriteLine("Access Token = {0}", result.AccessToken);
            Console.WriteLine("Access Token Secret = {0}", result.AccessTokenSecret);



            return OAuthDetails.Build
                (
                    accessToken,
                    result.AccessTokenSecret,
                    true
                );
        }
Coordinator
Oct 22, 2014 at 11:50 AM
Sorry I did not see your 2nd post when I published the previous answer.

As I tried to explain, a Captcha is linked to a specific URL that the user will be navigating to in order to see the captcha.
When calling CredentialsCreator.GetAuthorizationURL(applicationCredentials);, you are receiving an URL that twitter links to a specific captcha. The captcha and the URL are unique and will be generated each time you try to create get some user credentials.

The TwitterApplicationCredentials contains 2 information (AuthorizationToken, AuthorizationSecret) when receiving the URL from Twitter.
This allow Twitter to verify that the captcha was the one that was displayed on the page of the URL it provided with these 2 information.

As a result you will always have to use GetAuthorizationURL and it is not possible to request credentials the way you intended to do.

Linvi
Coordinator
Oct 22, 2014 at 12:08 PM
Again async :p

I can see what is wrong with your code.
As I tried to described previously, calling GetAuthorizationURL results in the applicationCredentials to have its AuthorizationToken value to be set.
In the second method you are creating a new ApplicationCredentials which will not be linked to the previous URL because the AuthorizationKey won't be set.

What you need to do is to store the information of the AuthorizationKey and AuthorizationSecret in your Session and set them back when the second method is invoked.
// In GenerateAuthorisationRequest after the call to GetAuthorizationURL
Session["AuthorizationKey"] = applicationCredentials.AuthorizationKey;
Session["AuthorizationSecret"] = applicationCredentials.AuthorizationSecret;

// In ConfirmAuthorisationRequest right after creating the applicationCredentials
applicationCredentials.AuthorizationKey = Session["AuthorizationKey"];
applicationCredentials.AuthorizationSecret = Session["AuthorizationSecret"];
Linvi
Marked as answer by russellagnew on 10/22/2014 at 6:58 AM
Oct 22, 2014 at 12:11 PM
Hi Linvi,

Thanks for that. I had noticed the same thing myself when stepping through your code, that secret field is only populated after the call to GetAuthorizationURL, I'm just working those details through now, but it looks promising!!! I'll let you know how it goes.

Many thanks,
Russell
Oct 22, 2014 at 2:58 PM
Hi Linvi,

Thanks for your patient help, picking up that AuthorisationSecret was the missing piece and now it works from end to end.

Keep up the great work on this lovely library.

All the best,
Russell

P.S. for anybody else following this, if it helps, here is the final working solution

public OAuthDetails GenerateAuthorisationRequest()
        {



            var applicationCredentials = CredentialsCreator.GenerateApplicationCredentials(oauth_consumer_key, oauth_consumer_secret);
            
            
            
            var url = CredentialsCreator.GetAuthorizationURL(applicationCredentials);
            
            /// Having made this call, you now have both the captcha URL, and the and the authorisation secret that is linked to this URL
            /// This secret is available from the previously created applicationCredentials

            
            var details = new OAuthDetails
                (
                    applicationCredentials.AuthorizationKey, 
                    "",
                    url
                );

            details.AuthorisationSecret = applicationCredentials.AuthorizationSecret;


            return details;


        }

        public OAuthDetails ConfirmAuthorisationRequest(string authorisationKey, string authorisationSecret, string authorisationPinCode)
        {



            var applicationCredentials =
                CredentialsCreator.GenerateApplicationCredentials(oauth_consumer_key, oauth_consumer_secret);

            //Now add the key and tmeporary authorisation secret that were generated from the previous call to GetAuthorizationURL
            applicationCredentials.AuthorizationKey    = authorisationKey;
            applicationCredentials.AuthorizationSecret = authorisationSecret;


            var result = CredentialsCreator.GetCredentialsFromVerifierCode(authorisationPinCode, applicationCredentials);
            //And now you should have the final secret that is needed to work with the API.

            return OAuthDetails.Build
                (
                    authorisationKey,
                    result.AccessTokenSecret,
                    true
                );
        }
Oct 30, 2014 at 7:19 AM
As for twitter OAuth... it do not require authorization secret in POST oauth/access_token (wich is actually performed on GetCredentialsFromVerifierCode call) but tweetinvi check for it presense . You can set it to dummy data and it will work. Imho, this check has to be fixed for this call.