1

I have added facebook and google and they work just fine (I use the latest Owin.Security.Providers 3.0.1).

But once I add yahoo google returns a blank page after signin (I noticed that it only happens if I sign in with yahoo first and the with google - testing on my localhost)

It feels like the yahoo provider does something to fu ck up the google provider...

If I move the yahoo code to the end, than yahoo does the same thing and I get a blank page after sign in (and google work fine in this scenario)

I also saw the class names YahooAuthenticationOptions and GoogleOAuth2AuthenticationOptions - which makes me wonder why the yahoo class is not named YahooOAuth2AuthenticationOptions...

Is there a better yahoo provider that I can try or any idea what else to try?

This is the code I'm using:

        #region Yahoo

        var yahooAuthOptions = new YahooAuthenticationOptions(); // Same issue if using: Owin.Security.Providers.Yahoo.YahooAuthenticationOptions();
        yahooAuthOptions.ConsumerKey = externalProviderManager.YahooAuthConsumerKey;
        yahooAuthOptions.ConsumerSecret = externalProviderManager.YahooAuthConsumerSecret;
        yahooAuthOptions.CallbackPath = new PathString("/signin-external"); // Note: by default yahoo is looking for a route named "signin-yahoo"

        yahooAuthOptions.Provider = new YahooAuthenticationProvider()
        {
            OnAuthenticated = (context) =>
            {
                foreach (var claim in context.User)
                {
                    var claimType = string.Format("urn:yahoo:{0}", claim.Key);
                    var claimValue = claim.Value.ToString();
                    if (!context.Identity.HasClaim(claimType, claimValue))
                        context.Identity.AddClaim(new System.Security.Claims.Claim(claimType, claimValue, "XmlSchemaString", "Yahoo"));
                }

                return Task.FromResult(0);
            }
        };
        app.UseYahooAuthentication(yahooAuthOptions);

        #endregion Yahoo

        #region Google

        var googleAuthOptions = new GoogleOAuth2AuthenticationOptions();
        googleAuthOptions.ClientId = externalProviderManager.GoogleAuthClientId;
        googleAuthOptions.ClientSecret = externalProviderManager.GoogleAuthClientSecret;
        googleAuthOptions.CallbackPath = new PathString("/signin-external");

        // googleAuthOptions.Scope.Add("https://www.googleapis.com/auth/plus.me"); // Know who you are on Google
        googleAuthOptions.Scope.Add("https://www.googleapis.com/auth/plus.login"); // Know your basic profile info and list of people in your circles.
        googleAuthOptions.Scope.Add("https://www.googleapis.com/auth/userinfo.email"); // View your email address
        googleAuthOptions.Scope.Add("https://www.googleapis.com/auth/userinfo.profile"); // View your basic profile info

        googleAuthOptions.Provider = new GoogleOAuth2AuthenticationProvider
        {
            OnAuthenticated = (context) =>
            {
                context.Identity.AddClaim(new System.Security.Claims.Claim("GoogleAccessToken", context.AccessToken));

                var expiryDuration = context.ExpiresIn ?? new TimeSpan();
                context.Identity.AddClaim(new Claim("urn:google:expires_in", DateTime.UtcNow.Add(expiryDuration).ToString(CultureInfo.InvariantCulture)));

                if (context.Email != null) context.Identity.AddClaim(new Claim("urn:google:email", context.Email));
                if (context.Id != null) context.Identity.AddClaim(new Claim("urn:google:id", context.Id));
                if (context.GivenName != null) context.Identity.AddClaim(new Claim("urn:google:given_name", context.GivenName));
                if (context.FamilyName != null) context.Identity.AddClaim(new Claim("urn:google:family_name", context.FamilyName));
                if (context.Name != null) context.Identity.AddClaim(new Claim("urn:google:name", context.Name));
                if (context.Profile != null) context.Identity.AddClaim(new Claim("urn:google:profile", context.Profile));

                // Note: for the birthday value (yyyy/mm/dd) - make sure Google Plus profile allows sharing birthday with public

                // Add all other available claims
                foreach (var claim in context.User)
                {
                    var claimType = string.Format("urn:google:{0}", claim.Key);
                    var claimValue = claim.Value.ToString();
                    if (!context.Identity.HasClaim(claimType, claimValue))
                        context.Identity.AddClaim(new System.Security.Claims.Claim(claimType, claimValue, "XmlSchemaString", "Google"));
                }

                return Task.FromResult(0);
            }
        };
        app.UseGoogleAuthentication(googleAuthOptions);

        #endregion Google

        #region Facebook

        var facebookAuthOptions = new FacebookAuthenticationOptions();
        facebookAuthOptions.AppId = externalProviderManager.FacebookAuthAppId;
        facebookAuthOptions.AppSecret = externalProviderManager.FacebookAuthAppSecret;
        facebookAuthOptions.SendAppSecretProof = true;

        // public_profile (Default) includes: id,name,first_name,last_name,age_range,link,gender,locale,timezone,updated_time,verified
        facebookAuthOptions.Scope.Add("public_profile");
        facebookAuthOptions.Scope.Add("email");
        facebookAuthOptions.Scope.Add("user_birthday");
        facebookAuthOptions.Scope.Add("user_location"); // current city through the location field on the User object

        facebookAuthOptions.Provider = new FacebookAuthenticationProvider()
        {
            OnAuthenticated = (context) =>
            {
                // http://stackoverflow.com/questions/7999934/facebook-c-sharp-sdk-problems-getting-user-email/8013211#8013211
                // http://blogs.msdn.com/b/webdev/archive/2013/10/16/get-more-information-from-social-providers-used-in-the-vs-2013-project-templates.aspx
                // Get the access token from FB and store it in the database and use FacebookC# SDK to get more information about the user
                context.Identity.AddClaim(new System.Security.Claims.Claim("FacebookAccessToken", context.AccessToken));

                var expiryDuration = context.ExpiresIn ?? new TimeSpan();
                context.Identity.AddClaim(new Claim("urn:facebook:expires_in", DateTime.UtcNow.Add(expiryDuration).ToString(CultureInfo.InvariantCulture)));

                // Add all other available claims
                foreach (var claim in context.User)
                {
                    var claimType = string.Format("urn:facebook:{0}", claim.Key);
                    var claimValue = claim.Value.ToString();
                    if (!context.Identity.HasClaim(claimType, claimValue))
                        context.Identity.AddClaim(new System.Security.Claims.Claim(claimType, claimValue, "XmlSchemaString", "Facebook"));
                }

                return Task.FromResult(0);
            }
        };
        app.UseFacebookAuthentication(facebookAuthOptions);

        #endregion Facebook

