19

I decided to give the new Google Oauth2 middleware a try and it has pretty much broken everything. Here is my provider config from startup.auth.cs.. When turned on, all of the providers including the google provider get a 500 internal server on Challenge. However the details of the internal server error are not available and I cant figure out how to turn on any debugging or tracing for the Katana middleware. Seems to me like they were in a rush to get the google Oauth middleware out the door.

  //// GOOGLE
        var googleOptions = new GoogleOAuth2AuthenticationOptions
        {
            ClientId = "228",
            ClientSecret = "k",
            CallbackPath = new PathString("/users/epsignin")
            SignInAsAuthenticationType = DefaultAuthenticationTypes.ExternalCookie,
            Provider = new GoogleOAuth2AuthenticationProvider
            {
                OnAuthenticated = context =>
                {
                    foreach (var x in context.User)
                    {
                        string claimType = string.Format("urn:google:{0}", x.Key);
                        string claimValue = x.Value.ToString();
                        if (!context.Identity.HasClaim(claimType, claimValue))
                            context.Identity.AddClaim(new Claim(claimType, claimValue, XmlSchemaString, "Google"));
                    }
                    return Task.FromResult(0);
                }
            }
        };

        app.UseGoogleAuthentication(googleOptions);

ActionMethod Code:

 [AllowAnonymous]
    public ActionResult ExternalProviderSignIn(string provider, string returnUrl)
    {
       var ctx = Request.GetOwinContext();
        ctx.Authentication.Challenge(
            new AuthenticationProperties
            {
                RedirectUri = Url.Action("EPSignIn", new { provider })
            },
            provider);
        return new HttpUnauthorizedResult();
    }
CrazyCoderz
  • 1,351
  • 1
  • 15
  • 30
  • I'm unsure what the specific issue is about - Can you try setting up symbols for the Google package and see where it is going wrong. Here are some instructions to setup symbols for katana - https://katanaproject.codeplex.com/wikipage?title=Debugging&referringTitle=Documentation – Praburaj Feb 24 '14 at 19:09
  • This appears to be an issue with CallBackPath. Is it possible that when the Callback Path is set for this provider it is setting that prop globally for all the providers. Have not dug into the src, just a thought... – CrazyCoderz Feb 24 '14 at 19:36
  • When you say prop globally for all providers => Do you want the same being set for all providers? There is no way to automatically set the same for all providers, but you can set them manually if you want. You should make sure you have this redirect uri being registered in the respective application setup in respective portals. Each of these middlewares have a default CallBackPath if you don't provide one explicitly. For instance this google middleware has a default value of /signin-google. – Praburaj Feb 24 '14 at 19:48
  • I don't think you understood my question. I do see the property for each of the providers. I was wondering if somehow setting the property for one provider was getting applied to all of the other Oauth2 providers. – CrazyCoderz Feb 24 '14 at 20:13
  • I wouldn't think so. I'm unable to reproduce this issue. Would you mind uploading a stand alone project which reproduces this issue somewhere to have a look? – Praburaj Feb 24 '14 at 21:15
  • Sure going to take me a bit still at work. – CrazyCoderz Feb 24 '14 at 22:59
  • @Praburaj Better late than never? https://github.com/Pritchard/Test-AspNetGoogleOAuth2Authentication –  Feb 26 '15 at 06:29

5 Answers5

29

This took me hours to figure out, but the issue is the CallbackPath as mentioned by @CrazyCoder. I realised that the CallbackPath in public void ConfigureAuth(IAppBuilder app) MUST be different to when it is being set in the ChallengeResult. If they are the same a 500 error is thrown in OWIN.

My code is for ConfigureAuth(IAppBuilder app) is

var googleOptions = new Microsoft.Owin.Security.Google.GoogleOAuth2AuthenticationOptions
{
    ClientId = "xxx",
    ClientSecret = "yyy",
    CallbackPath = new PathString("/callbacks/google"), //this is never called by MVC, but needs to be registered at your oAuth provider

    Provider = new GoogleOAuth2AuthenticationProvider
    {
        OnAuthenticated = (context) =>
        {
            context.Identity.AddClaim(new Claim("picture", context.User.GetValue("picture").ToString()));
            context.Identity.AddClaim(new Claim("profile", context.User.GetValue("profile").ToString()));
            return Task.FromResult(0);
        }      
    }
};

googleOptions.Scope.Add("email");

app.UseGoogleAuthentication(googleOptions);

My 'callbacks' Controller code is:

// GET: /callbacks/googlereturn - callback Action
[AllowAnonymous]
public async Task<ActionResult> googlereturn()
{
        return View();
}

//POST: /Account/GooglePlus
public ActionResult GooglePlus()
{
    return new ChallengeResult("Google", Request.Url.GetLeftPart(UriPartial.Authority) + "/callbacks/googlereturn", null);  
    //Needs to be a path to an Action that will handle the oAuth Provider callback
}

