Search code examples
c#twitteroauthasp.net-mvc-5linq-to-twitter

How to Authenticate a user with Twitter but not for Log in purposes


I have MVC5 installed and after a day i think it is time to reach out and get help, I've never been a OAuth Guru, other things yes but this no. And I have searched the internet but no one specifically focuses on the authentication process only and nothing else, or the discussion is so loose that i cannot grasp the concept of the logic for the specific framework I am attempt to learn.

I have a MVC controller and in my view when the user clicks the button to authenticate it is a simple action called Social. So my URL is http://localhost://mywebsite/enterprise/social i know this is school boy stuff but i need the help and I am starting from the beginning, this USE TO WORK IN 1.0 OAuth.

I do not want to use the Twitter for Log In, it is used for a custom Twitter box I've made years back and is used for tweeting, re-tweeting, basically same time as if they were on Twitter. IT IS NOT FOR THEIR LOG IN FOR THE SITE.

I want the use to be directed to Twitter, they sign in, redirected back to same MVC page, i capture tokens, save to my person storage, and were done.

All calls after that I make my own calls to the database and create my own cookies, for future calls, reason being it is already created so I will continue to use it, no special purpose.

So, here is my Controller, where am i going wrong?

public async Task Social(FormCollection form)
{

    //var auth = new MvcSignInAuthorizer
    var auth = new MvcAuthorizer
    {
        CredentialStore = new InMemoryCredentialStore
        {
            ConsumerKey = ConfigurationManager.AppSettings["twitterConsumerKey"],
            ConsumerSecret = ConfigurationManager.AppSettings["twitterConsumerSecret"]
        }
    };

    string twitterCallbackUrl = Request.Url.ToString().Replace("Begin", "Complete");
    auth.Callback = new Uri(twitterCallbackUrl);

    await auth.BeginAuthorizationAsync();
}
public async Task<PartialViewResult> CompleteAsync()
{
    var auth = new MvcAuthorizer
    {
        CredentialStore = new InMemoryCredentialStore()
    };

    await auth.CompleteAuthorizeAsync(HttpContext.Request.Url);

    var credentials = auth.CredentialStore;
    string oauthToken = credentials.OAuthToken,
           oauthTokenSecret = credentials.OAuthTokenSecret,
           screenName = credentials.ScreenName,
           userName = Membership.GetUser().UserName;
    ulong userID = credentials.UserID;


    System.Web.Profile.ProfileBase pro = System.Web.Profile.ProfileBase.Create(userName, true);
    pro.SetPropertyValue("twoauth_token", oauthToken);
    pro.SetPropertyValue("twtokensecret", oauthTokenSecret);
    pro.Save();

    HttpContext.Response.SetOauthFromCookie("twit", oauthTokenSecret, oauthToken, userName, true);

    ViewBag.IsTwitterConnected = IsTwitterConnected;
    return PartialView("_SocialPartial");
}

Here is my Startup.cs

    public partial class Startup
    {
        public void ConfigureAuth(IAppBuilder app)
        {
            // Use a cookie to temporarily store information about a user logging in with a third party login provider
            app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);



            app.UseTwitterAuthentication(
                new TwitterAuthenticationOptions
                {
                    ConsumerKey = ConfigurationManager.AppSettings["twitterConsumerKey"],
                    ConsumerSecret = ConfigurationManager.AppSettings["twitterConsumerSecret"],
                    Provider = new LinqToTwitterAuthenticationProvider()
                });

        }
    }
}

Rearrangement of the code to do what i need it to do with MVC 5 4.6.2 Framework would be AWESOME!


