From dce25e12d55ce1dacc01113b28f1488d7f6a557c Mon Sep 17 00:00:00 2001 From: Caleb Doxsey Date: Tue, 19 Sep 2023 17:40:51 -0600 Subject: [PATCH] add signed out page --- authenticate/handlers.go | 25 ++++++++++++++++--------- internal/handlers/signedout.go | 23 +++++++++++++++++++++++ ui/src/App.tsx | 16 ++++++++++------ ui/src/components/Header.tsx | 10 +++++++--- ui/src/components/SignedOutPage.tsx | 16 ++++++++++++++++ ui/src/types/index.ts | 5 +++++ 6 files changed, 77 insertions(+), 18 deletions(-) create mode 100644 internal/handlers/signedout.go create mode 100644 ui/src/components/SignedOutPage.tsx diff --git a/authenticate/handlers.go b/authenticate/handlers.go index 8f31ee278..c854f042f 100644 --- a/authenticate/handlers.go +++ b/authenticate/handlers.go @@ -89,6 +89,7 @@ func (a *Authenticate) mountDashboard(r *mux.Router) { // routes that don't need a session: sr.Path("/sign_out").Handler(httputil.HandlerFunc(a.SignOut)) + sr.Path("/signed_out").Handler(handlers.SignedOut(handlers.SignedOutData{})).Methods(http.MethodGet) // routes that need a session: sr = sr.NewRoute().Subrouter() @@ -266,16 +267,25 @@ func (a *Authenticate) signOutRedirect(w http.ResponseWriter, r *http.Request) e rawIDToken := a.revokeSession(ctx, w, r) - signOutURL := "" + authenticateURL, err := options.GetAuthenticateURL() + if err != nil { + return fmt.Errorf("error getting authenticate url: %w", err) + } + signOutRedirectURL, err := options.GetSignOutRedirectURL() if err != nil { return err } - if signOutRedirectURL != nil { - signOutURL = signOutRedirectURL.String() - } + + var signOutURL string if uri := r.FormValue(urlutil.QueryRedirectURI); uri != "" { signOutURL = uri + } else if signOutRedirectURL != nil { + signOutURL = signOutRedirectURL.String() + } else { + signOutURL = authenticateURL.ResolveReference(&url.URL{ + Path: "/.pomerium/signed_out", + }).String() } if idpSignOutURL, err := authenticator.GetSignOutURL(rawIDToken, signOutURL); err == nil { @@ -284,11 +294,8 @@ func (a *Authenticate) signOutRedirect(w http.ResponseWriter, r *http.Request) e log.Warn(r.Context()).Err(err).Msg("authenticate: failed to get sign out url for authenticator") } - if signOutURL != "" { - httputil.Redirect(w, r, signOutURL, http.StatusFound) - return nil - } - return httputil.NewError(http.StatusOK, errors.New("user logged out")) + httputil.Redirect(w, r, signOutURL, http.StatusFound) + return nil } // reauthenticateOrFail starts the authenticate process by redirecting the diff --git a/internal/handlers/signedout.go b/internal/handlers/signedout.go new file mode 100644 index 000000000..099a333db --- /dev/null +++ b/internal/handlers/signedout.go @@ -0,0 +1,23 @@ +package handlers + +import ( + "net/http" + + "github.com/pomerium/pomerium/internal/httputil" + "github.com/pomerium/pomerium/ui" +) + +// SignedOutData is the data for the SignedOut page. +type SignedOutData struct{} + +// ToJSON converts the data into a JSON map. +func (data SignedOutData) ToJSON() map[string]interface{} { + return map[string]interface{}{} +} + +// SignedOut returns a handler that renders the signed out page. +func SignedOut(data SignedOutData) http.Handler { + return httputil.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error { + return ui.ServePage(w, r, "SignedOut", data.ToJSON()) + }) +} diff --git a/ui/src/App.tsx b/ui/src/App.tsx index 56851e5b2..48d645910 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -1,12 +1,13 @@ import Box from "@mui/material/Box"; import CssBaseline from "@mui/material/CssBaseline"; import { ThemeProvider } from "@mui/material/styles"; -import React, {FC, useLayoutEffect} from "react"; +import React, { FC, useLayoutEffect } from "react"; import ErrorPage from "./components/ErrorPage"; import Footer from "./components/Footer"; import Header from "./components/Header"; import SignOutConfirmPage from "./components/SignOutConfirmPage"; +import SignedOutPage from "./components/SignedOutPage"; import { ToolbarOffset } from "./components/ToolbarOffset"; import UserInfoPage from "./components/UserInfoPage"; import WebAuthnRegistrationPage from "./components/WebAuthnRegistrationPage"; @@ -27,6 +28,9 @@ const App: FC = () => { case "SignOutConfirm": body = ; break; + case "SignedOut": + body = ; + break; case "DeviceEnrolled": case "UserInfo": body = ; @@ -38,18 +42,18 @@ const App: FC = () => { useLayoutEffect(() => { const favicon = document.getElementById( - 'favicon' + "favicon" ) as HTMLAnchorElement | null; if (favicon) { - favicon.href = data?.faviconUrl || '/.pomerium/favicon.ico'; + favicon.href = data?.faviconUrl || "/.pomerium/favicon.ico"; } const extraFaviconLinks = document.getElementsByClassName( - 'pomerium_favicon' + "pomerium_favicon" ) as HTMLCollectionOf | null; for (const link of extraFaviconLinks) { - link.style.display = data?.faviconUrl ? 'none' : ''; + link.style.display = data?.faviconUrl ? "none" : ""; } - }, []) + }, []); return ( diff --git a/ui/src/components/Header.tsx b/ui/src/components/Header.tsx index dfa11e3d9..e562fb85a 100644 --- a/ui/src/components/Header.tsx +++ b/ui/src/components/Header.tsx @@ -57,6 +57,8 @@ const Header: FC = ({ includeSidebar, data }) => { get(data, "user.claims.picture") || get(data, "profile.claims.picture") || null; + const showAvatar = + data?.page !== "SignOutConfirm" && data?.page !== "SignedOut"; const handleDrawerOpen = () => { setDrawerOpen(true); @@ -122,9 +124,11 @@ const Header: FC = ({ includeSidebar, data }) => { )} - - - + {showAvatar && ( + + + + )} = ({ data }) => { + return ( + + User has been Logged Out + + ); +}; +export default SignedOutPage; diff --git a/ui/src/types/index.ts b/ui/src/types/index.ts index dbf62c207..d8158402f 100644 --- a/ui/src/types/index.ts +++ b/ui/src/types/index.ts @@ -132,6 +132,10 @@ export type SignOutConfirmPageData = BasePageData & { url: string; }; +export type SignedOutPageData = BasePageData & { + page: "SignedOut"; +}; + export type UserInfoPageData = BasePageData & UserInfoData & { page: "UserInfo"; @@ -150,6 +154,7 @@ export type PageData = | ErrorPageData | DeviceEnrolledPageData | SignOutConfirmPageData + | SignedOutPageData | UserInfoPageData | WebAuthnRegistrationPageData;