private class ChallengeResult : HttpUnauthorizedResult
{
    public ChallengeResult(string provider, string redirectUri)
        : this(provider, redirectUri, null)
    {
    }

    public ChallengeResult(string provider, string redirectUri, string userId)
    {
        LoginProvider = provider;
        RedirectUri = redirectUri;
        UserId = userId;
    }

    public string LoginProvider { get; set; }
    public string RedirectUri { get; set; }
    public string UserId { get; set; }

    public override void ExecuteResult(ControllerContext context)
    {
        var properties = new AuthenticationProperties() { RedirectUri = RedirectUri };
        if (UserId != null)
        {
            properties.Dictionary[XsrfKey] = UserId;
        }
        context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
    }
}
  • callbacks/google seems to handled by OWIN
  • callbacks/googlereturn seems to handled by MVC

It is all working now, although would love to know exactly what is happening 'under the bonnet'

My advice, unless you have another requirement, is to let OWIN use default redirect paths and make sure you don't use them yourself.

Bensmind
  • 169
  • 1
  • 7
Craig Champion
  • 442
  • 9
  • 16
  • Sorry, I forgot to answer this long ago. – CrazyCoderz Aug 14 '14 at 04:06
  • To clarify what fixed this for me: 1) Redirect URL setting in the account.live.com application area (API Settings) was set to "http:///Account/MicrosoftLoginCallback" 2) Callback path was set to "PathString("/Account/MicrosoftLoginCallback");" for IAppBuilder: app.UseMicrosoftAccountAuthentication(new MicrosoftAccountAuthenticationOptions {CallbackPath = msCallbackPath, ClientId =, ClientSecret =} 3) RedirectUri used for challenge had to be changed from "MicrosoftLoginCallback" to "MSLoginCallback", just in the AccountController action and the Authentication.Challenge() call. – Paul Schroeder Sep 22 '14 at 01:27
  • In short, when all three items above matched in name I was getting a 500 error and not much else to go on. Looking in Fiddler, I think that Microsoft is actually calling back to "Account/MicrosoftLoginCallback" (as configured for IAppBuilder). However, something in the infrastrucutre/provider might be mapping that to "Account/MSLoginCallback" (the actual controller action method I provided for the challenge AuthenticationProperties object. – Paul Schroeder Sep 22 '14 at 01:44
  • Have posted as a UserVoice bug, please UPVOTE, http://aspnet.uservoice.com/forums/41199-general-asp-net/suggestions/6487840-oauth-default-sample-project-gives-500-internal-se – OzBob Sep 26 '14 at 06:21
7

There is no need to specify CallbackPath in UseGoogleAuthentication:

CallbackPath = new PathString("/Account/ExternalLoginCallback")

Just keep the Google setting for Authorized redirect URIs as:

http(s)://yoururl:orPort/signin-google

Owin handles signin-google internally and redirects to the redirectUri as mentioned in your code for ChallengeResult class. Which is Account/ExternalLoginCallback.

Maximilian Ast
  • 3,369
  • 12
  • 36
  • 47
Jay Dubal
  • 241
  • 2
  • 8
  • 3
    This should be marked as the solution. Worked for me like a charm. – ejcortes Sep 08 '15 at 19:34
  • I would also note that the provider parameter when i.e. clicking the login button via the web browser should be "Google" when calling /Account/ExternalLogin (i.e. https://localhost:44300/Account/ExternalLogin?provider=Google&returnUrl=blah) – Jason Dufair Nov 13 '15 at 20:55
  • When you say Google setting what are you referring to? This answer seems to lack clarity for someone who hasn't any experience. Are you talking about CallBackPath? – jwize Apr 25 '16 at 23:25
  • The Google settings are the values you provide in the Google dev console when creating the oAuth token, https://developers.google.com/identity/sign-in/web/devconsole-project – Jay Dubal Apr 30 '16 at 02:13
  • This is the best answer of all. Lots of other answers suggest to add a route mapping to redirect the callback to the proper method in MVC but that just crashes the server down. – GETah May 26 '16 at 16:18
  • This is not working for me, it's not happening automatically. Following these instructions and the last step doesn't happen: http://www.oauthforaspnet.com/providers/google/guides/aspnet-mvc5/ – Nathan McKaskle Jun 08 '16 at 19:42
  • That was the case for me until I changed machines. I changed from VS2017 to VS2022, different port, but still on .Net 4.71. – Mike Apr 27 '22 at 20:35
  • Next, I updated the Authorized redirect URIs for my apps Google credentials. It should work just like it did on the old machine, yeah? NO. Just a mysterious 500 Server Error. Adding a dummy callback path doesn't make any sense to me; the challenge result uses /signin-google. However, using the dummy callback path makes it work. Can anyone explain that? Oh, I did move the keys out of StartupAuth to an external config file. – Mike Apr 27 '22 at 20:47
3

Got it working vanilla from the tutorial with ONE simple change - just posting this for any nubes to this approach. I think the problems related to oauth2 in this instance are largely fleshed out in the latest templates/apis - what I mean is, if you are starting from scratch, you may be in luck - read on:

I JUST did this tutorial https://azure.microsoft.com/en-us/documentation/articles/web-sites-dotnet-deploy-aspnet-mvc-app-membership-oauth-sql-database/

and referenced this also http://blogs.msdn.com/b/webdev/archive/2014/07/02/changes-to-google-oauth-2-0-and-updates-in-google-middleware-for-3-0-0-rc-release.aspx

The one change: it worked but ONLY after enabling google+ apis in the newest version of the google developer site.

(Just go to google api lib manager, sign in and search the apis directory for the google+ api).
Note: for me the Google+ api was disabled by default.

I did nothing else unique.

Cheers

Richard Strickland
  • 1,771
  • 17
  • 8
1

The answers given so far led me down a really dark path that I wish I had not traveled... the solution is simple make sure that the following 3 things match:

1) In the Google OATH Credentials (https://console.developers.google.com/):

2) In your AccountController:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider, string returnUrl)
{
    return new ChallengeResult(provider, 
        Url.Action("ExternalLoginCallback", "Account", 
        new { ReturnUrl = returnUrl }));
}

