Authorization with twitter 3.0 no longer works in stateless solution. PLEASE HELP!!!

Feb 17, 2014 at 7:16 PM
In September, I developed an authorization page that used IOAuthCredentials and it worked fine. Since then, I upgraded the nuget version and everything broke. The IOAythCredentials interface is gone. I am not sure what replaced it but the only solution that seems to be documented is using SessionStateCredentialStore which breaks in my stateless/Sessionless Web.API MVC project.

THE OLD CODE THAT USED TO WORK but no longer does:

private readonly IOAuthCredentials _credentials = new InMemoryCredentials();
    protected void Page_Load(object sender, EventArgs e)
{
      var motweet = new TwitterAuthorizationViewModel();

        _credentials.ConsumerKey = _consumerKey;
        _credentials.ConsumerSecret = _consumerSecret;
        _auth = new MvcAuthorizer { Credentials = _credentials };

        _auth.CompleteAuthorization(Request.Url);

        if (_auth.IsAuthorized)
        {
            //save key to database
            RegisterClientCredentials(motweet);
        }
        else { return Request.Url == null ? View(motweet) : _auth.BeginAuthorization(Request.Url); }

        return View(motweet);
}

The new code:
    [OperationContract(AsyncPattern = true)]
    public async Task<ActionResult> BeginAsync()
    {


        var auth = new MvcAuthorizer 
        {
            CredentialStore = new InMemoryCredentialStore
            {
                ConsumerKey = _consumerKey,
                ConsumerSecret = _consumerSecret//,
                //OAuthToken = _consumerAccessToken,
                //OAuthTokenSecret = _consumerAccessTokenSecret
            }
        };
        string twitterCallbackUrl = Request.Url.ToString().Replace("Begin", "Complete");
        return await auth.BeginAuthorizationAsync(new Uri(twitterCallbackUrl));
   }


    public async Task<ActionResult> CompleteAsync()
    {
        var auth = new MvcAuthorizer 
        {
            CredentialStore = new InMemoryCredentialStore()
        };

        await auth.CompleteAuthorizeAsync(Request.Url);

        //Saver to db
        RegisterClientCredentials(auth);

        return RedirectToAction("Index", "Home");
    }
I have tried several different ways to get this working with a Sessionless/Stateless project and cannot find good examples that work.

PLEASE HELP!!!!
Coordinator
Feb 17, 2014 at 8:23 PM
Hi,

LINQ to Twitter v3.0 is async. There are two ASP,NET demos in the downloadable source code: Linq2TwitterDemos_WebForms and Linq2TwitterDemos_Mvc. You might want to run those to get some ideas.

@JoeMayo
Feb 17, 2014 at 9:29 PM
Hi Joe, Thanks for the quick reply. I looked at both solutions and they both use "SessionStateCredentialStore". My solution has session state turned off in the config files. When I try to "new" up the SessionStateCredentialStore object, it throws a null objet exception. The consumer keys cannot be set when session is turned off.

Any ideas with using inMemory solutions?

Thanks,

Stephan
Mar 4, 2014 at 7:46 PM
Edited Mar 4, 2014 at 7:47 PM
This worked for me on MVC5


public async Task<ActionResult> LinkToTwitter()
        {
            auth = new MvcSignInAuthorizer
            {
                CredentialStore = new InMemoryCredentialStore()
                {
                    ConsumerKey = WebConfig.TwitterConsumerKey,
                    ConsumerSecret = WebConfig.TwitterConsumerSecret
                }
            };
            string twitterCallbackUrl = Request.Url.ToString().Replace("LinkToTwitter", "CompleteLinkToTwitter");
            return await auth.BeginAuthorizationAsync(new Uri(twitterCallbackUrl));

        }

        public async Task<ActionResult> CompleteLinkToTwitter(string oauth_token, string oauth_verifier)
        {
            var auth = new MvcSignInAuthorizer
            {
                CredentialStore = new InMemoryCredentialStore()
                {
                    ConsumerKey = WebConfig.TwitterConsumerKey,
                    ConsumerSecret = WebConfig.TwitterConsumerSecret,
                    OAuthToken = oauth_token
                }
            };

            await auth.CompleteAuthorizeAsync(Request.Url);
        }
May 10, 2014 at 4:53 PM
Ok, I thought I had one more last thing to try but I still get the same error.

When I try this code (and I verify that the auth is not null and my consumer key/secret is correct) I get:

