15

I'm trying to implement authentication using Auth0 in a react-admin v3 app. I need to implement an authProvider that talks with Auth0. This sounds like something that should be available somewhere, but the closest I could find was https://github.com/alexicum/merge-admin/blob/master/src/Auth/index.js, which is about 2 years old (the SDKs have changed since then).

Is there an Auth0 authProvider somewhere I can reuse, or do I have to implement it myself?

Thanks!

Vitor Baptista
  • 2,016
  • 1
  • 23
  • 28
  • Did you have any luck with this? I'm not a React developer and about to attempt the same thing and was rather surprised that your question is the most relevant thing I could find. – Hastarin Feb 14 '20 at 22:49
  • 2
    @Hastarin not really, I had to develop one myself. It's still pretty hackish, and the Auth0 and react-admin AuthProvider abstraction is leaky at places. It's working for now, but I'm still looking for a better solution. The best so far is https://github.com/alexicum/merge-admin/blob/master/src/Auth/index.js. – Vitor Baptista Mar 07 '20 at 13:39

4 Answers4

8

for reference sake, here's an example of a way to integrate react admin with auth0-react package

index.js

import { Auth0Provider } from "@auth0/auth0-react";

ReactDOM.render(
  <Auth0Provider
    domain="XXXXX.auth0.com"
    clientId="XXXXX"
    audience="https://XXXXX"
    redirectUri={window.location.origin}
  >
    <React.StrictMode>
      <App />
    </React.StrictMode>
  </Auth0Provider>,
  document.getElementById("root")
);

App.js

import { withAuth0, withAuthenticationRequired } from "@auth0/auth0-react";
import ApolloClient from "apollo-boost";

// I'm using Hasura w/ JWT Auth, so here's an example of how to set Authorization Header
async componentDidMount() {
    const token = await this.props.auth0.getAccessTokenSilently();

    const client = new ApolloClient({
      uri: "https://HASURA_URL/v1/graphql",
      headers: {
        Authorization: `Bearer ${token}`
      },
    });

    buildHasuraProvider({ client }).then((dataProvider) =>
      this.setState({ dataProvider })
    );
  }

export default withAuthenticationRequired(withAuth0(App));
pnl data
  • 426
  • 5
  • 4
  • While this doesn't conform to react-admin's opinionated way of handling auth, this is certainly very simple and so far seems reliable. Thanks! – Jake Stoeffler Aug 25 '20 at 02:28
3

I've created a sample application with Auth0 and react-admin way of auth

https://github.com/spintech-software/react-admin-auth0-example

Here is auth provider code for reference

import authConfig from "./authConfig";
import {Auth0Client} from '@auth0/auth0-spa-js';

const auth0 = new Auth0Client({
    domain: authConfig.domain,
    client_id: authConfig.clientID,
    cacheLocation: 'localstorage',
    useRefreshTokens: true
});

const CallbackURI = "http://localhost:3000/login"

export default {
    // called when the user attempts to log in
    login: (url) => {
        if (typeof url === 'undefined') {
            return auth0.loginWithRedirect({
                redirect_uri: CallbackURI
            })
        }
        return auth0.handleRedirectCallback(url.location);
    },
    // called when the user clicks on the logout button
    logout: () => {
        return auth0.isAuthenticated().then(function (isAuthenticated) {
            if (isAuthenticated) { // need to check for this as react-admin calls logout in case checkAuth failed
                return auth0.logout({
                    redirect_uri: window.location.origin,
                    federated: true // have to be enabled to invalidate refresh token
                });
            }
            return Promise.resolve()
        })
    },
    // called when the API returns an error
    checkError: ({status}) => {
        if (status === 401 || status === 403) {
            return Promise.reject();
        }
        return Promise.resolve();
    },
    // called when the user navigates to a new location, to check for authentication
    checkAuth: () => {
        return auth0.isAuthenticated().then(function (isAuthenticated) {
            if (isAuthenticated) {
                return Promise.resolve();
            }
            return auth0.getTokenSilently({
                redirect_uri: CallbackURI
            })
        })
    },
    // called when the user navigates to a new location, to check for permissions / roles
    getPermissions: () => {
        return Promise.resolve()
    },
};

Sergey
  • 379
  • 2
  • 5
3

My answer is following react-admin approach where I use its authProvider like below. There are two main steps:

  • Get needed data from useAuth0 hook.
  • Convert authProvider into function where it takes the above values, and return an object like default.
// In App.js

import authProvider from './providers/authProvider';// my path is changed a bit

const App = () => {
  const {
    isAuthenticated,
    logout,
    loginWithRedirect,
    isLoading,
    error,
    user,
  } = useAuth0();
  const customAuthProvider = authProvider({
    isAuthenticated,
    loginWithRedirect,
    logout,
    user,
  });

  return (
      <Admin
        {...otherProps}
        authProvider={customAuthProvider}
      >
      {...children}
      </Admin>
    );
}
// My authProvider.js

const authProvider = ({
  isAuthenticated,
  loginWithRedirect,
  logout,
  user,
}) => ({
  login: loginWithRedirect,
  logout: () => logout({ returnTo: window.location.origin }),
  checkError: () => Promise.resolve(),
  checkAuth: () => (isAuthenticated ? Promise.resolve() : Promise.reject()),
  getPermissions: () => Promise.reject('Unknown method'),
  getIdentity: () =>
    Promise.resolve({
      id: user.id,
      fullName: user.name,
      avatar: user.picture,
    }),
});

export default authProvider;

That's it.

ShinaBR2
  • 2,519
  • 2
  • 14
  • 26
  • 1
    Thanks for making this! it was super helpful. The only changes i had to make to get it to work were to include an `|| loading` to check the loading flag from auth0 (which i added to the parameter) in the condition being checked in `checkAuth` (or i was immediately logged out again) and move the loginWithRedirect call to a custom login page, changing `login` to be the same as checkAuth (because login is called after the user logs in to check if the auth was successful) – MoralCode Feb 06 '22 at 21:41
  • Yes agree, checking `loading` is crucial, I forgot to mention that. – ShinaBR2 Feb 08 '22 at 12:23
2

It's more convenient to wrap the react-admin app with auth0 native login, and then provide react-admin dataProvider an http client that reads the jwt token stored in local storage by auth0.

viging543
  • 31
  • 1
  • 6