From c0c5709194505ff1972d1c62285b446f39e8b14a Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Thu, 7 Oct 2021 19:23:00 -0400 Subject: [PATCH] Switch to server-side auth --- src/MyPrayerJournal/Server/Handlers.fs | 192 ++++++++---------- .../Server/MyPrayerJournal.Server.fsproj | 2 +- src/MyPrayerJournal/Server/Program.fs | 71 ++++++- src/MyPrayerJournal/Server/Views.fs | 39 ++-- .../Server/wwwroot/script/mpj.js | 74 ------- 5 files changed, 171 insertions(+), 207 deletions(-) diff --git a/src/MyPrayerJournal/Server/Handlers.fs b/src/MyPrayerJournal/Server/Handlers.fs index 5e831ce..e200b71 100644 --- a/src/MyPrayerJournal/Server/Handlers.fs +++ b/src/MyPrayerJournal/Server/Handlers.fs @@ -6,18 +6,44 @@ module MyPrayerJournal.Handlers open Giraffe open Giraffe.Htmx +open Microsoft.AspNetCore.Authentication +open Microsoft.AspNetCore.Http open System +open System.Security.Claims + +/// Helper function to be able to split out log on +[] +module private LogOnHelpers = + + /// Log on, optionally specifying a redirected URL once authentication is complete + let logOn url : HttpHandler = + fun next ctx -> task { + match url with + | Some it -> + do! ctx.ChallengeAsync ("Auth0", AuthenticationProperties (RedirectUri = it)) + return! next ctx + | None -> return! challenge "Auth0" next ctx + } /// Handlers for error conditions module Error = open Microsoft.Extensions.Logging + open System.Threading.Tasks /// Handle errors let error (ex : Exception) (log : ILogger) = log.LogError (EventId(), ex, "An unhandled exception has occurred while executing the request.") clearResponse >=> setStatusCode 500 >=> json ex.Message + /// Handle unauthorized actions, redirecting to log on for GETs, otherwise returning a 401 Not Authorized reponse + let notAuthorized : HttpHandler = + fun next ctx -> + (next, ctx) + ||> match ctx.Request.Method with + | "GET" -> logOn None + | _ -> setStatusCode 401 >=> fun _ _ -> Task.FromResult None + /// Handle 404s from the API, sending known URL paths to the Vue app so that they can be handled there let notFound : HttpHandler = setStatusCode 404 >=> text "Not found" @@ -28,36 +54,14 @@ module Error = module private Helpers = open LiteDB - open Microsoft.AspNetCore.Authentication - open Microsoft.AspNetCore.Http - open Microsoft.AspNetCore.Http.Features.Authentication open Microsoft.Extensions.Logging open Microsoft.Net.Http.Headers - open System.Security.Claims - open System.Threading.Tasks let debug (ctx : HttpContext) message = let fac = ctx.GetService() let log = fac.CreateLogger "Debug" log.LogInformation message - /// This type is internal in ASP.NET Core. :( - type AuthFeatures (result : AuthenticateResult) = - let mutable _user : ClaimsPrincipal = match result with null -> null | r -> r.Principal - let mutable _result : AuthenticateResult = result - interface IAuthenticateResultFeature with - member __.AuthenticateResult - with get () = _result - and set v = - _result <- v - _user <- match _result with null -> null | rslt -> rslt.Principal - interface IHttpAuthenticationFeature with - member __.User - with get () = _user - and set v = - _user <- v - _result <- null - /// Get the LiteDB database let db (ctx : HttpContext) = ctx.GetService() @@ -67,11 +71,12 @@ module private Helpers = |> Option.ofObj |> Option.map (fun user -> user.Claims |> Seq.tryFind (fun u -> u.Type = ClaimTypes.NameIdentifier)) |> Option.flatten + |> Option.map (fun claim -> claim.Value) /// Get the current user's ID - // NOTE: this may raise if you don't run the request through the authorize handler first + // NOTE: this may raise if you don't run the request through the requiresAuthentication handler first let userId ctx = - ((user >> Option.get) ctx).Value |> UserId + (user >> Option.get) ctx |> UserId /// Return a 201 CREATED response let created = @@ -87,54 +92,6 @@ module private Helpers = let jsNow () = DateTime.UtcNow.Subtract(DateTime (1970, 1, 1, 0, 0, 0)).TotalSeconds |> (int64 >> ( * ) 1_000L >> Ticks) - /// Handler to return a 401 Not Authorized reponse - let notAuthorized : HttpHandler = - setStatusCode 401 >=> fun _ _ -> Task.FromResult None - - /// Handler to require authorization - // NOTE: This is cribbed from ASP.NET Core's `AuthenticationMiddleware#Invoke` - // https://github.com/dotnet/aspnetcore/blob/main/src/Security/Authentication/Core/src/AuthenticationMiddleware.cs - let authorize : HttpHandler = - fun next ctx -> task { - let schemes = ctx.GetService () - ctx.Features.Set - (AuthenticationFeature (OriginalPath = ctx.Request.Path, OriginalPathBase = ctx.Request.PathBase)) - - // Give any IAuthenticationRequestHandler schemes a chance to handle the request - let handlers = ctx.GetService () - let! schms = schemes.GetRequestHandlerSchemesAsync () - let mutable handled = false - - for schm in schms do - match handled with - | true -> () - | false -> - match! handlers.GetHandlerAsync (ctx, schm.Name) with - | null -> () - | :? IAuthenticationRequestHandler as handler -> - match! handler.HandleRequestAsync () with true -> handled <- true | _ -> () - | _ -> () - - match handled with - | true -> return None - | false -> - match! schemes.GetDefaultAuthenticateSchemeAsync () with - | null -> () - | auth -> - match! ctx.AuthenticateAsync auth.Name with - | null -> () - | result -> - match result.Principal with null -> () | _ -> ctx.User <- result.Principal - match result.Succeeded with - | true -> - let authFeatures = AuthFeatures result - ctx.Features.Set authFeatures - ctx.Features.Set authFeatures - | false -> () - - return! match user ctx with Some _ -> next ctx | None -> notAuthorized next ctx - } - /// Render a component result let renderComponent nodes : HttpHandler = fun next ctx -> task { @@ -212,7 +169,7 @@ module Components = // GET /components/journal-items let journalItems : HttpHandler = - authorize + requiresAuthentication Error.notAuthorized >=> fun next ctx -> task { let shouldShow now r = now > Ticks.toLong r.snoozedUntil && now > Ticks.toLong r.showAfter let! jrnl = Data.journalByUserId (userId ctx) (db ctx) @@ -222,7 +179,7 @@ module Components = // GET /components/request/[req-id]/edit let requestEdit requestId : HttpHandler = - authorize + requiresAuthentication Error.notAuthorized >=> fun next ctx -> task { match requestId with | "new" -> @@ -230,22 +187,17 @@ module Components = (Views.Request.edit (JournalRequest.ofRequestLite Request.empty) false) next ctx | _ -> match! Data.tryJournalById (RequestId.ofString requestId) (userId ctx) (db ctx) with - | Some req -> - return! partialIfNotRefresh "Edit Prayer Request" (Views.Request.edit req false) next ctx + | Some req -> return! partialIfNotRefresh "Edit Prayer Request" (Views.Request.edit req false) next ctx | None -> return! Error.notFound next ctx } // GET /components/request-item/[req-id] let requestItem reqId : HttpHandler = - authorize + requiresAuthentication Error.notAuthorized >=> fun next ctx -> task { match! Data.tryJournalById (RequestId.ofString reqId) (userId ctx) (db ctx) with - | Some req -> - debug ctx "Found the item" - return! renderComponent [ Views.Request.reqListItem req ] next ctx - | None -> - debug ctx "Did not find the item" - return! Error.notFound next ctx + | Some req -> return! renderComponent [ Views.Request.reqListItem req ] next ctx + | None -> return! Error.notFound next ctx } @@ -256,20 +208,20 @@ module Home = let home : HttpHandler = partialIfNotRefresh "Welcome!" Views.Home.home - // GET /user/log-on - let logOn : HttpHandler = - partialIfNotRefresh "Logging on..." Views.Home.logOn - /// /journal URL module Journal = // GET /journal let journal : HttpHandler = - authorize + requiresAuthentication Error.notAuthorized >=> fun next ctx -> task { - let usr = ctx.Request.Headers.["X-Given-Name"].[0] - return! partialIfNotRefresh "Your Prayer Journal" (Views.Journal.journal usr) next ctx + let usr = + ctx.User.Claims + |> Seq.tryFind (fun c -> c.Type = ClaimTypes.GivenName) + |> Option.map (fun c -> c.Value) + |> Option.defaultValue "Your" + return! partialIfNotRefresh (sprintf "%s Prayer Journal" usr) (Views.Journal.journal usr) next ctx } @@ -294,7 +246,7 @@ module Request = // PATCH /request/[req-id]/prayed let prayed requestId : HttpHandler = - authorize + requiresAuthentication Error.notAuthorized >=> fun next ctx -> task { let db = db ctx let usrId = userId ctx @@ -315,7 +267,7 @@ module Request = /// POST /api/request/[req-id]/note let addNote requestId : HttpHandler = - authorize + requiresAuthentication Error.notAuthorized >=> fun next ctx -> task { let db = db ctx let usrId = userId ctx @@ -329,17 +281,17 @@ module Request = | None -> return! Error.notFound next ctx } - /// GET /requests/active + // GET /requests/active let active : HttpHandler = - authorize + requiresAuthentication Error.notAuthorized >=> fun next ctx -> task { let! reqs = Data.journalByUserId (userId ctx) (db ctx) return! partialIfNotRefresh "Active Requests" (Views.Request.active reqs) next ctx } - /// GET /requests/snoozed + // GET /requests/snoozed let snoozed : HttpHandler = - authorize + requiresAuthentication Error.notAuthorized >=> fun next ctx -> task { let! reqs = Data.journalByUserId (userId ctx) (db ctx) let now = (jsNow >> Ticks.toLong) () @@ -347,9 +299,9 @@ module Request = return! partialIfNotRefresh "Active Requests" (Views.Request.snoozed snoozed) next ctx } - /// GET /requests/answered + // GET /requests/answered let answered : HttpHandler = - authorize + requiresAuthentication Error.notAuthorized >=> fun next ctx -> task { let! reqs = Data.answeredRequests (userId ctx) (db ctx) return! partialIfNotRefresh "Answered Requests" (Views.Request.answered reqs) next ctx @@ -357,7 +309,7 @@ module Request = /// GET /api/request/[req-id] let get requestId : HttpHandler = - authorize + requiresAuthentication Error.notAuthorized >=> fun next ctx -> task { match! Data.tryJournalById (RequestId.ofString requestId) (userId ctx) (db ctx) with | Some req -> return! json req next ctx @@ -366,7 +318,7 @@ module Request = // GET /request/[req-id]/full let getFull requestId : HttpHandler = - authorize + requiresAuthentication Error.notAuthorized >=> fun next ctx -> task { match! Data.tryFullRequestById (RequestId.ofString requestId) (userId ctx) (db ctx) with | Some req -> return! partialIfNotRefresh "Prayer Request" (Views.Request.full req) next ctx @@ -375,7 +327,7 @@ module Request = /// GET /api/request/[req-id]/notes let getNotes requestId : HttpHandler = - authorize + requiresAuthentication Error.notAuthorized >=> fun next ctx -> task { let! notes = Data.notesById (RequestId.ofString requestId) (userId ctx) (db ctx) return! json notes next ctx @@ -383,7 +335,7 @@ module Request = // PATCH /request/[req-id]/show let show requestId : HttpHandler = - authorize + requiresAuthentication Error.notAuthorized >=> fun next ctx -> task { let db = db ctx let usrId = userId ctx @@ -398,7 +350,7 @@ module Request = /// PATCH /api/request/[req-id]/snooze let snooze requestId : HttpHandler = - authorize + requiresAuthentication Error.notAuthorized >=> fun next ctx -> task { let db = db ctx let usrId = userId ctx @@ -414,7 +366,7 @@ module Request = // PATCH /request/[req-id]/cancel-snooze let cancelSnooze requestId : HttpHandler = - authorize + requiresAuthentication Error.notAuthorized >=> fun next ctx -> task { let db = db ctx let usrId = userId ctx @@ -434,7 +386,8 @@ module Request = // POST /request let add : HttpHandler = - fun next ctx -> task { + requiresAuthentication Error.notAuthorized + >=> fun next ctx -> task { let! form = ctx.BindModelAsync () let db = db ctx let usrId = userId ctx @@ -462,7 +415,8 @@ module Request = // PATCH /request let update : HttpHandler = - fun next ctx -> Ply.task { + requiresAuthentication Error.notAuthorized + >=> fun next ctx -> Ply.task { let! form = ctx.BindModelAsync () let db = db ctx let usrId = userId ctx @@ -489,6 +443,25 @@ module Request = } +/// Handlers for /user URLs +module User = + + open Microsoft.AspNetCore.Authentication.Cookies + + // GET /user/log-on + let logOn : HttpHandler = + logOn (Some "/journal") + + // GET /user/log-off + let logOff : HttpHandler = + requiresAuthentication Error.notAuthorized + >=> fun next ctx -> task { + do! ctx.SignOutAsync ("Auth0", AuthenticationProperties (RedirectUri = "/")) + do! ctx.SignOutAsync CookieAuthenticationDefaults.AuthenticationScheme + return! next ctx + } + + open Giraffe.EndpointRouting /// The routes for myPrayerJournal @@ -526,7 +499,12 @@ let routes = route "" Request.add ] ] - GET_HEAD [ route "/user/log-on" Home.logOn ] + subRoute "/user/" [ + GET_HEAD [ + route "log-off" User.logOff + route "log-on" User.logOn + ] + ] subRoute "/api/" [ GET [ subRoute "request" [ diff --git a/src/MyPrayerJournal/Server/MyPrayerJournal.Server.fsproj b/src/MyPrayerJournal/Server/MyPrayerJournal.Server.fsproj index 8d0e37f..5c8a096 100644 --- a/src/MyPrayerJournal/Server/MyPrayerJournal.Server.fsproj +++ b/src/MyPrayerJournal/Server/MyPrayerJournal.Server.fsproj @@ -16,7 +16,7 @@ - + diff --git a/src/MyPrayerJournal/Server/Program.fs b/src/MyPrayerJournal/Server/Program.fs index 396ab25..e99aeaf 100644 --- a/src/MyPrayerJournal/Server/Program.fs +++ b/src/MyPrayerJournal/Server/Program.fs @@ -56,27 +56,75 @@ module Configure = open Giraffe open LiteDB - open Microsoft.AspNetCore.Authentication.JwtBearer + open Microsoft.AspNetCore.Authentication.Cookies + open Microsoft.AspNetCore.Authentication.OpenIdConnect + open Microsoft.AspNetCore.Http open Microsoft.Extensions.DependencyInjection + open Microsoft.IdentityModel.Protocols.OpenIdConnect + open System open System.Text.Json open System.Text.Json.Serialization + 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 + | _, _ -> () + bldr.Services .AddRouting() .AddGiraffe() + .Configure( + fun (opts : CookiePolicyOptions) -> + opts.MinimumSameSitePolicy <- SameSiteMode.Unspecified + opts.OnAppendCookie <- fun ctx -> sameSite ctx.CookieOptions + opts.OnDeleteCookie <- fun ctx -> sameSite ctx.CookieOptions) .AddAuthentication( /// Use HTTP "Bearer" authentication with JWTs fun opts -> - opts.DefaultAuthenticateScheme <- JwtBearerDefaults.AuthenticationScheme - opts.DefaultChallengeScheme <- JwtBearerDefaults.AuthenticationScheme) - .AddJwtBearer( - /// Configure JWT options with Auth0 options from configuration + opts.DefaultAuthenticateScheme <- CookieAuthenticationDefaults.AuthenticationScheme + opts.DefaultSignInScheme <- CookieAuthenticationDefaults.AuthenticationScheme + opts.DefaultChallengeScheme <- CookieAuthenticationDefaults.AuthenticationScheme) + .AddCookie() + .AddOpenIdConnect("Auth0", + /// Configure OIDC with Auth0 options from configuration fun opts -> - let jwtCfg = bldr.Configuration.GetSection "Auth0" - opts.Authority <- sprintf "https://%s/" jwtCfg.["Domain"] - opts.Audience <- jwtCfg.["Audience"]) + let cfg = bldr.Configuration.GetSection "Auth0" + opts.Authority <- sprintf "https://%s/" 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 + sprintf "%s://%s%s%s" request.Scheme request.Host.Value request.PathBase.Value redirUri + | false -> redirUri + Uri.EscapeDataString finalRedirUri |> sprintf "&returnTo=%s" + sprintf "https://%s/v2/logout?client_id=%s%s" cfg.["Domain"] cfg.["Id"] returnTo + |> ctx.Response.Redirect + ctx.HandleResponse () + + Task.CompletedTask + ) |> ignore let jsonOptions = JsonSerializerOptions () jsonOptions.Converters.Add (JsonFSharpConverter ()) @@ -97,11 +145,14 @@ module Configure = | true -> app.UseDeveloperExceptionPage () | false -> app.UseGiraffeErrorHandler Handlers.Error.error |> ignore - app.UseAuthentication() - .UseStaticFiles() + app.UseStaticFiles() + .UseCookiePolicy() .UseRouting() + .UseAuthentication() + // .UseAuthorization() .UseEndpoints (fun e -> e.MapGiraffeEndpoints Handlers.routes + // TODO: fallback to 404 e.MapFallbackToFile "index.html" |> ignore) |> ignore app diff --git a/src/MyPrayerJournal/Server/Views.fs b/src/MyPrayerJournal/Server/Views.fs index 765461e..34fe3ff 100644 --- a/src/MyPrayerJournal/Server/Views.fs +++ b/src/MyPrayerJournal/Server/Views.fs @@ -9,7 +9,10 @@ open System module Helpers = /// Create a link that targets the `main` element and pushes a URL to history - let pageLink href attrs = a (attrs |> List.append [ _href href; _hxBoost; _hxTarget "main"; _hxPushUrl ]) + let pageLink href attrs = + attrs + |> List.append [ _href href; _hxBoost; _hxTarget "main"; _hxPushUrl ] + |> a /// Create a Material icon let icon name = span [ _class "material-icons" ] [ str name ] @@ -235,18 +238,18 @@ module Navigation = /// Generate the navigation items based on the current state let currentNav isAuthenticated hasSnoozed (url : Uri option) = seq { + let currUrl = match url with Some u -> (u.PathAndQuery.Split '?').[0] | None -> "" + let navLink (matchUrl : string) = + match currUrl.StartsWith matchUrl with true -> [ _class "is-active-route" ] | false -> [] + |> pageLink matchUrl match isAuthenticated with | true -> - let currUrl = match url with Some u -> (u.PathAndQuery.Split '?').[0] | None -> "" - let navLink (matchUrl : string) = - match currUrl.StartsWith matchUrl with true -> [ _class "is-active-route" ] | false -> [] - |> pageLink matchUrl li [ _class "nav-item" ] [ navLink "/journal" [ str "Journal" ] ] li [ _class "nav-item" ] [ navLink "/requests/active" [ str "Active" ] ] if hasSnoozed then li [ _class "nav-item" ] [ navLink "/requests/snoozed" [ str "Snoozed" ] ] li [ _class "nav-item" ] [ navLink "/requests/answered" [ str "Answered" ] ] - li [ _class "nav-item" ] [ a [ _href "/user/log-off"; _onclick "mpj.logOff(event)" ] [ str "Log Off" ] ] - | false -> li [ _class "nav-item"] [ a [ _href "/user/log-on"; _onclick "mpj.logOn(event)"] [ str "Log On" ] ] + li [ _class "nav-item" ] [ a [ _href "/user/log-off" ] [ str "Log Off" ] ] + | false -> li [ _class "nav-item"] [ a [ _href "/user/log-on" ] [ str "Log On" ] ] li [ _class "nav-item" ] [ a [ _href "https://docs.prayerjournal.me"; _target "_blank" ] [ str "Docs" ] ] } |> List.ofSeq @@ -290,7 +293,13 @@ module Journal = /// The journal loading page let journal user = article [ _class "container-fluid mt-3" ] [ - h2 [ _class "pb-3" ] [ str user; rawText "’s Prayer Journal" ] + h2 [ _class "pb-3" ] [ + str user + match user with + | "Your" -> () + | _ -> rawText "’s" + str " Prayer Journal" + ] p [ _hxGet "/components/journal-items" _hxSwap HxSwap.OuterHtml @@ -571,7 +580,8 @@ module Layout = /// The HTML `head` element let htmlHead pageTitle = - head [] [ + head [ _lang "en" ] [ + meta [ _name "viewport"; _content "width=device-width, initial-scale=1" ] title [] [ str pageTitle; rawText " « myPrayerJournal" ] link [ _href "https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" @@ -581,11 +591,6 @@ module Layout = ] link [ _href "https://fonts.googleapis.com/icon?family=Material+Icons"; _rel "stylesheet" ] link [ _href "/style/style.css"; _rel "stylesheet" ] - script [ - _src "https://unpkg.com/htmx.org@1.5.0" - _integrity "sha384-oGA+prIp5Vchu6we2YkI51UtVzN9Jpx2Z7PnR1I78PnZlN8LkrCT4lqqqmDkyrvI" - _crossorigin "anonymous" - ] [] ] /// Element used to display toasts @@ -612,13 +617,17 @@ module Layout = ] ] ] + script [ + _src "https://unpkg.com/htmx.org@1.5.0" + _integrity "sha384-oGA+prIp5Vchu6we2YkI51UtVzN9Jpx2Z7PnR1I78PnZlN8LkrCT4lqqqmDkyrvI" + _crossorigin "anonymous" + ] [] script [ _async _src "https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" _integrity "sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" _crossorigin "anonymous" ] [] - script [ _src "https://cdn.auth0.com/js/auth0-spa-js/1.13/auth0-spa-js.production.js" ] [] script [ _src "/script/mpj.js" ] [] ] diff --git a/src/MyPrayerJournal/Server/wwwroot/script/mpj.js b/src/MyPrayerJournal/Server/wwwroot/script/mpj.js index af865fe..51b1bd3 100644 --- a/src/MyPrayerJournal/Server/wwwroot/script/mpj.js +++ b/src/MyPrayerJournal/Server/wwwroot/script/mpj.js @@ -2,41 +2,6 @@ /** myPrayerJournal script */ const mpj = { - /** Auth0 configuration */ - auth: { - /** The Auth0 client */ - auth0: null, - /** Configure the Auth0 client */ - async configureClient () { - const response = await fetch("/auth-config.json") - const config = await response.json() - mpj.auth.auth0 = await createAuth0Client({ - domain: config.domain, - client_id: config.clientId, - audience: config.audience - }) - } - }, - /** Whether the user is currently authenticated */ - isAuthenticated: false, - /** Whether we should redirect to the journal the next time the menu items are refreshed */ - redirToJournal: false, - /** - * Process a log on request - * @param {Event} e The HTML event from the `onclick` event - */ - async logOn (e) { - e.preventDefault() - await mpj.auth.auth0.loginWithRedirect({ redirect_uri: `${window.location.origin}/user/log-on` }) - }, - /** - * Log the user off - * @param {Event} e The HTML event from the `onclick` event - */ - logOff (e) { - e.preventDefault() - mpj.auth.auth0.logout({ returnTo: window.location.origin }) - }, /** * Show a message via toast * @param {string} message The message to show @@ -89,37 +54,6 @@ const mpj = { }, } -window.onload = async () => { - /** If the user is authenticated, set the JWT on the `body` tag */ - const establishAuth = async () => { - mpj.isAuthenticated = await mpj.auth.auth0.isAuthenticated() - if (mpj.isAuthenticated) { - const token = await mpj.auth.auth0.getTokenSilently() - const user = await mpj.auth.auth0.getUser() - document.querySelector("body") - .setAttribute("hx-headers", `{ "Authorization": "Bearer ${token}", "X-Given-Name": "${user.given_name}" }`) - htmx.trigger(htmx.find(".navbar-nav"), "menu-refresh") - } - } - - // Set up Auth0 - await mpj.auth.configureClient() - await establishAuth() - if (mpj.isAuthenticated) return - - // Handle log on code, if present - const query = window.location.search - if (query.includes("code=") && query.includes("state=")) { - await mpj.auth.auth0.handleRedirectCallback() - await establishAuth() - if (window.location.pathname === "/user/log-on") { - mpj.redirToJournal = true - } else { - window.history.replaceState({}, document.title, "/") - } - } -} - htmx.on("htmx:afterOnLoad", function (evt) { const hdrs = evt.detail.xhr.getAllResponseHeaders() // Set the page title if a header was in the response @@ -133,11 +67,3 @@ htmx.on("htmx:afterOnLoad", function (evt) { mpj.showToast(evt.detail.xhr.getResponseHeader("x-toast")) } }) -htmx.on("htmx:afterSettle", function (evt) { - // Redirect to the journal (once menu items load after log on) - if (mpj.redirToJournal - && ([...evt.target.attributes].find(it => it.name === "hx-target")?.value ?? "") === ".navbar-nav") { - mpj.redirToJournal = false - document.querySelector(`a[href="/journal"]`).click() - } -})