3

I have created a simple ASP.NET MVC4 web site to test the new OWIN Authentication middleware, I decided to start with Google OAuth2, I have had struggle quite a bit with the configuration but I have managed to have Google to authorize the user, the problem I have right now is that OWIN is not authenticating the user.

I think I have the proper settings in the web config

<system.web>
     <authentication mode="None" />
</system.web>
<system.webServer>
     <modules>
        <remove name="FormsAuthenticationModule" />
     </modules>
</system.webServer>

Then I have in the Startup class a very simple configuration

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        ConfigureAuth(app);
    }

    public void ConfigureAuth(IAppBuilder app)
    {
        app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
        // Enable the External Sign In Cookie.
        app.SetDefaultSignInAsAuthenticationType(DefaultAuthenticationTypes.ExternalCookie);
        // Enable Google authentication.
        app.UseGoogleAuthentication(GetGoogleOptions());
    }

    private static GoogleOAuth2AuthenticationOptions GetGoogleOptions()
    {
        var reader = new KeyReader();
        var keys = reader.GetKey("google");
        var options = new GoogleOAuth2AuthenticationOptions()
        {
            ClientId = keys.Public,
            ClientSecret = keys.Private
        };
        return options;
    }
}

In the AccountController I have coded the actions the following way which is again very simple but it should work.

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

    [AllowAnonymous, HttpGet]
    public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
    {
        var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
        if (loginInfo == null || !loginInfo.ExternalIdentity.IsAuthenticated)
        {
            return RedirectToAction("Login");
        }

        var identity = new ClaimsIdentity(new[] {
            new Claim(ClaimTypes.Name, loginInfo.DefaultUserName),
            new Claim(ClaimTypes.Email, loginInfo.Email)
        }, DefaultAuthenticationTypes.ExternalCookie);

        AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);

        AuthenticationManager.SignIn(new AuthenticationProperties
                {
                    IsPersistent = false
                }, identity);

        return RedirectToLocal(returnUrl);
    }

The main problem I'm having is that the call to the method AuthenticationManager.SignIn doesn't appear to be doing anything, even though Google is granting access to the request, when the user is redirected to the home page in which I have the following code

@using Microsoft.AspNet.Identity
@{
    Layout = "~/Views/Shared/_Main.cshtml";
}
<h2>Welcome</h2>
@{
    if (Request.IsAuthenticated)
    {
        <p>Welcome @User.Identity.GetUserName()</p>
    }
    else
    {
        @Html.ActionLink("Login", "Login", "Account") 
    }
}

The value of Request.IsAuthenticated is always false, anybody has an idea as to what am I missing here? From what I read online this should be working.

I have cookies enabled in my browser and other Google OAuth samples that I have that rely on the UserManager class work but this simple implementation I have is not working

Oscar Marin
  • 136
  • 2
  • 8

2 Answers2

6

After countless hours of reading on the web for answers I decided to debug the OWIN source code to find a solution to this problem, while the debugging session I came accross this gem in the AuthenticationHandler class

if (BaseOptions.AuthenticationMode == AuthenticationMode.Active)
        {
            AuthenticationTicket ticket = await AuthenticateAsync();
            if (ticket != null && ticket.Identity != null)
            {
                Helper.AddUserIdentity(ticket.Identity);
            }
        }

In my original Startup class I was enabling the external sign in cookie with this method

app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

This method was using a default CookieAuthenticationOptions instance that had AuthenticationMode = AuthenticationMode.Passive and this was preventing the class from reading the information stored in the cookie, that way on every new request the OwinContext was not loading the authenticated identity and it resulted on Request.IsAuthenticated

After I realized this all I did was to change app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie); with this

app.UseCookieAuthentication(new CookieAuthenticationOptions()
            {
                AuthenticationMode = AuthenticationMode.Passive,
                AuthenticationType = DefaultAuthenticationTypes.ExternalCookie,
                ExpireTimeSpan = TimeSpan.FromMinutes(30)
            });

and everything worked beautifully

Oscar Marin
  • 136
  • 2
  • 8
  • this results in `[InvalidOperationException: A default value for SignInAsAuthenticationType was not found in IAppBuilder Properties. This can happen if your authentication middleware are added in the wrong order, or if one is missing.]` – PC. Oct 30 '20 at 08:11
6

I had the same problem but all I needed to do was add "Google + API" to my list of APIS in the google developer console. After that, everything worked.

Mitchell Skurnik
  • 1,419
  • 4
  • 25
  • 37