UPDATE: in RouteConfig.cs

#region External Sign-in API redirects

routes.MapRoute(
    name: "External Sign-in API redirect for Facebook",
    url: "signin-facebook",
    defaults: new { controller = "Account", action = "SignUpConnectRedirect", returnUrl = UrlParameter.Optional }
    );

routes.MapRoute(
    name: "External Sign-in API redirect for Google",
    url: "signin-google",
    defaults: new { controller = "Account", action = "SignUpConnectRedirect", returnUrl = UrlParameter.Optional }
    );

routes.MapRoute(
    name: "External Sign-in API redirect for LinkedIn",
    url: "signin-linkedin",
    defaults: new { controller = "Account", action = "SignUpConnectRedirect", returnUrl = UrlParameter.Optional }
    );

routes.MapRoute(
    name: "External Sign-in API redirect for Microsoft Live",
    url: "signin-microsoft",
    defaults: new { controller = "Account", action = "SignUpConnectRedirect", returnUrl = UrlParameter.Optional }
    );

routes.MapRoute(
    name: "External Sign-in API redirect for Twitter",
    url: "signin-twitter",
    defaults: new { controller = "Account", action = "SignUpConnectRedirect", returnUrl = UrlParameter.Optional }
    );

routes.MapRoute(
    name: "External Sign-in API redirect for Yahoo",
    url: "signin-yahoo",
    defaults: new { controller = "Account", action = "SignUpConnectRedirect", returnUrl = UrlParameter.Optional }
    );

#endregion External Sign-in API redirects
Yovav
  • 2,557
  • 2
  • 32
  • 53
  • In my tests the Yahoo oauth never hits your Account controller..."SignUpConnectRedirect". I can't see any external claims. – Tom McDonald Aug 23 '16 at 13:18

1 Answers1

1

That was a tough one to solve...

I was trying to use a catch all callback URI for all my external providers,

Once I created an entry for each provider separately in RouteConfig.cs everything works like a charm

Also of course - update all the callback URIs at the provider app interfaces...

Yovav
  • 2,557
  • 2
  • 32
  • 53