Search code examples
reactjsreact-admin

Cookie-based authentication via REST API in react-admin


I'm new to react-admin. I already read through all the questions here in stackoverflow, and google'd for my question too, but did not find any useful solution.

I am setting up React-admin to replace an existing admin page for one of my projects. I use cookie-based authentication via REST API.

Is it possible (and if yes how?) to use it in react-admin? Can someone please lead me to the right direction?

Cheers!


Solution

  • It is possible of course. You just have to make fetch use cookies.

    react-admin uses fetch to send http requests to your back-end. And fetch does not send cookies by default.

    So to make fetch send cookies, you have to add the credentials: 'include' option for every fetch call the app makes.

    (if your admin and api are not on the same domain, you will have to enable CORS on your back-end.)

    See react-admin's doc for how to customize requests on the dataProvider here: https://github.com/marmelab/react-admin/blob/master/docs/Authentication.md#sending-credentials-to-the-api

    import { fetchUtils, Admin, Resource } from 'react-admin';
    import simpleRestProvider from 'ra-data-simple-rest';
    
    const httpClient = (url, options = {}) => {
        if (!options.headers) {
            options.headers = new Headers({ Accept: 'application/json' });
        }
        const token = localStorage.getItem('token');
        options.headers.set('Authorization', `Bearer ${token}`);
        return fetchUtils.fetchJson(url, options);
    }
    const dataProvider = simpleRestProvider('http://localhost:3000', httpClient);
    
    const App = () => (
        <Admin dataProvider={dataProvider} authProvider={authProvider}>
            ...
        </Admin>
    );
    

    You'll have to customize this to add options.credentials = 'include' like so :

    const httpClient = (url, options = {}) => {
        if (!options.headers) {
            options.headers = new Headers({
              Accept: 'application/json'
            });
        }
        options.credentials = 'include';
        return fetchUtils.fetchJson(url, options);
    }
    

    You will have to do the same thing for the authProvider.

    Something like

    // in src/authProvider.js
    export default (type, params) => {
        // called when the user attempts to log in
        if (type === AUTH_LOGIN) {
            const { username, password } = params;
            const request = new Request(`${loginUri}`, {
                method: 'POST',
                body: JSON.stringify({ username: username, password }),
                credentials: 'include',
                headers: new Headers({ 'Content-Type': 'application/json' }),
            });
            return fetch(request)
            .then(response => {
                if (response.status < 200 || response.status >= 300) throw new Error(response.statusText);
    
                localStorage.setItem('authenticated', true);
            });
        }
        // called when the user clicks on the logout button