Reputation: 1790
I want to add a signup page to my react-admin
web portal. Since react-admin
do not provide a signup page, I created a custom page and added it to custom routes:
customRoutes.js
import React from 'react';
import { Route } from 'react-router-dom';
import SignupForm from './signupForm';
export default [
<Route path="/signup" component={SignupForm} noLayout/>,
];
The problem is that I am only able to open the page at /signup
when a user is already signed in. Otherwise I am automatically redirected to /login
route.
How to disable authentication for custom routes? Is there some attribute which <Route>
accepts or something to do with the dataProvider.js
?
EDIT:
Adding the representative code for my signupForm.js
:
import React from 'react';
import TextField from '@material-ui/core/TextField';
import Button from '@material-ui/core/Button';
import Dialog from '@material-ui/core/Dialog';
import { fetchUtils } from 'react-admin';
import { ApiUrl } from './config';
class SignupForm extends React.Component {
constructor() {
super();
this.state = {
fields: {
username: '',
password: ''
}
}
handleChange = name => event => {
let fields = this.state.fields;
fields[name] = event.target.value;
this.setState({
fields: fields,
});
};
handleSubmit = (event) => {
// Prevent default
event.preventDefault();
if (this.handleValidation()) {
let body = JSON.parse(JSON.stringify(this.state.fields));
let url = ApiUrl + '/api/user/create';
let options = {}
options.headers = new Headers({ Accept: 'application/json' });
options.method = 'POST'
options.body = JSON.stringify(body);
fetchUtils.fetchJson(url, options)
.then(data => {
alert(data.json.message);
this.props.history.push('/login')
})
.catch((err, ...rest) => {
console.log(err.status, err.message);
alert(err.message);
});
}
}
render() {
const { classes } = this.props;
const actions = [
<Button
type="submit"
label="Submit"
color='primary'
variant="flat"
>Submit</Button>,
];
return (
<Dialog
open={true}
style={{
textAlign: "center",
}}
onClose={this.handleClose}
classes={{ paper: classes.dialogPaper }}
>
<DialogTitle>Create an Account</DialogTitle>
<form className={classes.container} noValidate autoComplete="off" onSubmit={this.handleSubmit}>
<TextField
required
id="username"
label="Username"
value={this.state.fields["username"]}
onChange={this.handleChange('username')}
/>
<br />
<TextField
required
id="password"
label="Password"
value={this.state.fields["password"]}
onChange={this.handleChange('password')}
type="password"
/>
<div style={{ textAlign: 'right', padding: 40 }}>
{actions}
</div>
</form>
</Dialog>
);
}
}
export default withStyles(styles)(SignupForm);
Upvotes: 8
Views: 6247
Reputation: 61
in my case with react-admin 3.12.5 i need do this:
Add noLayout for Route:
const ForgotPassword = props => {
return <div>Forgot Password View</div>;
};
const customRoutes = [
<Route exact path="/forgot-password"
component={ForgotPassword}
noLayout/>,
];
const App = (props) => {
return (
<Admin
theme={theme}
customRoutes={customRoutes}
authProvider={authProvider}
dataProvider={dataProvider}
i18nProvider={i18nProvider}
loginPage={MyLoginPage}
catchAll={() => <Redirect to="/"/>}
>
{resourcesByPermissions}
</Admin>
)
};
Go to http://localhost:3000/#/forgot-password
, need add /#
for href
<a href="/#/forgot-password">Forgot-password?</a>
Upvotes: 0
Reputation: 570
Inside your authProvider you should have a method called checkAuth
that returns Promise.resolve()
if the window.location.pathname
is a public route.
const publicRoutes = [
'/appointment',
]
const authProvider: AuthProvider = {
login: ({ username, password }) => {
},
checkAuth: () => {
if (publicRoutes.includes(window.location.pathname)) {
return Promise.resolve()
}
// your authentication endpoint / system
// ...
},
getPermissions: () => {
},
logout: () => {
},
checkError: (error) => {
},
getIdentity: () => {
},
};
Upvotes: 0
Reputation: 11
You can make an enum-like object PublicRoutes
with all unauthenticated routes in your customRoutes.js
file like this:
customRoutes.js
export const PublicRoutes = {
SIGNUP: "/signup",
}
export const customRoutes = [
<Route path={PublicRoutes.SIGNUP} component={SignupForm} noLayout/>
]
Then in your authProvider
import history
or router
object (created in your Admin
component), to get access to current location pathname
.
Next, make a function expression isPublicRoute
, which will check if the current route can be served without authentication.
Add this check on top of AUTH_GET_PERMISSIONS
and optionally AUTH_CHECK
(if you have e.g. JWT resolver here).
For AUTH_GET_PERMISSIONS
return Promise.resolve()
as we have permissions to public routes. For AUTH_CHECK
return Promise.reject()
as we don't need authorization here (e.g. fetch or resolve JWT).
authProvider.js
import {PublicRoutes} from "./customRoutes";
...
const isPublicRoute = (pathname) => Object.values(PublicRoutes).some(route => route === pathname);
...
const {pathname} = router.location;
if (type === AUTH_GET_PERMISSIONS) {
// has to be on top
if(isPublicRoute(pathname)) return Promise.resolve();
...
}
if (type === AUTH_CHECK) {
// has to be on top
if(isPublicRoute(pathname)) return Promise.reject()
...
}
Upvotes: 0
Reputation: 5842
React-admin V3 version, change authProvider to return 'guest' role by default:
authProvider.js
...,
getPermissions: () => {
const role = localStorage.getItem('permissions')
return role ? Promise.resolve(role) : Promise.resolve('guest') // default 'guest'
},
...
Now your customRoutes pages will no longer redirect to /login, and you can use the usePermissions hook to check the role in your custom page.
Upvotes: 1
Reputation: 1790
The problem was that on request to /signup
, react-admin
was calling the authProvider
with type AUTH_GET_PERMISSIONS
whose code was:
if (type === AUTH_GET_PERMISSIONS) {
const role = localStorage.getItem('role');
return role ? Promise.resolve(role) : Promise.reject();
}
Since the user was not logged in so localStorage.role
was never initialised.
Changed it to:
if (type === AUTH_GET_PERMISSIONS) {
const role = localStorage.getItem('role');
return role ? Promise.resolve(role) : Promise.resolve('guest');
}
Upvotes: 7