Why wont my Auth0 find my callback route in react? - javascript

Auth0 redirects to http://localhost:3000/callback#/acccess-token=dxy
I'm getting a blank screen in my react app.
Heres my main app.js
render((
<HashRouter>
<Main />
</HashRouter>
), $('#app')[0]);
My main contains my routes.js component.
class Routes extends Component {
constructor(props, context) {
super(props, context);
this.state = { mainData: this.props.mainData };
this.handleAuthentication = this.handleAuthentication.bind(this)
}
componentWillReceiveProps(newProps) {
this.setState((previousState) => update(previousState, {
mainData: { $set: newProps.mainData },
}));
}
handleAuthentication(nextState, replace) {
if (/access_token|id_token|error/.test(nextState.location.hash)) {
this.props.auth.handleAuthentication();
}
}
render() {
return (
<div>
<Switch>
<Route path='/callback' render={props => {
this.handleAuthentication(props);
return <Callback {...props} />
}} />
<Route exact path='/' render={props => (
<Dashboard changeAppBar={this.props.changeAppBar} userProfile={this.state.mainData.userProfile} windowHeight={this.props.wh} windowWidth={this.props.ww} />)}
/>
<Route path='/settings' render={props => (
<Settings changeAppBar={this.props.changeAppBar} userProfile={this.state.mainData.userProfile} />)}
/>
</Switch>
</div>
);
}
}
export default Routes;
Heres my init of auth0
this.auth0 = new auth0.WebAuth({
clientID: 'oiEjW4Mf6Ju4BvRfHeuObQnMbghKs38g',
domain: 'cryptok1ng.auth0.com',
responseType: 'token id_token',
redirectUri: 'http://localhost:3000/callback'
})
Everything works fine until I get redirected from auth0 back to /callback. Simply doesn't find a screen /route and renders nothing.
Screenshot of the console. /callback breakpoint is never hit.
Thanks for any help I've been going through the docs and answers to no avail.

I am assuming you In Auth0 front end client configuration> you have added the callback URi as http://localhost:3000/callback and saved it.
And also in your callback.html file you added some tags to show up something once the token is authenticated properly.
If everything is fine and you still get blank error. Please post your console screenshot to have a look.

Related

React Router V5 best way to use context variable in route

