Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Updating OWIN from 2.1 to 3.0.1 breaks external auth

I'm having a difficult time debugging an OWIN package upgrade in my open source project. The short description is that using external logins breaks in the new v3 version when I upgrade from v2.1, and in the debugging process I can't figure out what's different. Keep in mind that none of my code is changed, I'm only updating the OWIN components (the packages in Microsoft.Owin and the other child namespaces).

It starts with this form post:

<form action="/Forums/Authorization/ExternalLogin?ReturnUrl=http%3A%2F%2Flocalhost%3A1973%2FForums" method="post"><input name="__RequestVerificationToken" type="hidden" value="--verificationtoken--" />       <h2>External Logins</h2>
   <p>
    <button type="submit" id="Google" name="provider" value="Google" class="btn btn-primary">Google</button>
   </p>
</form>

It posts to this method: https://github.com/POPWorldMedia/POPForums/blob/v13.0.0/PopForums/Controllers/AuthorizationController.cs

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

The callback lands here:

public async Task<ActionResult> ExternalLoginCallback(string loginProvider, string returnUrl)
{
    var authentication = _owinContext.Authentication;
    var authResult = await _externalAuthentication.GetAuthenticationResult(authentication);
    if (authResult == null)
        return RedirectToAction("Login", "Account", new { error = Resources.ExpiredLogin });
...

That second line calls this: https://github.com/POPWorldMedia/POPForums/blob/v13.0.0/PopForums/ExternalLogin/ExternalAuthentication.cs

public async Task<ExternalAuthenticationResult> GetAuthenticationResult(IAuthenticationManager authenticationManager)
{
    var authResult = await authenticationManager.AuthenticateAsync(ExternalCookieName);
    if (authResult == null)
        return null;
...

AuthenticationManager can be any of the implementations of IAuthenticationManager, in the Google, Facebook, etc. packages. The problem is that they all fail and return a null object, so the app can't login the user.

To reproduce:

  • Clone the v13 branch: https://github.com/POPWorldMedia/POPForums.git
  • Run the app locally per instructions: https://github.com/POPWorldMedia/POPForums/wiki/Documentation
  • In the admin, choose the external logins page, and add credentials for one of the providers and check the box.
  • Logout, then use the button to login with the new provider.
  • Watch it fail, and see that the authResult the above ExternalAuthentication GetAuthenticationResult method is null.

I keep wondering if something changed in the OWIN configuration that I don't understand. For the record, that's here: https://github.com/POPWorldMedia/POPForums/blob/v13.0.0/PopForums/Configuration/PopForumsOwinStartup.cs

using System;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Owin;
using PopForums.ExternalLogin;
using PopForums.Services;
using PopForums.Web;

namespace PopForums.Configuration
{
    public class PopForumsOwinStartup
    {
        public void Configuration(IAppBuilder app)
        {
            var setupService = PopForumsActivation.ServiceLocator.GetInstance<ISetupService>();
            if (!setupService.IsDatabaseSetup())
                return;

            var settings = PopForumsActivation.ServiceLocator.GetInstance<ISettingsManager>().Current;

            app.SetDefaultSignInAsAuthenticationType(ExternalAuthentication.ExternalCookieName);

            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = ExternalAuthentication.ExternalCookieName,
                AuthenticationMode = AuthenticationMode.Passive,
                CookieName = CookieAuthenticationDefaults.CookiePrefix + ExternalAuthentication.ExternalCookieName,
                ExpireTimeSpan = TimeSpan.FromMinutes(60)
            });

            if (settings.UseTwitterLogin)
                app.UseTwitterAuthentication(
                   consumerKey: settings.TwitterConsumerKey,
                   consumerSecret: settings.TwitterConsumerSecret);

            if (settings.UseMicrosoftLogin)
                app.UseMicrosoftAccountAuthentication(
                    clientId: settings.MicrosoftClientID,
                    clientSecret: settings.MicrosoftClientSecret);

            if (settings.UseFacebookLogin)
                app.UseFacebookAuthentication(
                   appId: settings.FacebookAppID,
                   appSecret: settings.FacebookAppSecret);

            if (settings.UseGoogleLogin)
                app.UseGoogleAuthentication(settings.GoogleClientId, settings.GoogleClientSecret);
        }
    }
}

Any ideas?

like image 307
Jeff Putz Avatar asked Feb 24 '15 19:02

Jeff Putz


1 Answers

Not sure if this will help but if you look at the templates the use AuthenticationManager.ExternalLinkLoginInfoAsync() to retrieve the result on the OAuth callbacks. Can you check and see i

[AllowAnonymous]
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ExternalLinkLogin(string provider) //Google,Twitter etc.
{
    return new ChallengeResult(provider, Url.Action("ExternalLinkLoginCallback"), userId);
}

[AllowAnonymous]
[HttpGet]        
public async Task<ActionResult> ExternalLinkLoginCallback()
{
    // Handle external Login Callback
    var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey,userId);
    if (loginInfo == null)
    {
        IdentitySignout(); // to be safe we log out
        return RedirectToAction("Register", new {message = "Unable to authenticate with external login."});
    }

    ...

    IdentitySignIn(userId, userName, returnUrl);
}

Also it looks like your startup code is slightly different from the default templates.

You're using:

app.SetDefaultSignInAsAuthenticationType(ExternalAuthentication.ExternalCookieName);

where the default templates use:

app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

You can quickly compare what the templates use in this summary of one of my blog posts:

http://weblog.west-wind.com/posts/2015/Apr/29/Adding-minimal-OWIN-Identity-Authentication-to-an-Existing-ASPNET-MVC-Application#MinimalCodeSummary

Very frustrating to hear that this broke for you though - this stuff should be backwards compatible - it's not alright to break existing code using a core system component like this.

like image 91
Rick Strahl Avatar answered Nov 05 '22 09:11

Rick Strahl