Base solution for your next web application
Open Closed

Error on Social Registration #1483


User avatar
0
kythor created

I'm trying to setup registration through social login, twitter in this case.

I added the correct keys in web.config. had to change the code in Startup.cs like this:

private static TwitterAuthenticationOptions CreateTwitterAuthOptions()
        {
            return new TwitterAuthenticationOptions
            {
                ConsumerKey = ConfigurationManager.AppSettings["ExternalAuth.Twitter.ConsumerKey"],
                ConsumerSecret = ConfigurationManager.AppSettings["ExternalAuth.Twitter.ConsumerSecret"],
                BackchannelCertificateValidator = new Microsoft.Owin.Security.CertificateSubjectKeyIdentifierValidator(new[]
                {
                    "A5EF0B11CEC04103A34A659048B21CE0572D7D47", // VeriSign Class 3 Secure Server CA - G2
                    "0D445C165344C1827E1D20AB25F40163D8BE79A5", // VeriSign Class 3 Secure Server CA - G3
                    "7FD365A7C2DDECBBF03009F34339FA02AF333133", // VeriSign Class 3 Public Primary Certification Authority - G5
                    "39A55D933676616E73A761DFA16A7E59CDE66FAD", // Symantec Class 3 Secure Server CA - G4
                    "‎add53f6680fe66e383cbac3e60922e3b4c412bed", // Symantec Class 3 EV SSL CA - G3
                    "4eb6d578499b1ccf5f581ead56be3d9b6744a5e5", // VeriSign Class 3 Primary CA - G5
                    "5168FF90AF0207753CCCD9656462A212B859723B", // DigiCert SHA2 High Assurance Server C‎A 
                    "B13EC36903F8BF4701D498261A0802EF63642BC3" // DigiCert High Assurance EV Root CA
                })
            };
        }

I can click on the twitter icon, and it correctly redirects from twitter back to the application. But it doesnt fill in the email address automatically and when I click on SUBMIT button I get an error:

[NullReferenceException: Object reference not set to an instance of an object.]
   Abp.Web.Mvc.Controllers.AbpController.OnException(ExceptionContext context) in D:\Halil\GitHub\aspnetboilerplate\src\Abp.Web.Mvc\Web\Mvc\Controllers\AbpController.cs:339
   Castle.DynamicProxy.AbstractInvocation.Proceed() +110
   Castle.DynamicProxy.AbstractInvocation.Proceed() +447
   System.Web.Mvc.ControllerActionInvoker.InvokeExceptionFilters(ControllerContext controllerContext, IList`1 filters, Exception exception) +174
   System.Web.Mvc.Async.<>c__DisplayClass21.<BeginInvokeAction>b__19(AsyncCallback asyncCallback, Object asyncState) +1134
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +169
   System.Web.Mvc.Async.AsyncControllerActionInvoker.BeginInvokeAction(ControllerContext controllerContext, String actionName, AsyncCallback callback, Object state) +454
   System.Web.Mvc.Controller.<BeginExecuteCore>b__1c(AsyncCallback asyncCallback, Object asyncState, ExecuteCoreState innerState) +41
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +64
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +169
   System.Web.Mvc.Controller.BeginExecuteCore(AsyncCallback callback, Object state) +893
   Castle.Proxies.Invocations.Controller_BeginExecuteCore.InvokeMethodOnTarget() +116
   Castle.DynamicProxy.AbstractInvocation.Proceed() +111
   Castle.DynamicProxy.AbstractInvocation.Proceed() +448
   Castle.Proxies.AccountControllerProxy.BeginExecuteCore(AsyncCallback callback, Object state) +206
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +170
   System.Web.Mvc.Controller.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state) +711
   Castle.Proxies.Invocations.Controller_BeginExecute.InvokeMethodOnTarget() +168
   Castle.DynamicProxy.AbstractInvocation.Proceed() +111
   Castle.DynamicProxy.AbstractInvocation.Proceed() +448
   Castle.Proxies.AccountControllerProxy.BeginExecute(RequestContext requestContext, AsyncCallback callback, Object state) +227
   System.Web.Mvc.MvcHandler.&lt;BeginProcessRequest&gt;b__4(AsyncCallback asyncCallback, Object asyncState, ProcessRequestState innerState) +94
   System.Web.Mvc.Async.WrappedAsyncVoid`1.CallBeginDelegate(AsyncCallback callback, Object callbackState) +65
   System.Web.Mvc.Async.WrappedAsyncResultBase`1.Begin(AsyncCallback callback, Object state, Int32 timeout) +170
   System.Web.Mvc.MvcHandler.BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, Object state) +571
   System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +921
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +137

any ideas how to get this right?


4 Answer(s)
  • User Avatar
    0
    kythor created

    After some debugging... this is what I found out:

    when trying to register through social login, twitter in this case, it takes me back to the Register View. The problem is that twitter does not return the email address. (any solution for that?)

    after filling in the register form with an email address, I continue by pressing Submit.

    it tries to validate the model through this:

    if (!UserName.Equals(EmailAddress) && emailRegex.IsMatch(UserName))
    

    but when initializing the Register View, the UserName is never set. So this line of code will always throw an error.

    When looking further, if I provide the "Default" tenant name in the code, it takes me directly to the "Register" method. and there again, the Username is being set using the EmailAddress

    model.UserName = model.EmailAddress
    

    but this is also Null...throwing in error as result.

    what to do? I think getting the email address from twitter would be the best solution, no?

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    First of all I got this solution from <a class="postlink" href="http://stackoverflow.com/questions/36330675/get-users-email-from-twitter-api-for-external-login-authentication-asp-net-mvc">http://stackoverflow.com/questions/3633 ... sp-net-mvc</a>.

    It seems like twitter does not return email address when user authanticates over twitter. You need to get email address from twitter after user allows your app for his/her account.

    To do that,

    1)Go to <a class="postlink" href="https://apps.twitter.com">https://apps.twitter.com</a> and under Permissions tab of your app, select "Request email addresses from users" checkbox on the bottom of page if you haven't alread done it. (It requires yo to provide Privacy Policy URL and Terms of Service URL but you can provide fake url's for test purposes).

    1. Set Provider property of TwitterAuthenticationOptions like this,
    Provider = new TwitterAuthenticationProvider
    {
        OnAuthenticated = (context) =>
        {
            context.Identity.AddClaim(new System.Security.Claims.Claim("urn:twitter:access_token", context.AccessToken));
            context.Identity.AddClaim(new System.Security.Claims.Claim("urn:twitter:access_secret", context.AccessTokenSecret));
            return Task.FromResult(0);
        }
    }
    
    1. in AccountController's ExternalLoginCallback action, you need to make a second request to twitter in order to get user's email.
    if (loginInfo.Login.LoginProvider.ToLower() == "twitter")
    {
        string access_token = loginInfo.ExternalIdentity.Claims.Where(x => x.Type == "urn:twitter:access_token").Select(x => x.Value).FirstOrDefault();
        string access_secret = loginInfo.ExternalIdentity.Claims.Where(x => x.Type == "urn:twitter:access_secret").Select(x => x.Value).FirstOrDefault();
        TwitterDto response = MyHelper.TwitterLogin(access_token, access_secret, ConfigurationManager.AppSettings["ExternalAuth.Twitter.ConsumerKey"], ConfigurationManager.AppSettings["ExternalAuth.Twitter.ConsumerSecret"]);
        loginInfo.Email = response.email;
    }
    
    1. Change Validate method of RegisterViewModel to
    var emailRegex = new Regex(@"^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$");
    if (!UserName.IsNullOrEmpty() && !UserName.Equals(EmailAddress) && emailRegex.IsMatch(UserName))
    {
        yield return new ValidationResult("Username cannot be an email address unless it's same with your email address !");
    }
    

    You can find TwitterDto class and TwitterLogin method to get user's twitter email in related stackoverflow post. You can use a twitter library for getting user's email address. Please let us know if you have any problems while implementing this.

  • User Avatar
    -1
    kythor created

    thanks for the help. I got it working, I can register automatically with my twitter account.

    Next problem is when I try to login with my Twitter account. This line in the code is called in method "ExternalLogin":

    var loginResult = await _userManager.LoginAsync(loginInfo.Login, tenancyName);
    

    but it always returns "UnknownExternalLogin", even though the account is registered and active...

    I'm wondering why loginInfo.Login is needed here, as it only holds "LoginProvider" and "ProviderKey". The actual UserInfo is in "loginInfo" itself.

    How to solve this?

  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi,

    Can you change this definition in accoun controller

    user.Logins = new List<UserLogin>
    {
        new UserLogin
        {
            LoginProvider = externalLoginInfo.Login.LoginProvider,
            ProviderKey = externalLoginInfo.Login.ProviderKey
        }
    };
    

    to

    user.Logins = new List<UserLogin>
    {
        new UserLogin
        {
            LoginProvider = externalLoginInfo.Login.LoginProvider,
            ProviderKey = externalLoginInfo.Login.ProviderKey,
            TenantId = tenant.Id
        }
    };
    

    we forgot to set tenantId when defining UserLogins. This will be fixed on the next release as well.