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 CA
"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.<BeginProcessRequest>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)
-
0
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?
-
0
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).
- 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); } }
- 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; }
- 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.
-
-1
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?
-
0
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.