Solution

  • I'm not sure what you mean by "I do not want to use the Twitter for Log In". If you're referring to the old username/password credentials log-in, that's deprecated and you can't use it anyway. That System.Web.Profile... and SetOAuthFromCookie... are not part of LINQ to Twitter - is that perhaps what you were referring to as the Log In code?

    It sounds like you want to implement a normal OAuth workflow. In that case, I use an OAuthController to isolate the code and simplify my calling code, like this:

    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");
        }
    }
    

    Notice that the CredentialStore is a SessionStateCredentialStore, which persists your tokens in session state. If you don't like the way the credentials are saved, implement your own ICredentialStore - it's extensible like that. Also, look at the comments in CompleteAsync - they show you how to extract all of the credentials to persist in the DB (or the user-specific tokens in a cookie if you like).

    Since the credentials are in session state after authorization, any code that needs to run LINQ to Twitter can check to see if those credentials are available, like this:

            if (!new SessionStateCredentialStore().HasAllCredentials()) 
                return RedirectToAction("Index", "OAuth"); 
    

    With LINQ to Twitter, if the authorizer has all 4 credentials, you can instantiate a TwitterContext and do what you need. The HasAllCredentials() is what tells you if all 4 of those credentials are available. If not, start the OAuth process again. This demo takes the user to a page to start the authorization process manually, but you could have redirected to BeginAsync directly.

    Another improvement, in real code, would be to modify BeginAsync so it looks for a user's credentials from the database (or another store, such as a cookie), populates the SessionStateCredentialStore, and redirects back to the caller. If the user credentials aren't available, then let it take the user through the OAuth process and then save the credentials so that you don't have to do this again.

    If you would like to see the whole demo, visit the MVCDemo project in the LINQ to Twitter Samples folder.

    If you're receiving the error below, you should populate your 'Callback URL' in your twitter application settings. Here's a Q/A where someone had the same problem:

    Desktop applications only support the oauth_callback value 'oob'/oauth/request_token


    Receiving Server Error
    
    
    
    Server Error in '/' Application.
    <?xml version="1.0" encoding="UTF-8"?>
    <hash>
     <error>Desktop applications only support the oauth_callback value 'oob'</error>
     <request>/oauth/request_token</request>
    </hash>
    - Please visit the LINQ to Twitter FAQ (at the HelpLink) for help on resolving this error.
    Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
    
    Exception Details: LinqToTwitter.TwitterQueryException: <?xml version="1.0" encoding="UTF-8"?>
    <hash>
     <error>Desktop applications only support the oauth_callback value 'oob'</error>
     <request>/oauth/request_token</request>
    </hash>
    - Please visit the LINQ to Twitter FAQ (at the HelpLink) for help on resolving this error.
    
    Source Error:
    
    
    Line 55:         protected async void AuthorizeButton_Click(object sender, EventArgs e)
    Line 56:         {
    Line 57:             await auth.BeginAuthorizeAsync(Request.Url);
    Line 58:         }
    Line 59:     }
    
    
    Source File: D:\Users\Errrrrrr\Documents\visual studio 2015\ForTesting\LinqToTwitter-master\Samples\net46\CSharp\AspNetSamples\WebFormsDemo\OAuth.aspx.cs    Line: 57
    
    Stack Trace:
    
    
    [TwitterQueryException: <?xml version="1.0" encoding="UTF-8"?>
    <hash>
      <error>Desktop applications only support the oauth_callback value 'oob'</error>
      <request>/oauth/request_token</request>
    </hash>
     - Please visit the LINQ to Twitter FAQ (at the HelpLink) for help on resolving this error.]
       LinqToTwitter.Net.<HandleUnauthorizedAsync>d__4.MoveNext() +494
       System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +14139120
       System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +62
       LinqToTwitter.Net.<ThrowIfErrorAsync>d__0.MoveNext() +360
       System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +14139120
       System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +62
       LinqToTwitter.<HttpGetAsync>d__57.MoveNext() +1159
       System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +14139120
       System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +62
       LinqToTwitter.<GetRequestTokenAsync>d__50.MoveNext() +675
       System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +14139120
       System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +62
       LinqToTwitter.<BeginAuthorizeAsync>d__14.MoveNext() +568
       System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +14139120
       System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +62
       System.Runtime.CompilerServices.TaskAwaiter.GetResult() +28
       WebFormsDemos.<AuthorizeButton_Click>d__2.MoveNext() in D:\Users\Edddddd\Documents\visual studio 2015\ForTesting\LinqToTwitter-master\Samples\net46\CSharp\AspNetSamples\WebFormsDemo\OAuth.aspx.cs:57
       System.Runtime.CompilerServices.<>c.<ThrowAsync>b__6_0(Object state) +56
       System.Web.Util.SynchronizationHelper.SafeWrapCallback(Action action) +110
       System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +14139120
       System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +62
       System.Web.Util.WithinCancellableCallbackTaskAwaiter.GetResult() +32
       System.Web.UI.<ProcessRequestMainAsync>d__523.MoveNext() +7762