.netasp.net-coref#asp.net-authenticationf#-giraffe

Facebook login with Giraffe


I'm trying to convert my MVC app to Giraffe, but there is one final boss: Facebook login.

I've been able to get every part working except for the challenge:

        public IActionResult ExternalLogin(string returnUrl = null)
        {
            var properties = _signInManager.ConfigureExternalAuthenticationProperties("Facebook", $"/mycallbackurl?returnUrl={returnUrl}");
            return new ChallengeResult("Facebook",   properties);
        }

How do I do this in Giraffe?

When I simply return challenge "Facebook" the flow works fine, except when I come back to my callback endpoint

let! info = signInManager.GetExternalLoginInfoAsync() info is null.

The console even says

info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[10]
      AuthenticationScheme: Identity.External signed in

How can I get hold of this event to sign in my user using signInManager.SignInAsync ?

Startup.cs


 services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
                .AddCookie(o =>
                    {
                        o.Cookie.Expiration = TimeSpan.FromDays(16);
                        o.Cookie.Name = "_myt";
                        o.SlidingExpiration = true;
                    }

                )
                .AddFacebook(o =>
                {
                    o.AppId = Configuration["Authentication:Facebook:AppId"];
                    o.AppSecret = Configuration["Authentication:Facebook:AppSecret"];
                });

Solution

  • signInManager.GetExternalLoginInfoAsync() relies on the challenge being issued with the callback url included, which Giraffe's challenge doesn't do.

    A solution is to roll your own challenge :

    let externalLogin : HttpHandler =
        fun (next : HttpFunc) (ctx : HttpContext) ->
            task {
                let provider = "Facebook"
    
                let returnUrl =
                    (ctx.TryGetQueryStringValue "returnUrl"
                     |> Option.map (sprintf "?returnurl=%s")
                     |> Option.defaultValue "")
    
                let items = // This must be a mutable dictionary to work
                    System.Collections.Generic.Dictionary<string, string>()
                
                items.Add("LoginProvider", provider)
                    
                let props = AuthenticationProperties(items, RedirectUri = (sprintf "/myCallbackEndpointWhereILogTheUserIn%s" returnUrl))
                
                do! ctx.ChallengeAsync(provider, props)
                return! next ctx
            }