Amplify-js: How to unprotect certain pages/components when using withAuthenticator

Created on 13 Jan 2019  路  11Comments  路  Source: aws-amplify/amplify-js

When using withAuthenticator, how to add a list of unprotected components/pages ?

I am building a react app in which certain pages like - about us, contact, search are unprotected- no authenticated user is needed to access these. I went through the documentation here and googled too, but could not find how to go about this.

Right now I have added withAuthenticator with custom signin and all the pages/components are secured. I want to add some components/pages as unsecured - Please advise as how to ?

React pending-close-response-required question

Most helpful comment

@haverchuck myself and @dabit3 are working on some sample code here to make this more clear for customers. Let's leave this open until a resolution is confirmed.

@jkeys-ecg-nmsu does the solution that @dabit3 posted above help you at all? What else could we do here?

All 11 comments

Put those pages outside your component that's being wrapped in Authenticator. So something like this in your top-level component (let's call it PublicApp.jsx):

<About />
<Contact />
<Authenticator>
    <ProtectedApp />
</Authenticator>

Since you're using withAuthenticator, it would look more like:

<About />
<Contact />
<ProtectedApp />

Where ProtectedApp.jsx's default export looks like this:

export default withAuthenticator(ProtectedApp)

@bishonbopanna - Does @jkeys-ecg-nmsu response make sense to you?

Thanks for the response @jkeys-ecg-nmsu and @haverchuck

Yup it does. However does it makes sense to have feature request to have an exclude list to withAuthenticator ?

@bishonbopanna @haverchuck Personally no, because then you can't see the structure of what is protected and what is public just by looking at the component hierarchy. It seems more natural to me to compose your app in a way that Authenticator wraps the minimum subset of components that need protection, and the rest occupy the same (or higher) level of hierarchy in the component tree.

I think you should just design your hierarchy to reflect what is protected.

You'll probably want to explore react-router at some point as well.

@haverchuck your thoughts? Also, how would you integrate Authenticator with react-router routes? I briefly experimented with hash-based routing but found it to be more trouble than it was worth, when containers can easily manage the state of dependent components (such as whether to render said components) with prop drilling. True SPA seems simpler and more intuitive than adding routing, but @bishonbopanna may need that for his use case.

@jkeys-ecg-nmsu @bishonbopanna -

I agree with @bishonbopanna that an exclude list wouldn't be something I'd want to implement for the react authenticator component in this package.... however, it could be something that a developer can implement themselves if needed (i.e. by filtering out the excluded components and passing the rest into the HOC). I could imagine some use cases for this, but I think it makes sense to let developers implement that themselves.

@jkeys-ecg-nmsu - I believe this sample app uses Authenticator with react-router-dom.

I'm closing this issue for now, but if you think that a change to this package is still desirable feel free to submit a feature request.

If you have specific authenticated & unauthenticated routes it may make sense to just write your own authentication logic.

If you look at React Router, they provide a basic implementation of how to create a protected route:

function PrivateRoute({ component: Component, ...rest }) {
  return (
    <Route
      {...rest}
      render={props =>
        fakeAuth.isAuthenticated ? (
          <Component {...props} />
        ) : (
          <Redirect
            to={{
              pathname: "/login",
              state: { from: props.location }
            }}
          />
        )
      }
    />
  );
}

We actually have a React Authentication starter that has a basic auth flow build in with boilerplate for Authenticated & unauthenticated routes that uses this idea here.

You can see where we implemented this in this router file.

We're still exploring how to also possibly improve the withAuthenticator component to handle this but figured I'd also throw this out there for you or anyone else coming upon this post.

@haverchuck myself and @dabit3 are working on some sample code here to make this more clear for customers. Let's leave this open until a resolution is confirmed.

@jkeys-ecg-nmsu does the solution that @dabit3 posted above help you at all? What else could we do here?

@undefobj @haverchuck I would love a paint-by-numbers example of using Amplify's Authenticator w/ react-router-dom.

But otherwise, the sample that @dabit3 was very helpful. It's pretty close to what I had (plus a lot of nice validation logic written by someone who really understands the React programming model, lol, thanks @dabit3); the reason I stripped it from our CES demo is that, upon reflection, that routing fundamentally doesn't make sense for an AR interactive home healthcare assistant that should always be full-screened.* (Or, can you think of some benefit that routing brings to such a use case, besides perhaps increased modularity at price of verbosity?)

You can close this issue as far as I'm concerned.

*(To elaborate, what use is there for a "back" functionality in an interactive 24/7 AR experience? It would be more jarring than anything. Better to design the decision trees, etc. to accomodate the need to return to sets of previous application states.)

(Also @haverchuck did you mean you agree with @bishonbopanna here? Just clarifying.

Thankyou @jkeys-ecg-nmsu, @haverchuck, @undefobj and @dabit3 for response and support.

I took your advice and separated the secured and unsecured parts of the app for my use case. However based on @dabit3 's examples I did the below just to learn, copying below for reference for others - please review.

@dabit3 - I must express my sincere gratitude for all those wonderful articles and video tutorials on amplify and related topics. Thank you 馃憤


class AppWrapper extends React.Component 
{ 
  state = {
    authState: {
      isLoggedIn: false
    }
  };

  handleUserSignIn = (state) => {
    if(state === "signedIn")
    {
      this.setState({ authState: { isLoggedIn: true } });
    }
    else 
    {
      this.setState({ authState: { isLoggedIn: false } });
    }
  };


  protectedRouteFunc = (C, P, K, authProps) => {
    return <Route  path={P} 
              component={rProps =>
                {
                  if(authProps.isLoggedIn)
                  {
                    return <C {...rProps} />
                  }
                  else
                  {
                    return <Authenticator 
                                hide={[SignIn]}
                                onStateChange={authProps.onUserSignIn}
                                children={[<CustomLoginPage/>]}
                              />
                  }
                }
              }
              key={K}
          />
  }

  unprotectedRouteFunc = (C, P, K) => {
    return <Route  path={P} 
              component={rProps =>
                {
                  return <C {...rProps} />
                }
              }
              key={K}
          />
  }


   render() {
    const authProps = {
      isLoggedIn: this.state.authState.isLoggedIn,
      onUserSignIn: this.handleUserSignIn
    };

     return <Provider client={client}>
               <Rehydrated>
                 <Router history={hist}>
                   <Switch>
                    {
                      indexRoutes.map((prop, key) => {
                          let C = prop.component;
                          let P = prop.path;

                          if(P.search("/unprotectedpages") !== -1)
                          {
                            return this.unprotectedRouteFunc(C, P, key);
                          }
                          else
                          {
                            return this.protectedRouteFunc(C, P, key, authProps);
                          }
                      })
                    }
                   </Switch>
                 </Router>
               </Rehydrated>
            </Provider> 
   }  
}   

@bishonbopanna I am facing similar issue and trying to digest the example solution you provided above (thank you!). Is your full implementation available to view on github or elsewhere?

When you create an <AppWrapper /> component, how to do you pass the C, P, and authProps attributes?

How does this line work? if(P.search("/unprotectedpages") !== -1)

@kwhitejr not @bishonbopanna , but: It's iterating over an unseen list of objects called indexRoutes, each of which contains a defined React Component (C) and Path (P).

indexRoutes.map((prop, key) => { ... }

As for what the line you asked about is doing, it's nothing magical. search is a prototype method on JS strings: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/search

It's checking for a partial match, like /unprotectedpages/page1, and returning -1 if it doesn't find it. This condition is used to determine whether to render the path P for component C as a protected (nested in Authenticator component) or unprotected route.

Nice code, still not sure the benefits of react-router for my company's needs.

Was this page helpful?
0 / 5 - 0 ratings