In My app I have my routes defined, as per below:
<BrowserRouter>
<Header />
<div className="App">
<Switch>
<Route exact path="/">
<Redirect to="/home" />
</Route>
<Route exact path={["/home", "/"]} component={Home} />
<Route path="/account/:id" render={(props: RouteComponentProps<any>) => <Account {...props} />} />
<Route component={NotFound} />
</Switch>
</div>
</BrowserRouter>
What I want to know is, this can be tricky, If I wanted my route to have a prefix from my context i.e variable how would I do this, but the twist is the variable comes from an api response?
so what if i wanted the route /contextVariable/home but contextVariable is from an api response and is stored in a context value, I know how I would bring that variable into the component but how would the routes handle it i.e from not being /undefined/home as in the response would need to finish before being inserted into the route?
Any idea's?
I had once made a project that had similar requirement. In that, instead of declaring dynamic routes, I fetched a routes array from the state which was an object array with component, path, and few other parameters. By default I added the initial landing page and not found page:
const [routes, setRoutes] = React.useState([
{
component: HomeComponent,
path: '/',
},
{
component: NoMatchPage,
path: '*',
}
])
And then I had the request in a useEffect block which would update this state like so:
React.useEffect(()=>{
// call api()
const oldRoutes = routes;
const noMatchPage = oldRoutes.pop();
const newRoutes = [...oldRoutes,
responseFromApi.map(
routeItem =>
({
component: ComponentName,
path: routeItem.path
})
), noMatchPage]
setRoutes(newRoutes)
},[])
Edit 1 : Cause I'm forgetful
Sorry, I forgot the main part, here's how the Route rendering would be:
<Switch>
{
routes.map(routeItem =>
<Route path={routeItem.path} component={routeItem.component} />
)
}
</Switch>
Also if you want to avoid the extra code in useEffect, you could simply do this:
React.useEffect(()=>{
// call api()
setRoutes(responseFromApi.map(
routeItem =>
({
component: ComponentName,
path: routeItem.path
})
))
},[])
and then
<Switch>
<Route exact path={["/home", "/"]} component={Home} />
{
routes.map(routeItem =>
<Route path={routeItem.path} component={routeItem.component} />
)
}
<Route component={NotFound} />
</Switch>
Edit 2 : Cause I'm ignorant
In the case where the user enters the URL directly and Switch is unable to identify the Route and therefore loads the NotFoundPage, you could do the following:
Set a condition when you start loading your paths, inside your useEffect block:
const [loading, setLoading] = React.useState(false);
React.useEffect(() =>
{
setLoading(true);
// load paths
setLoading(false);
}, [])
While the fetch is in progress, show a Loader to the user:
return
(
<>
{
loading ?
<LoaderComponent /> :
<Switch>
// same as before
</Switch>
}
</>
)
Preferable show something for the user to read, so they don't get irritated, cause patience is a thing of the past. Hope this helps!
If you want to do this with a React Context then this is the pattern I'd suggest. Create a React Context that holds the API logic to fetch a "base path" and expose that out to consumers. Consumers will take the provided "base path" value and prepend it to all link targets and route paths.
Example:
BasePathProvider
import { createContext, useContext } from "react";
const BasePath = createContext({
basepath: ""
});
const BasePathProvider = ({ children }) => {
... logic to fetch basepath ...
return (
<BasePath.Provider value={{ basepath }}>
{children}
</BasePath.Provider>
);
};
const useBasePath = () => useContext(BasePath);
Header
const Header = () => {
const { basepath } = useBasePath();
return (
...
<Link to={`${basepath}/`}>Home</Link>
<Link to={`${basepath}/account/${/* some id value */}`}>
Account
</Link>
...
);
};
App
function App() {
return (
<div className="App">
<Header />
<BasePath.Consumer>
{({ basepath }) => (
<Switch>
<Redirect from={`${basepath}/`} exact to={`${basepath}/home`} />
<Route path={`${basepath}/home`} component={Home} />
<Route path={`${basepath}/account/:id`} component={Account} />
<Route component={NotFound} />
</Switch>
)}
</BasePath.Consumer>
</div>
);
}
index.js
import { BrowserRouter as Router } from "react-router-dom";
import BasePathProvider from "../path/to/BasePathProvider";
...
<Router>
<BasePathProvider>
<App />
</BasePathProvider>
</Router>
Note: You might also want/need to implement a "loading" state to conditionally render the BasePathProvider component's children until the basepath value has been fetched.

Validating React route with axios async function

So I am creating a web app with server (node+express) and client (cra) sides.
I have an issue with validating user according to the jwt token I set as a cookie. The validation api endpoint on server side works as it should (tested it with postman) but the problem is the async check function that returns the promise therefore the route doesn't really know is it validated since the response is pending.
Here is the api endpoint on server side:
/api/token.js
router.get('/',
jwt({secret:'token-secret' }),
function (req,res) {
console.log(req);
if(!req.user) return res.sendStatus(401);
res.sendStatus(200);
}
)
and here is the app.js on the client side: src/app.js that handles routing ( /dashboard should be available only for validated users)
function App() {
function checkToken() {
let token = Cookies.get('Access Token')
axios.get('http://localhost:9000/api/token', {
headers: {
'Authorization': `bearer ${token}`
}
}).then(res => {
return res.status;
}).catch(err => console.log(err));
}
const handleToken = async () => {
const result = await checkToken();
return result;
}
return (
<BrowserRouter>
<Route exact={true} path='/' render={() => (
<div className="App">
<Home />
</div>
)}/>
<Route exact={true} path='/dashboard' render={() => (
<div className="App">
{console.log('checktoken log', handleToken())}
{checkToken() ? <Dashboard /> : <Login />}
</div>
)}/>
<Route exact={true} path='/login' render={() => (
<div className="App">
<Login />
</div>
)}/>
</BrowserRouter>
);
}
At this point I am aware that perhaps I shouldn't be doing a validation in this way since there is probably no way I can get the return before the render, perhaps it should be done in a lifecycle hook componentWillMount but I haven't been able to introduce it to this file (or everything should be done in an entirely different file).
Thanks
p.s. I've omitted all imports and export defaults since that is not relevant here
Well, I've made it with a couple of substantial changes. First, in order to use history.push I had to refactor BrowserRouter part so now it looks like this
app.js
render() {
return (
<Router history={history}>
<Route exact path='/' component={Home} />
<Route exact path='/dashboard' component={Dashboard} />
<Route exact path='/login' component={Login} />
</Router>
);
}
Then I've decided not to use api/token.js. Instead of this api endpoint I've created a Higher Order Component that will check for cookies that have been set during login. Part that gave me most trouble is asynchronous nature of fetching cookies. That was solved with setTimeout inside getCookie function, I've called this function in componentDidMount lifecycle.
src/components/withAuth.js
state = {
data: false,
open: false,
auth: false
}
componentDidMount() {
this.getCookie();
}
getCookie(){
this.setState({
open: true,
})
setTimeout(() => {
const cookie = Cookies.get('Access Token')
if(cookie) {
this.setState({
data: true,
open: false,
auth: true
})
} else if (cookie === undefined) {
this.setState({
auth: true,
open: false
})
}
}, 700)
}
In the end, in order to protect the route I've wrapped the component with HOC
src/Views/Dashboard.js
import requireAuthentication from '../components/withAuth';
class Dashboard extends Component {
render() {
return (
<div>
<DashboardContent />
</div>
);
}
}
export default requireAuthentication(Dashboard);

