Search code examples
c#facebookasp.net-corexamarin.formswebauthenticator

WebAuthenticator.AuthenticateAsync Facebook Login not closing


I'm trying to get this up and running but I do not know where the issue is. My startup:

public void ConfigureServices(IServiceCollection services)
    {
services.AddIdentity<ApplicationUser, ApplicationRole>(options => options.SignIn.RequireConfirmedAccount = true)
            .AddEntityFrameworkStores<ApplicationDbContext>()
            .AddDefaultUI();
         //   .AddDefaultTokenProviders();
        services.AddControllersWithViews();

        //language
        services.Configure<RequestLocalizationOptions>(options =>
        {
            options.DefaultRequestCulture = new RequestCulture("fr");
            options.RequestCultureProviders = new List<IRequestCultureProvider>
            {
                new QueryStringRequestCultureProvider(),
                new CookieRequestCultureProvider()
            };
        });

        services.AddAuthentication(o =>
        {
            o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        })
        .AddCookie()
        .AddFacebook(facebookOptions =>
        {
            facebookOptions.AppId = "XXXXX";
            facebookOptions.AppSecret = "XXXXX";
            facebookOptions.SaveTokens = true;
        });
services.AddControllersWithViews();
        services.AddRazorPages();

        services.AddAuthorization(options =>
        {
            options.FallbackPolicy = new AuthorizationPolicyBuilder()
                .RequireAuthenticatedUser()
                .Build();
        });
    }

My LoginController

[HttpGet]
    [AllowAnonymous]
    public async Task GetFacebook()
    {
        try
        {
            string scheme = "Facebook";

            var auth = await Request.HttpContext.AuthenticateAsync(scheme);

            if (!auth.Succeeded
                || auth?.Principal == null
                || !auth.Principal.Identities.Any(id => id.IsAuthenticated)
                || string.IsNullOrEmpty(auth.Properties.GetTokenValue("access_token")))
            {
                COMMON_FUNCTIONS.storeError(_context, "Not authenticated", MethodBase.GetCurrentMethod().ReflectedType.Name, auth.ToString());

                // Not authenticated, challenge
                await Request.HttpContext.ChallengeAsync(scheme);
            }
            else
            {
                var claims = auth.Principal.Identities.FirstOrDefault()?.Claims;

                var email = string.Empty;
                email = claims?.FirstOrDefault(c => c.Type == System.Security.Claims.ClaimTypes.Email)?.Value;

                COMMON_FUNCTIONS.storeError(_context, "authenticated - mail", MethodBase.GetCurrentMethod().ReflectedType.Name, email);

                // Get parameters to send back to the callback
                var qs = new Dictionary<string, string>
            {
                { "access_token", auth.Properties.GetTokenValue("access_token") },
                { "refresh_token", auth.Properties.GetTokenValue("refresh_token") ?? string.Empty },
                { "expires", (auth.Properties.ExpiresUtc?.ToUnixTimeSeconds() ?? -1).ToString() },
                { "email", email }
            };


                // Build the result url
                var url = "APPSCHEME" + "://#" + string.Join(
                    "&",
                    qs.Where(kvp => !string.IsNullOrEmpty(kvp.Value) && kvp.Value != "-1")
                    .Select(kvp => $"{WebUtility.UrlEncode(kvp.Key)}={WebUtility.UrlEncode(kvp.Value)}"));

                COMMON_FUNCTIONS.storeError(_context, "authenticated - url", MethodBase.GetCurrentMethod().ReflectedType.Name, url);

                // Redirect to final url
                Request.HttpContext.Response.Redirect(url);
            }
        }
        catch (Exception ex)
        {
            COMMON_FUNCTIONS.storeError(_context, "Authenticate-ex", MethodBase.GetCurrentMethod().ReflectedType.Name, ex.ToString());
        }
    }

In my XAMARIN.FORMS:

private async void OnFacebookClicked(object sender, EventArgs e)
    {
        lblMessage.Text = "";
        try
        {
            var authResult = await  WebAuthenticator.AuthenticateAsync(new Uri(App.g_WebSite + "api/v1/Authenticate/GetFacebook"), new Uri("APPSCHEME://"));

            var accessToken = authResult?.AccessToken;
        }
        catch (Exception ex) when (ex is TaskCanceledException || ex is OperationCanceledException)
        {
            lblMessage.Text = "User cancelled!";
        }
        catch (Exception ex)
        {
            string tt = ex.ToString();
            lblMessage.Text = "Login failed!";
        }
    }

When I Click on the Loginbutton, my function GetFacebook() is called. In the server log I can see how the token is generated APPSCHEME://#access_token=xxxxxxxxxxxxxxxxxxxxxxxxx before the Redirect call

Request.HttpContext.Response.Redirect(url);

But the page facebook page is not closing, the code in the XAMARIN.FORMS newer reaches var accessToken = authResult?.AccessToken;

Update The code in my Android

[Activity(Label = "GSAQ_Mobile", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize )]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
    protected override void OnCreate(Bundle savedInstanceState)
    {
        TabLayoutResource = Resource.Layout.Tabbar;
        ToolbarResource = Resource.Layout.Toolbar;

        base.OnCreate(savedInstanceState);

        Xamarin.Essentials.Platform.Init(this, savedInstanceState);
        global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
        LoadApplication(new App());
    }

    public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
    {
        Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);

        base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
    }

    //protected override void OnResume()
    //{
    //    base.OnResume();

    //    Xamarin.Essentials.Platform.OnResume();
    //}
}

[Activity(NoHistory = true, LaunchMode = LaunchMode.SingleTop)]
[IntentFilter(new[] { Android.Content.Intent.ActionView }, Categories = new[] { Android.Content.Intent.CategoryDefault, Android.Content.Intent.CategoryBrowsable },
DataScheme = "APPSCHEME")]
public class WebAuthenticationCallbackActivity : Xamarin.Essentials.WebAuthenticatorCallbackActivity
{
}

On my website, the facebooklogin works fine.

Anyone an idea?


Solution

  • After lot's of testing I got it to work. I made several changes, but I do not know for certain which change did the trick:

    1. Added [ApiController] the LoginController class
    2. Added cookie handling: https://learn.microsoft.com/en-us/aspnet/core/security/gdpr?view=aspnetcore-5.0
    3. On my emulated Android, i figured there was an issue with the chrome version so I updated the chrome browser in the hosted Android to the latest version
    4. I changed the callback scheme from APPSCHEME to app

    In my opinion, point 4 did the trick, but the other points are sure a point of interest.