Q:

Adding a button for google signin using f#/fable/asp.net/react

I'm working with the SAFE stack (https://safe-stack.github.io/) and through the example dojo. It's great so far. 
I'd like to extend the example to include a button to login/auth via Google. So I looked at an example on the Google website (https://developers.google.com/identity/sign-in/web/build-button). And then I had a look how to do authentication using ASP.NET (https://docs.microsoft.com/en-us/aspnet/core/security/authentication/social/google-logins?view=aspnetcore-2.1&tabs=aspnetcore2x) As a result I ended up confused as to how to integrate this into a SAFE project. Can someone tell me what they would do? SHould I be trying to use ASP.NET Identity or should I be using the JWT approach? I don't even know if they are the same since I'm very new to web frameworks..... 
The other question I have is how would one inject raw Javascript into the client side of a SAFE project. The google example above shows raw JS/CSS/HTML code? Should I be injecting that as is or should I look in React for some button that does this and map that idea back through Fable?

A:

Setting up OAuth
The easiest way to use Google OAuth is to wait until the next release of Saturn, at which point Saturn will include the use_google_oauth feature that I just added. :-) See the source code if you're interested in how it works, though I'm afraid you can't implement this yourself with use_custom_oauth because you'll run into a type error (the underlying ASP.NET code has a GoogleOptions class, and use_custom_oauth wants an OAuthOptions class, and they aren't compatible).
To use it, add the following to your application CE: 
use_google_oauth googleClientId googleClientSecret "/oauth_callback_google" []

The last parameter should be a sequence of string * string pairs that represent keys and values: you could use a list of tuples, or a Map passed through Map.toSeq, or whatever. The keys of that sequence are keys in the JSON structure that Google returns for the "get more details about this person" API call, and the values are the claim types that those keys should be mapped to in ASP.NET's claims system. The default mapping that use_google_oauth already does is:

id → ClaimTypes.NameIdentifier
displayName → ClaimTypes.Name
emails[] (see note) → ClaimTypes.Email

Those three are automatically mapped by ASP.NET. I added a fourth mapping:

avatar.url → `"urn:google:avatar:url"

There's no standard ClaimTypes name for this one, so I picked an arbitrary URN. Caution: this feature hasn't been released yet, and it's possible (though unlikely) that this string might change between now and when the feature is released in the next version of Saturn.
With those four claim types mapped automatically, I found that I didn't need to specify any additional claims, so I left the final parameter to use_google_oauth as an empty list in my demo app. But if you want more (say you want to get the user's preferred language to use in your localization) then just add them to that list, e.g.:
use_google_oauth googleClientId googleClientSecret "/oauth_callback_google" ["language", "urn:google:language"]

And then once someone has logged in, look in the User.Claims seq for a claim of type "urn:google:language".
Note re: the emails[] list in the JSON: I haven't tested this with a Google account that has multiple emails, so I don't know how ASP.NET picks an email to put in the ClaimTypes.Email claim. It might just pick the first email in the list, or it might pick the one with a type of account; I just don't know. Some experimentation might be needed.
Also note that third-party OAuth, including GitHub and Google, has been split into a new Saturn.Extensions.Authorization package. It will be released on NuGet at the same time that Saturn's next version (probably 0.7.0) is released.
Making the button
Once you have the use_google_oauth call in your application, create something like the following:
let googleUserIdForRmunn = "106310971773596475579"
let matchUpUsers : HttpHandler = fun next ctx ->
    // A real implementation would match up user identities with something stored in a database, not hardcoded in Users.fs like this example
    let isRmunn =
        ctx.User.Claims |> Seq.exists (fun claim ->
            claim.Issuer = "Google" && claim.Type = ClaimTypes.NameIdentifier && claim.Value = googleUserIdForRmunn)
    if isRmunn then
        printfn "User rmunn is an admin of this demo app, adding admin role to user claims"
        ctx.User.AddIdentity(new ClaimsIdentity([Claim(ClaimTypes.Role, "Admin", ClaimValueTypes.String, "MyApplication")]))
    next ctx

let loggedIn = pipeline {
    requires_authentication (Giraffe.Auth.challenge "Google")
    plug matchUpUsers
}

let isAdmin = pipeline {
    plug loggedIn
    requires_role "Admin" (RequestErrors.forbidden (text "Must be admin"))
}

And now in your scope (NOTE: "scope" will probably be renamed to "router" in Saturn 0.7.0), do something like this:
let loggedInView = scope {
    pipe_through loggedIn

    get "/" (htmlView Index.layout)
    get "/index.html" (redirectTo false "/")
    get "/default.html" (redirectTo false "/")
    get "/admin" (isAdmin >=> htmlView AdminPage.layout)
}

And finally, let your main router have a URL that passes things to the loggedInView router:
let browserRouter = scope {
    not_found_handler (htmlView NotFound.layout) //Use the default 404 webpage
    pipe_through browser //Use the default browser pipeline

    forward "" defaultView //Use the default view
    forward "/members-only" loggedInView
}

Then your login button can just go to the /members-only route and you'll be fine.
Note that if you want multiple OAuth buttons (Google, GitHub, Facebook, etc) you'll probably need to tweak that a bit, but this answer is long enough already. When you get to the point of wanting multiple OAuth buttons, go ahead and ask another question.

