Search code examples
asp.net-mvcmongodbasp.net-identityasp.net-core-mvcasp.net-identity-3

Asp.Net MVC 6 Identity 3 MongoDB External Login (Facebook)


I'm running the RC1 of ASP.NET MVC 6 and would like to use a MongoDB Identity Provider.

I have implemented the provider by Grant Megrabyan which is doing a great job of registering new users and allowing them to log in but I get the error:

InvalidOperationException: No authentication handler is configured to handle the scheme: Microsoft.AspNet.Identity.External Microsoft.AspNet.Http.Authentication.Internal.DefaultAuthenticationManager.d__13.MoveNext()

I had the external login previously working using EntityFramework so I'm assuming my configuration for third party auth is probably correct.

When the user clicks login with Facebook they are redirected to the following action :

    // POST: /Account/ExternalLogin
    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public IActionResult ExternalLogin(string provider, string returnUrl = null)
    {
        // Request a redirect to the external login provider.
        var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl });
        var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
        return new ChallengeResult(provider, properties);
    }

There is no exception thrown at this point however when the Facebook returns from the ChallengeResponse it sends a GET to : http://localhost:51265/signin-facebook?code=[facebook user token].

At this point ASP.NET throws the exception:

enter image description here

The callback url made by Facebook doesn't seem to make sense. Surely it should return to my ExternalLoginCallback action?

It's about here that I'm out of ideas?!

If anyone can see where I've gone wrong then I'd be a very happy guy.

My startup.cs:

public class Startup
{
    public Startup(IHostingEnvironment env)
    {
        // Set up configuration sources.
        var builder = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json")
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);

        if (env.IsDevelopment())
        {
            // For more details on using the user secret store see http://go.microsoft.com/fwlink/?LinkID=532709
            builder.AddUserSecrets();
        }

        builder.AddEnvironmentVariables();
        Configuration = builder.Build();
    }

    public IConfigurationRoot Configuration { get; set; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<ApplicationDbContext>();

        // Add framework services.
        services.AddIdentity<ApplicationUser, IdentityRole>()
            .AddMongoStores<ApplicationDbContext, ApplicationUser, IdentityRole>()
            .AddDefaultTokenProviders();

        services.AddMvc();

        // Add application services.
        services.AddTransient<IEmailSender, AuthMessageSender>();
        services.AddTransient<ISmsSender, AuthMessageSender>();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole(Configuration.GetSection("Logging"));
        loggerFactory.AddDebug();

        if (env.IsDevelopment())
        {
            app.UseBrowserLink();
            app.UseDeveloperExceptionPage();
            app.UseDatabaseErrorPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }

        app.UseIISPlatformHandler(options => options.AuthenticationDescriptions.Clear());

        app.UseStaticFiles();

        app.UseFacebookAuthentication(options =>
        {
            options.AppId = "removed";
            options.AppSecret = "removed";
        });

        app.UseIdentity();

        app.UseMvcWithDefaultRoute();
    }

    // Entry point for the application.
    public static void Main(string[] args) => WebApplication.Run<Startup>(args);
}

Solution

  • Call UseFacebookAuthentication after UseIdentity, but before UseMvc;

    app.UseIdentity();
    
    app.UseFacebookAuthentication(options =>
    {
        options.AppId = "removed";
        options.AppSecret = "removed";
    });
    
    app.UseMvcWithDefaultRoute();