Notice the Action is "ExternalLoginCallback"

3) In your App_Start\Startup.Auth.cs

app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
{
    ClientId = "yourclientid.apps.googleusercontent.com",
    ClientSecret = "yoursecret",
    Provider = new GoogleOAuth2AuthenticationProvider(),
    CallbackPath = new PathString("/Account/ExternalLoginCallback")
});

Notice the CallbackPath again has the same PathString as the other 2

Finally, if you're still not getting it, set your authentication mode to None in your app Web.config

<authentication mode="None" />

to get some more details about the issue.

Serj Sagan
  • 28,927
  • 17
  • 154
  • 183
0

I'm using the default ASP.NET MVC 5 template with Identity Authentication for simplicity, but hopefully this can be modified for different use cases.

StartupAuth.cs

Do not customize the redirect path. It gets replaced by /signin-google anyways and my attempts at getting around that caused "silent" (not in the debugger) Internal Server 500 errors.

app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
{
    ClientId = "whatevs.apps.googleusercontent.com",
    ClientSecret = "whatevs_secrut",
    Provider = new GoogleOAuth2AuthenticationProvider()
});

Make sure to add http://whatever.com/signin-google to https://console.developers.google.com/ in your APIs & auth > Credentials > Redirect URIs section.

RouteConfig.cs

Add a route to a permanent redirect controller action to your routes. Permanent redirects are the only thing that will suffice here. It is not enough to simply direct directly to the Callback URL.

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
        name: "Google API Sign-in",
        url: "signin-google",
        defaults: new { controller = "Account", action = "ExternalLoginCallbackRedirect" }
    );

    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
}

AccountController.cs

Permanent redirect to the built-in callback method and you should be fine.

[AllowAnonymous]
public ActionResult ExternalLoginCallbackRedirect(string returnUrl)
{
    return RedirectPermanent("/Account/ExternalLoginCallback");
}

A template project has been posted on GitHub for reference: https://github.com/Pritchard/Test-AspNetGoogleOAuth2Authentication

  • 1
    This answer is incorrect. /signin-google is handled by OWIN, and whether you add this Route or not, it's ignored; OWIN handles the request before MVC Routing would touch it in the first place. OWIN doesn't need any help handling the /signin-google URL - it isn't the problem. – Chris Moschini Apr 28 '15 at 13:15
  • 1
    @ChrisMoschini Please don't say something is incorrect unless you've actually tried it. I have, and it works, which is why I not only posted a response to this question but uploaded the solution to GitHub. The point is that in ASP.NET MVC you still need a controller action to handle the authentication response, and providing a custom redirect path to the OWIN provider does not work in the example provided by Microsoft. I'm not "helping" OWIN find /signin-google. I'm helping the HTTP pipeline properly redirect itself to Account/ExternalLoginCallbackRedirect. –  Apr 28 '15 at 18:26
  • @ChrisMoschini To clarify, you could implement the controller action for `Home/signin-google` instead. The problem this answer solves is redirection to some other controller action, which cannot be done by providing a redirect URI to the OWIN provider. No matter what URI you give the provider, it is automatically overridden to `/signin-google` by the framework. I hope that clarifies the purpose of this answer, which I am going to by good faith assume the confusion was located. –  Apr 28 '15 at 18:34
  • I tried this method today to fix a problem and it didnt seem to work. What did work was adding a the machineKey to the web.config. After adding the machineKey everything worked. – John Boker Apr 29 '15 at 16:52