hawtio / hawtio-next

Next generation Hawtio UI console
https://hawt.io
Apache License 2.0
7 stars 22 forks source link

How to override <HawtioLogin/> in Hawtio BrowserRouter #594

Closed phantomjinx closed 1 year ago

phantomjinx commented 1 year ago

In Hawtio-Online react app, I have called the Hawtio component directly as the base app, eg.

  root.render(
    <React.StrictMode>
      <Hawtio/>
    </React.StrictMode>,
  )

This has been sufficient for an implementation without requirement to add other routes. However, now that I am adding the possibility of an oauth login page, hawtio-online requires its own <BrowserRouter/>.

This generates a router-cannot-be-inside-a-router error since <Hawtio/> already contains a <BrowserRouter/> switching <HawtioPage/> with its own login page (<HawtioLogin/>).

Now, hawtio-online does not need the original <HawtioLogin/> component since it is being overridden with the OAuthLoginPage (token login rather than user/password). So what to do?

  1. An easy fix would be to export ui/page/HawtioPage.tsx (<HawtioPage/>) and so bypass the <Hawtio/> component altogether. This would allow control of the <BrowserRouter /> wholly in hawtio-online. However, this seems like exporting internals / implementations of hawtio/react and not really fixing the issue.
  2. Because <Hawtio/> is a component to be inserted in other components, perhaps it would make sense to have it return a set of <Route/> components for inclusion in a parent <BrowserRouter/> (maybe even do this conditionally based on a given prop??). Although whether the existing <HawtioLogin /> can be overridden in this regard am not sure?
  3. Provide props to <Hawtio/> that includes extra routes or overrides existing routes. That way <Hawtio/> remains the single <BrowserRouter/> but 'parent' components can add custom links/pages/routes to it. This seems the preferred solution but unsure if there are unforseen problems.
tadayosi commented 1 year ago

Generally, this is not a right direction. You should not touch anything on the default hawtio/react routes.

Firstly, what OAuth are we talking about? If it's OpenShift OAuth, then we don't need a custom login page, as each OpenShift cluster must have provided its own login page and it is what hawtio-online uses for its authentication as well.

The main mechanics of hawtio/react authentication are that the userService resolves an authenticated user in some way and if it's resolved hawtio/react considers the user is indeed authenticated and thus allows routes to /*: https://github.com/hawtio/hawtio-next/blob/6bcaec8ff813500ba243e75e408564e14747c723/packages/hawtio/src/auth/user-service.ts#L50-L89 As you can see in the code, by default it resolves the user by fetching /user endpoint API from the server. The server is responsible for managing the authentication state and returning the authenticated user. The userService provides FetchUserHook, which is the extension point for an authentication plugin to provide the custom way to resolve the user. Registration of the hook should be done at index.ts of each authentication plugin: https://github.com/hawtio/hawtio-next/blob/6bcaec8ff813500ba243e75e408564e14747c723/packages/hawtio/src/plugins/auth/keycloak/index.ts#L7

Since hawtio/react is just an index.html with js scripts, in most cases when a request reaches the index.html it should be already authenticated and the user should be resolved. Unauthenticated requests should generally be intercepted before reaching index.html and redirected to the specific login page provided by an authentication provider such as Keycloak and OpenShift OAuth. The Hawtio login page and /login are used only for a server that doesn't have its own authentication mechanics, where the authentication filter redirects unauthenticated requests to /login.

So, all you need to do should be providing a custom hook to userService and please don't try to modify the flow of Hawtio authentication.

phantomjinx commented 1 year ago

This is not Openshift OAuth but Form OAuth which comes with its own custom login page that is served via online/login. See hawtio-oauth form directory here.

The 1.0 version includes its own login page since this page only has token on it and not user/password.

I am not changing the flow but migrating an existing flow.

tadayosi commented 1 year ago

OK, then instead of customising browser router, what about extending HawtioLogin component so that an auth plugin can optionally provide its own login page component to replace the default one?

phantomjinx commented 1 year ago

Could do. Although, <HawtioLogin/> is not exported either. Nothing from ui package is.

phantomjinx commented 1 year ago

To avoid exporting, would mean providing some kind of JSX Element to 'replace' <HawtioLogin/> so that would be a modified suggestion 3 above, I think.

tadayosi commented 1 year ago

No, you can look at HawtioPage component for how a non-exported Hawtio component can refer and use plugins in an inversion-of-control way.

phantomjinx commented 1 year ago

@tadayosi,

So I think I have a solution but please review:

On the hawtio/react side:

So an example of how this works is available in the OAuth Test App. Have not coded higher up the Hawtio-Online stack till we are happy with this approach:

(Please note: since hawtio/react does not export HawtioLogin and HawtioLoadingPage, I oopy them for the purposes of the test application)

OAuth-App:

OAuth:

Hopefully, that makes sense but feel free to ask questions if you cannot make head or tail of it.

tadayosi commented 1 year ago

Thanks. It looks great in general. There are a few comments that I'd like to see addressed. If you could create a pull req, then it'd be easier to review it in more details.

phantomjinx commented 1 year ago

Draft PRs for this created, for review.

Changes tested using both Openshift and Minicube, specifically: