Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Router Component Authentication #1526

Open
wendivoid opened this issue Aug 27, 2020 · 10 comments
Open

Router Component Authentication #1526

wendivoid opened this issue Aug 27, 2020 · 10 comments
Labels
A-yew-router Area: The yew-router crate question

Comments

@wendivoid
Copy link

Question

I'm building a generic admin panel, I'm struggling implementing an authentication system that can work with the router component. In short it works by sending the browser a list of permissions then when a route is changed i can check this list and either allow the route change or redirect to an unauthorized error page.

Is there a way to do this with the Router component?

What I've tried (optional)

I've been able to do this by not using the router component and using RouteService::register_callback, i would just prefer to be able to use the included component.

Ideally i think it would be helpful to add a filter function similar to render & redirect in the router props that will map a route to another route Fn(SW) -> SW by default this could just pass the route on unchanged and would give a place to modify the route before the change.

@siku2 siku2 added the A-yew-router Area: The yew-router crate label Aug 27, 2020
@ericandre615
Copy link

ericandre615 commented Mar 9, 2021

Did you have any luck with this? I was wanting to setup something similar. Coming from React Router v4+ I wanted something more like what I do with that. I've been experimenting with it. I'm not sure if this is better than what you have using RouteService::register_callback (I haven't used that yet). What I did was make a simple Redirect component with a to prop and then another AuthRoute component. The AuthRoute component takes a component/view that is only rendered if a user is authenticated. The AuthRoute handles that logic of figuring all that out (this can be done however you are doing it now) and essentially it says if user_is_authenticated() render the component else render the <Redirect to="/login" /> which just redirects and doesn't actually render anything. Then I can use it in the router like other components.

<Router<AppRoute, ()>
  render = Router::render(|route: AppRoute| {
    match route {
      AppRoute::Info => html! { <AuthRoute redirect="/login"><Info /></AuthRoute> },
      AppRoute::Profile => html! { <AuthRoute redirect="/login"><Profile /></AuthRoute> },
      AppRoute::Login => html! { <Login /> },
      AppRoute::Index => html!{ <Index /> },
    }
   })
/>

I was wanting something that was very declarative and worked with the Router component in a way that all components do. The problem I found with the redirect prop on Router is that it only works if there is no match for the route (/Switch), but we did match, we just say it's not valid without the condition of being authenticated.

Here's a link to the Redirect component I made that does that. It seems, so far, to work perfectly well (for my needs). https://github.com/ericandre615/yewi/blob/feature/ex-ss/src/components/routes/redirect.rs

@wendivoid
Copy link
Author

What you have there is probably the best workaround right now IIRC. The way i was talking about was forgoing the router component all together and just passing a custom callback to the route service provider (witch the router component uses internally) to change route's based on the provided route, it really is to much work for what it's worth.

@siku2
Copy link
Member

siku2 commented May 18, 2021

@hamza1311 do you have anything to add to this? Otherwise I would suggest we move this over to a discussion (now that that's a thing) or create a new feature request that's more specific about the details.
I believe it would be nice to support this out of the box, but the current system already allows for this in a slightly more verbose way so I feel like this needs a more concrete idea of what it should look like.

@hamza1311
Copy link
Member

@siku2, I don't think we should add this. Every time I used this in another router (react and angular), it gave me nothing but trouble. The callback to check if the user is authenticated needs to be async. Consider this case: a user token is stored but it needs to be verified by the server before marking authenticated state as true in the UI. We can't do that unless the callback is async. It would be better if user handled it themselves in their component. We should, however, provide a component that redirects to given path and renders html! { }.

Here's an example of the pain it has caused me in the past: https://github.com/hamza1311/dear-diary-web/blob/99b070abe46288c1722414323d17cc3ef50c7dd2/src/components/Routes.tsx#L36-L61
It should be as simple as a promise that can be awaited to obtain the result. I doubt my workaround is foolproof but that doesn't matter in my case.

@Indomitable
Copy link

Hi I see this issue is open and I think that yew router needs 2 features: Guards and Data resolver. First guards route to be access given a function ( like authenticated user ) and the second is to fetch data when route navigation is requested but to render the component after the data is received.

@hamza1311
Copy link
Member

hamza1311 commented Feb 13, 2022

I'm afraid it isn't as simple as what you describe. As I said in my previous comment, this will need async support to be practically usable. I can't think of a way to allow that without making the entire router async, which is inefficient as Promises aren't that well supported (think of lack of ability to directly get the return message from spawn_local, yew's methods being async, async not being supported in traits, etc).

Maybe Suspense can be used for this.
(CCing @futursolo for their input)

@Indomitable
Copy link

Yes understand, maybe in future when async methods in traits are stable. One more use case for async authentication which uses JWT tokens: User navigates to a page but the access token has been expired so a call with refresh token should be made and when access token is renewed then should be able to continue to the page.
Currently I'm doing it with a wrapper component which checks if user is authenticated and authorized to view the children components.

@Indomitable
Copy link

Just checked the Suspense I didn't know about it because it comes in the next version, this will help me in the fetching the data before the page is rendered. Thanks for the tip.

@futursolo
Copy link
Member

First guards route to be access given a function ( like authenticated user ) and the second is to fetch data when route navigation is requested but to render the component after the data is received.

I believe the fetch-then-render pattern is mostly manually implemented and is usually router agnostic (data is dispatched alongside route when fetched).

Maybe Suspense can be used for this.

A <AuthRoute /> (guard) component should no longer be needed if <Suspense /> is used with a suspense-aware data client.

// A thread_local! should be used here, static is only for demonstration purpose.
static GO_TO_LOGIN: Lazy<HtmlResult> = Lazy::new(|| Ok(html!{ <Redirect<Route> to={Route::Login} /> }));

#[function_component]
fn MyAccount() -> HtmlResult {
    let user = match use_user()? {
        Some(m) => m,
        None => return GO_TO_LOGIN.clone(),
    };

    Ok(html! { <div>{"Hello, "}{user.name}</div> })
}

@aisq2008
Copy link

aisq2008 commented Jul 4, 2023

we can use the follow code to auto redirect from any page to any other page

                        use wasm_bindgen::UnwrapThrowExt;
                        use web_sys::window;

                        let document = window()
                        .expect_throw("window is undefined").location().assign("/test"); 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-yew-router Area: The yew-router crate question
Projects
None yet
Development

No branches or pull requests

7 participants