React Router Redirect after Login not working properly

I'm developing a basic react application and included react-router.
I have a simple authentication control with the Local Storage.
After a user inputs username and password and clicks login, I do an HTTP call and take response from the server with Axios. Then I set the localStorage 'user' item.
To protect a route I implemented the PrivateRoute component where I check if 'user' in localStorage is set.
I already tried to move set Local Storage inside then() in the Axios HTTP call but nothing changed.
Api CALL
loginUser (username,password) {
return HTTP.post('/login', null, { params: {
username,
password
}})
Api.loginUser(username,password)
.then( (response) => {
console.log("Response DATA");
Api.saveUserData(response.data);
this.setState({ redirect: true });
})
RENDER METHOD
if (this.state.redirect === true) {
return <Redirect to='/home'/>;
}
APP COMPONENT
class App extends Component {
render() {
return (
<Router>
<Route path="/login" component={Login} />
<PrivateRoute path="/home" component={Home} />
</Router>
);
}
}
PRIVATE ROUTE COMPONENT
const PrivateRoute = ({ component: Component, ...rest }) => {
const isLoggedIn = AuthService.isAuthenticated();
return (
<Route
{...rest}
render={props =>
isLoggedIn ? (
<Component {...props} />
) : (
<Redirect to={{ pathname: '/login', state: { from: props.location } }} />
)
}
/>
)
}
The problems seem to be: the local storage is set after the redirect because is null. So I get blank page instead of loading the Home Component. If i refresh the page, the code works fine.

Implementing PrivateRoute in React.js

I'm having some problems with implementing PrivateRoute in React. Here is my code:
class App extends Component {
constructor(props) {
super(props);
this.state = {
currentUser: null,
loadingUser: true
}
}
componentDidMount() {
this.onAuth();
};
onAuth = () => {
getCurrentUser().then((json) => {
console.log(json);
this.setState({
currentUser: json,
loadingUser: false
})
}).catch((error) => {
this.setState({
currentUser: null,
loadingUser: false,
})
})
};
logout = () => {
logout();
this.setState({
currentUser: null,
loadingUser: false
});
this.props.history.push("/");
toast.info("Succesfully logout.");
};
render() {
return (
<div className="body">
<ToastContainer closeOnClick={false}/>
<ApplicationHeader currentUser={this.state.currentUser} logout={this.logout}/>
<Grid>
<div className="app-content">
<Switch>
<Route exact path="/vote/:id" render={(props) => <Vote currentUser={this.state.currentUser} {...props}/>}/>
<Route exact path="/login" render={() => <Login onAuth={this.onAuth} />}/>
<PrivateRoute authed={this.state.currentUser != null} exact path="/vote" component={NewProcess} />
<PrivateRoute authed={this.state.currentUser != null} exact path="/items" component={NewItems} />
<Route component={NotFound}/>
</Switch>
</div>
</Grid>
<Footer/>
</div>
);
}
}
const PrivateRoute = ({component: Component, authed, ...rest}) => {
return (
<Route
{...rest}
render={(props) => authed === true
? <Component {...props} />
: <Redirect to={{pathname: '/login', state: {from: props.location}}} />} />
)
}
When user posts credentials (or App main component gets rendered) onAuth method gets invoked and sets (or not) currentUser property of App's state. This property is null (when user is not authenticated) and represents userdetails such like id and username (when user is authenticated). Then, in PrivateRoute based on that property component gets rendered or application redirects user back to the login page. And that doesn't work well. I mean when i'm already authenticated and try to access any of private route, i am redirected to proper component. Problem occurs in 2 situations:
right after logging in - application doesnt redirect me to component
i want to access, insted i stay on the login page.
refreshing page (in browser) corresponded to private route.
It seems like PrivateRoute component doesnt get refreshed when currentUser property gets changed, which is kinda weird because i'm using similar approach in ApplicationHeader to display username when user is authenticated (and that is refreshed correctly).
So, what am i doing wrong here?
I did it rendering the route or the redirect depending on the condition and it worked for me.
Something like the following:
class PrivateRouteComponent extends React.Component{
render(){
return (
this.props.isAuthenticated===true?
(
<Route path={this.props.path} render={this.props.component} />
):
(<Redirect to={{
pathname: '/login',
state: { from: this.props.path }
}} />));
}
}

Catch the incoming route and redirect back after login

I have an app with login and dahsboard with threads. The particular thread has it's own url address. If i want to share the thread url with someone, he would need to log-in first and then I need to redirect him back to the thread's url. So basically I need to somehow get the location immediatelly he enters the app, save it to the eg. redux store, and after he successfuly log-in, redirect him back to the thread url. I have everything working except the getting the incoming url.
Now I have this routes.js:
const requireLogin = (nextState, replace, cb) => {
function checkAuth() {
const { userAuth: { loggedIn }} = store.getState();
if (!loggedIn) {
replace('/login');
}
cb();
}
checkAuth()
}
<Route path="/" component={App}>
<IndexRedirect to="/dashboard" />
<Route path="/login" component={LoginPage} />
<Route onEnter={requireLogin}>
<Route path="/dashboard" component={Dashboard} />
<Route path="/thread/:id" component={SingleThread} />
</Route>
</Route>
I my App react component I have:
class App extends Component {
constructor(props) {
super(props)
}
getChildContext() {
console.log("LOCATION:" + JSON.stringify(this.props.location))
return { location: this.props.location }
}
...
render() {
const { children } = this.props
return (
<div className={style.normal}>
{children}
</div>
)
}
}
to get incoming thread's url, but everytime I put eg. /thread/3 the console logs the '/' or the '/login' which I am redirecting to not logged users. Maybe the React Router TransitionHooks could help me, but I don't know how to use it properly in this case.
I am using react-router v2.0.0 with react-router-redux v4.0.2
Thanks for any suggestions!
You can try to wrap the private area in a component like ->
<Route path="/" component={App}>
<IndexRedirect to="/dashboard" />
<Route path="/login" component={LoginPage} />
<Route component={PrivateWrapper}>
<Route path="/dashboard" component={Dashboard} />
<Route path="/thread/:id" component={SingleThread} />
</Route>
</Route>
Your PrivateWrapper should look like this
class PrivateWrapper extends React.Component {
constructor(props) {
super(props);
this.checkIfUserIsValid = this.checkIfUserIsValid.bind(this);
}
checkIfUserIsValid(){
let {user} = this.props.auth;
if(user == null){
this.context.router.push("/login");
}
}
render(){
let {user} = this.props.auth;
if(user == null)
return <div></div>
else{
return <div>{this.props.children}</div>
}
}
PrivateWrapper.contextTypes = {
router: React.PropTypes.object
};
export default connect(**Auth state here**)(PrivateWrapper);
This way you dont even need hooks or anything.

Categories