diff --git a/.gitignore b/.gitignore index 37f26ff..3df3440 100644 --- a/.gitignore +++ b/.gitignore @@ -254,3 +254,5 @@ paket-files/ # Ionide VSCode extension .ionide + +src/environment.txt diff --git a/src/MyPrayerJournal/Program.fs b/src/MyPrayerJournal/Program.fs index 0181106..38e46e0 100644 --- a/src/MyPrayerJournal/Program.fs +++ b/src/MyPrayerJournal/Program.fs @@ -1,164 +1,111 @@ module MyPrayerJournal.Api +open Microsoft.AspNetCore.Http + +let sameSite (opts : CookieOptions) = + match opts.SameSite, opts.Secure with + | SameSiteMode.None, false -> opts.SameSite <- SameSiteMode.Unspecified + | _, _ -> () + +open Giraffe +open Giraffe.EndpointRouting +open Microsoft.AspNetCore.Authentication.Cookies +open Microsoft.AspNetCore.Authentication.OpenIdConnect open Microsoft.AspNetCore.Builder -open Microsoft.AspNetCore.Hosting -open System.IO - -/// Configuration functions for the application -module Configure = - - /// Configure the content root - let contentRoot root = - WebApplicationOptions (ContentRootPath = root) |> WebApplication.CreateBuilder - - - open Microsoft.Extensions.Configuration - - /// Configure the application configuration - let appConfiguration (bldr : WebApplicationBuilder) = - bldr.Configuration - .SetBasePath(bldr.Environment.ContentRootPath) - .AddJsonFile("appsettings.json", optional = false, reloadOnChange = true) - .AddJsonFile($"appsettings.{bldr.Environment.EnvironmentName}.json", optional = true, reloadOnChange = true) - .AddEnvironmentVariables "MPJ_" - |> ignore - bldr - - - open Microsoft.AspNetCore.Server.Kestrel.Core - - /// Configure Kestrel from appsettings.json - let kestrel (bldr : WebApplicationBuilder) = - let kestrelOpts (ctx : WebHostBuilderContext) (opts : KestrelServerOptions) = - (ctx.Configuration.GetSection >> opts.Configure >> ignore) "Kestrel" - bldr.WebHost.UseKestrel().ConfigureKestrel kestrelOpts |> ignore - bldr - - - /// Configure the web root directory - let webRoot pathSegments (bldr : WebApplicationBuilder) = - Array.concat [ [| bldr.Environment.ContentRootPath |]; pathSegments ] - |> (Path.Combine >> bldr.WebHost.UseWebRoot >> ignore) - bldr - - - open Microsoft.Extensions.Logging - open Microsoft.Extensions.Hosting - - /// Configure logging - let logging (bldr : WebApplicationBuilder) = - if bldr.Environment.IsDevelopment () then bldr.Logging.AddFilter (fun l -> l > LogLevel.Information) |> ignore - bldr.Logging.AddConsole().AddDebug() |> ignore - bldr - - - open Giraffe - open Microsoft.AspNetCore.Authentication.Cookies - open Microsoft.AspNetCore.Authentication.OpenIdConnect - open Microsoft.AspNetCore.Http - open Microsoft.Extensions.DependencyInjection - open Microsoft.IdentityModel.Protocols.OpenIdConnect - open MyPrayerJournal.Data - open NodaTime - open System - open System.Text.Json - open System.Threading.Tasks - - /// Configure dependency injection - let services (bldr : WebApplicationBuilder) = - let sameSite (opts : CookieOptions) = - match opts.SameSite, opts.Secure with - | SameSiteMode.None, false -> opts.SameSite <- SameSiteMode.Unspecified - | _, _ -> () - - let _ = bldr.Services.AddRouting () - let _ = bldr.Services.AddGiraffe () - let _ = bldr.Services.AddSingleton SystemClock.Instance - let _ = bldr.Services.AddSingleton DateTimeZoneProviders.Tzdb - - let _ = - bldr.Services.Configure(fun (opts : CookiePolicyOptions) -> - opts.MinimumSameSitePolicy <- SameSiteMode.Unspecified - opts.OnAppendCookie <- fun ctx -> sameSite ctx.CookieOptions - opts.OnDeleteCookie <- fun ctx -> sameSite ctx.CookieOptions) - let _ = - bldr.Services.AddAuthentication(fun opts -> - opts.DefaultAuthenticateScheme <- CookieAuthenticationDefaults.AuthenticationScheme - opts.DefaultSignInScheme <- CookieAuthenticationDefaults.AuthenticationScheme - opts.DefaultChallengeScheme <- CookieAuthenticationDefaults.AuthenticationScheme) - .AddCookie() - .AddOpenIdConnect("Auth0", fun opts -> - // Configure OIDC with Auth0 options from configuration - let cfg = bldr.Configuration.GetSection "Auth0" - opts.Authority <- $"""https://{cfg["Domain"]}/""" - opts.ClientId <- cfg["Id"] - opts.ClientSecret <- cfg["Secret"] - opts.ResponseType <- OpenIdConnectResponseType.Code - - opts.Scope.Clear () - opts.Scope.Add "openid" - opts.Scope.Add "profile" - - opts.CallbackPath <- PathString "/user/log-on/success" - opts.ClaimsIssuer <- "Auth0" - opts.SaveTokens <- true - - opts.Events <- OpenIdConnectEvents () - opts.Events.OnRedirectToIdentityProviderForSignOut <- fun ctx -> - let returnTo = - match ctx.Properties.RedirectUri with - | it when isNull it || it = "" -> "" - | redirUri -> - let finalRedirUri = - match redirUri.StartsWith "/" with - | true -> - // transform to absolute - let request = ctx.Request - $"{request.Scheme}://{request.Host.Value}{request.PathBase.Value}{redirUri}" - | false -> redirUri - Uri.EscapeDataString $"&returnTo={finalRedirUri}" - ctx.Response.Redirect $"""https://{cfg["Domain"]}/v2/logout?client_id={cfg["Id"]}{returnTo}""" - ctx.HandleResponse () - Task.CompletedTask - opts.Events.OnRedirectToIdentityProvider <- fun ctx -> - let bldr = UriBuilder ctx.ProtocolMessage.RedirectUri - bldr.Scheme <- cfg["Scheme"] - bldr.Port <- int cfg["Port"] - ctx.ProtocolMessage.RedirectUri <- string bldr - Task.CompletedTask) - - let _ = bldr.Services.AddSingleton Json.options - let _ = bldr.Services.AddSingleton (SystemTextJson.Serializer Json.options) - let _ = Connection.setUp bldr.Configuration |> Async.AwaitTask |> Async.RunSynchronously - - bldr.Build () - - - open Giraffe.EndpointRouting - - /// Configure the web application - let application (app : WebApplication) = - let _ = app.UseStaticFiles () - let _ = app.UseCookiePolicy () - let _ = app.UseRouting () - let _ = app.UseAuthentication () - let _ = app.UseGiraffeErrorHandler Handlers.Error.error - let _ = app.UseEndpoints (fun e -> e.MapGiraffeEndpoints Handlers.routes) - app - - /// Compose all the configurations into one - let webHost pathSegments = - contentRoot - >> appConfiguration - >> kestrel - >> webRoot pathSegments - >> logging - >> services - >> application - +open Microsoft.AspNetCore.HttpOverrides +open Microsoft.Extensions.Configuration +open Microsoft.Extensions.DependencyInjection +open Microsoft.Extensions.Hosting +open Microsoft.Extensions.Logging +open Microsoft.IdentityModel.Protocols.OpenIdConnect +open MyPrayerJournal.Data +open NodaTime +open System +open System.Text.Json +open System.Threading.Tasks [] -let main _ = - use host = Configure.webHost [| "wwwroot" |] (Directory.GetCurrentDirectory ()) - host.Run () +let main args = + //use host = Configure.webHost [| "wwwroot" |] (Directory.GetCurrentDirectory ()) + //host.Run () + let builder = WebApplication.CreateBuilder args + let _ = builder.Configuration.AddEnvironmentVariables "MPJ_" + let svc = builder.Services + let cfg = svc.BuildServiceProvider().GetRequiredService () + + let _ = svc.AddRouting () + let _ = svc.AddGiraffe () + let _ = svc.AddSingleton SystemClock.Instance + let _ = svc.AddSingleton DateTimeZoneProviders.Tzdb + let _ = svc.Configure(fun (opts : ForwardedHeadersOptions) -> + opts.ForwardedHeaders <- ForwardedHeaders.XForwardedFor ||| ForwardedHeaders.XForwardedProto) + + let _ = + svc.Configure(fun (opts : CookiePolicyOptions) -> + opts.MinimumSameSitePolicy <- SameSiteMode.Unspecified + opts.OnAppendCookie <- fun ctx -> sameSite ctx.CookieOptions + opts.OnDeleteCookie <- fun ctx -> sameSite ctx.CookieOptions) + let _ = + svc.AddAuthentication(fun opts -> + opts.DefaultAuthenticateScheme <- CookieAuthenticationDefaults.AuthenticationScheme + opts.DefaultSignInScheme <- CookieAuthenticationDefaults.AuthenticationScheme + opts.DefaultChallengeScheme <- CookieAuthenticationDefaults.AuthenticationScheme) + .AddCookie() + .AddOpenIdConnect("Auth0", fun opts -> + // Configure OIDC with Auth0 options from configuration + let auth0 = cfg.GetSection "Auth0" + opts.Authority <- $"""https://{auth0["Domain"]}/""" + opts.ClientId <- auth0["Id"] + opts.ClientSecret <- auth0["Secret"] + opts.ResponseType <- OpenIdConnectResponseType.Code + + opts.Scope.Clear () + opts.Scope.Add "openid" + opts.Scope.Add "profile" + + opts.CallbackPath <- PathString "/user/log-on/success" + opts.ClaimsIssuer <- "Auth0" + opts.SaveTokens <- true + + opts.Events <- OpenIdConnectEvents () + opts.Events.OnRedirectToIdentityProviderForSignOut <- fun ctx -> + let returnTo = + match ctx.Properties.RedirectUri with + | it when isNull it || it = "" -> "" + | redirUri -> + let finalRedirUri = + match redirUri.StartsWith "/" with + | true -> + // transform to absolute + let request = ctx.Request + $"{request.Scheme}://{request.Host.Value}{request.PathBase.Value}{redirUri}" + | false -> redirUri + Uri.EscapeDataString $"&returnTo={finalRedirUri}" + ctx.Response.Redirect $"""https://{auth0["Domain"]}/v2/logout?client_id={auth0["Id"]}{returnTo}""" + ctx.HandleResponse () + Task.CompletedTask + opts.Events.OnRedirectToIdentityProvider <- fun ctx -> + let uri = UriBuilder ctx.ProtocolMessage.RedirectUri + uri.Scheme <- auth0["Scheme"] + uri.Port <- int auth0["Port"] + ctx.ProtocolMessage.RedirectUri <- string uri + Task.CompletedTask) + + let _ = svc.AddSingleton Json.options + let _ = svc.AddSingleton (SystemTextJson.Serializer Json.options) + let _ = Connection.setUp cfg |> Async.AwaitTask |> Async.RunSynchronously + + if builder.Environment.IsDevelopment () then builder.Logging.AddFilter (fun l -> l > LogLevel.Information) |> ignore + let _ = builder.Logging.AddConsole().AddDebug() |> ignore + + use app = builder.Build () + let _ = app.UseStaticFiles () + let _ = app.UseCookiePolicy () + let _ = app.UseRouting () + let _ = app.UseAuthentication () + let _ = app.UseGiraffeErrorHandler Handlers.Error.error + let _ = app.UseEndpoints (fun e -> e.MapGiraffeEndpoints Handlers.routes) + + app.Run () + 0 diff --git a/src/MyPrayerJournal/appsettings.json b/src/MyPrayerJournal/appsettings.json index 554978c..2c63c08 100644 --- a/src/MyPrayerJournal/appsettings.json +++ b/src/MyPrayerJournal/appsettings.json @@ -1,12 +1,2 @@ { - "ConnectionStrings": { - "mpj": "host=localhost;username=mpj;password=devpassword;database=mpj" - }, - "Kestrel": { - "EndPoints": { - "Http": { - "Url": "http://localhost:3000" - } - } - } }