Object reference not set to an instance of an object.

On line:
return await auth.BeginAuthorizationAsync(new Uri(twitterCallbackUrl)); 
I'm using MVC5 and tried this on IISExpress, regular IIS with 'localhost' and '127.0.0.1' and still no luck.
public async Task<ActionResult> LinkToTwitter()
        {
            auth = new MvcSignInAuthorizer
            {
                CredentialStore = new InMemoryCredentialStore()
                {
                    ConsumerKey = WebConfig.TwitterConsumerKey,
                    ConsumerSecret = WebConfig.TwitterConsumerSecret
                }
            };
            string twitterCallbackUrl = Request.Url.ToString().Replace("LinkToTwitter", "CompleteLinkToTwitter");
            return await auth.BeginAuthorizationAsync(new Uri(twitterCallbackUrl));

        }

        public async Task<ActionResult> CompleteLinkToTwitter(string oauth_token, string oauth_verifier)
        {
            var auth = new MvcSignInAuthorizer
            {
                CredentialStore = new InMemoryCredentialStore()
                {
                    ConsumerKey = WebConfig.TwitterConsumerKey,
                    ConsumerSecret = WebConfig.TwitterConsumerSecret,
                    OAuthToken = oauth_token
                }
            };

            await auth.CompleteAuthorizeAsync(Request.Url);
        }
I've spent hours on this. What am I missing?
Coordinator
May 10, 2014 at 5:12 PM
Try a SessionStateCredentialStore. Do you have a stack trace?
May 10, 2014 at 5:15 PM
Edited May 10, 2014 at 5:18 PM
Actually, I didn't notice that their code was using InMemoryCredentialStore. I'm using your original sample code (below).
 var auth = new MvcAuthorizer
                {
                    CredentialStore = new SessionStateCredentialStore
                    {
                        ConsumerKey = ConfigurationManager.AppSettings["consumerKey"],
                        ConsumerSecret = ConfigurationManager.AppSettings["consumerSecret"]
                    }
                };

                string twitterCallbackUrl = Request.Url.ToString().Replace("Begin", "Complete");
                return await auth.BeginAuthorizationAsync(new Uri(twitterCallbackUrl));
Here's the stacktrace:

[NullReferenceException: Object reference not set to an instance of an object.]
LinqToTwitter.MvcAuthorizer.<BeginAuthorizationAsync>b__3(String authUrl) +31
LinqToTwitter.<BeginAuthorizeAsync>d__5.MoveNext() +721
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +144
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +84
LinqToTwitter.<BeginAuthorizationAsync>d__5.MoveNext() +442
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +144
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +84
System.Runtime.CompilerServices.TaskAwaiter1.GetResult() +49
TS.Controllers.<BeginAsync>d__2.MoveNext() in d:\Projects\TS\Controllers\OAuthController.cs:50
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +144
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +84
System.Web.Mvc.Async.TaskAsyncActionDescriptor.EndExecute(IAsyncResult asyncResult) +143
System.Web.Mvc.Async.<>c__DisplayClass37.<BeginInvokeAsynchronousActionMethod>b__36(IAsyncResult asyncResult) +23
System.Web.Mvc.Async.AsyncInvocationWithFilters.<InvokeActionMethodFilterAsynchronouslyRecursive>b__3f() +112
System.Web.Mvc.Async.<>c__DisplayClass48.<InvokeActionMethodFilterAsynchronouslyRecursive>b__41() +452
System.Web.Mvc.Async.<>c__DisplayClass33.<BeginInvokeActionMethodWithFilters>b__32(IAsyncResult asyncResult) +15
System.Web.Mvc.Async.<>c__DisplayClass2b.<BeginInvokeAction>b__1c() +37
System.Web.Mvc.Async.<>c__DisplayClass21.<BeginInvokeAction>b__1e(IAsyncResult asyncResult) +241
System.Web.Mvc.Controller.<BeginExecuteCore>b__1d(IAsyncResult asyncResult, ExecuteCoreState innerState) +29
System.Web.Mvc.Async.WrappedAsyncVoid
1.CallEndDelegate(IAsyncResult asyncResult) +111
System.Web.Mvc.Controller.EndExecuteCore(IAsyncResult asyncResult) +53
System.Web.Mvc.Async.WrappedAsyncVoid1.CallEndDelegate(IAsyncResult asyncResult) +19
System.Web.Mvc.MvcHandler.<BeginProcessRequest>b__5(IAsyncResult asyncResult, ProcessRequestState innerState) +51
System.Web.Mvc.Async.WrappedAsyncVoid
1.CallEndDelegate(IAsyncResult asyncResult) +111
System.Web.CallHandlerExecutionStep.OnAsyncHandlerCompletion(IAsyncResult ar) +282
May 10, 2014 at 5:29 PM
By the way, I'm using the latest from NuGet.

LinqToTwitter.AspNet 3.0.2.12655
LinqToTwitterPcl 3.0.2.12654

I'm able to query Twitter's API to get data but not able to authorize a Twitter user and get their Token because of the above error.

Thanks,
Craig
May 10, 2014 at 5:41 PM
Edited May 10, 2014 at 6:27 PM
I updated the solution to use SessionState = StateServer and that didn't help either.
Coordinator
May 10, 2014 at 6:57 PM
I made a recent update that affects async code and should fix some deadlock problems in that area. I don't know if it's related to what you're seeing, but you can download the source code and build the LinqToTwitterPcl project to see.
May 10, 2014 at 7:49 PM
Does that require windows 8?

Thanks
Craig

Sent from my iPhone

Coordinator
May 10, 2014 at 8:32 PM
The solution contains projects for Windows 8 and other technologies. However, you can just open the LinqToTwitterPcl project and build it.
May 11, 2014 at 1:04 AM
Edited May 11, 2014 at 1:08 AM
I think we're getting closer to the issue. I stepped through the code (added your projects to my solution).

I checked the authURL and it's actually fine and works.

https://api.twitter.com/oauth/authorize?oauth_token=3XWgguwPRBVy933xNlsAK1NRBrZiSilLeq8cnYANE

But when I hit this line it fails with the same error I was experiencing this morning.
HttpContext.Current.Response.Redirect(authUrl, false); 
if (GoToTwitterAuthorization == null)
                GoToTwitterAuthorization = authUrl =>
                        HttpContext.Current.Response.Redirect(authUrl, false); 
Coordinator
May 11, 2014 at 2:49 AM
I still can't repro yet. Can you try the Linq2TwitterDemos_MVC project? All you have to do is comment the MvcAuthorizer and uncomment the MvcSignInAuthorizer line in OAuthController.
using System;
using System.Configuration;
using System.Linq;
using System.Threading.Tasks;
using System.Web.Mvc;
using LinqToTwitter;

namespace Linq2TwitterDemos_MVC.Controllers
{
    public class OAuthController : AsyncController
    {
        public ActionResult Index()
        {
            return View();
        }

        public async Task<ActionResult> BeginAsync()
        {
            //var auth = new MvcSignInAuthorizer
            var auth = new MvcAuthorizer
            {
                CredentialStore = new SessionStateCredentialStore
                {
                    ConsumerKey = ConfigurationManager.AppSettings["consumerKey"],
                    ConsumerSecret = ConfigurationManager.AppSettings["consumerSecret"]
                }
            };

            string twitterCallbackUrl = Request.Url.ToString().Replace("Begin", "Complete");
            return await auth.BeginAuthorizationAsync(new Uri(twitterCallbackUrl));
        }

        public async Task<ActionResult> CompleteAsync()
        {
            var auth = new MvcAuthorizer
            {
                CredentialStore = new SessionStateCredentialStore()
            };

            await auth.CompleteAuthorizeAsync(Request.Url);

            // This is how you access credentials after authorization.
            // The oauthToken and oauthTokenSecret do not expire.
            // You can use the userID to associate the credentials with the user.
            // You can save credentials any way you want - database, 
            //   isolated storage, etc. - it's up to you.
            // You can retrieve and load all 4 credentials on subsequent 
            //   queries to avoid the need to re-authorize.
            // When you've loaded all 4 credentials, LINQ to Twitter will let 
            //   you make queries without re-authorizing.
            //
            //var credentials = auth.CredentialStore;
            //string oauthToken = credentials.OAuthToken;
            //string oauthTokenSecret = credentials.OAuthTokenSecret;
            //string screenName = credentials.ScreenName;
            //ulong userID = credentials.UserID;
            //

            return RedirectToAction("Index", "Home");
        }
    }
}
May 11, 2014 at 8:22 PM
First off, Joe, you rock! Thanks for the prompt replies. I really appreciate that!

Problem is solved by adding this to my web.config
<httpRuntime targetFramework="4.5" />
Thanks again!
Craig