Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move to Auth0 for authentication #483

Merged
merged 6 commits into from
Jan 25, 2021
Merged

Move to Auth0 for authentication #483

merged 6 commits into from
Jan 25, 2021

Conversation

daltonfury42
Copy link
Collaborator

@daltonfury42 daltonfury42 commented Jan 24, 2021

Resolved #461

@maaverik I was trying out auth0 as an authentication/identity provider. It went very well, and I am happy with the setup.

Followed their React Quickstart for the basic setup. I had to refactor our API calling logic a bit:

  1. Defined a new hook called useRequest which returns a function that can be used from within components to make authenticated API calls.
  2. All the request generation code is defined in api/requestFactory folder.

After the refactor, our code is more cleaner, to me. I've added comments with sample usage in the useRequest.js file.

TODO:

  1. Backend side changes to verify the access token with auth0. Without this, backend calls after login will result in auth failure.
  2. Home page customisations for logged in user and list of queues in new logic.

Copy link
Collaborator

@maaverik maaverik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still on the fence about this refactor. The old format felt more intuitive to me, we call a service, and it handles making the appropriate request, it felt like better abstraction. Now, we have two imports in every component for requests and each component has to explicitly make the request. I would have preferred having the useRequest be used in the old service scripts we had.

simplq/src/api/requestFactory/index.js Show resolved Hide resolved
// ...
// });
// }

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Kudos on the description

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel a bit more commenting is required in the request factory side. Let me add

@daltonfury42
Copy link
Collaborator Author

daltonfury42 commented Jan 25, 2021

Let me try to pull you off the fence. 😉

Right, the old design was simpler, but we have to do this as Auth0 lets you get access token for making API calls only via the React Hook that it provides. So we would have to write the code to fetch access token inside each component and pass it down to our service object as hooks can't be called outside of other hooks and functional components:

useEffect(() => {
  const getUserMetadata = async () => {
    const domain = "YOUR_DOMAIN";

    try {
      const accessToken = await getAccessTokenSilently({
        audience: `https://${domain}/api/v2/`,
        scope: "read:current_user",
      });

      const userDetailsByIdUrl = `https://${domain}/api/v2/users/${user.sub}`;

      const metadataResponse = await fetch(userDetailsByIdUrl, {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      });

      const { user_metadata } = await metadataResponse.json();

      setUserMetadata(user_metadata);
    } catch (e) {
      console.log(e.message);
    }
  };

  getUserMetadata();
}, []);

(Example from their quick start)

That would clutter our component code everywhere we do an API call, and the react way of solving this is to define our own custom hook. Auth0 has given a sample here. This looked horrible to me at first, but after asking around, hooks React’s recommended way of solving this problem for functional components where you need to reuse code that maintains state (Ref, Ref). Hooks in react allows you to keep common logic for functional components in one place

Now theory aside, this design might not look very intuitive at first, but there are some advantages.

There are two parts to our API handling logic now, 1) a useRequest hook that makes network calls and 2) RequestFactory to create request objects.

The useRequest hook code is write once and forget. It abstracts out and makes network requests from inside react components. The RequestFactory is responsible for generating different types of requests.

It's a bit of single responsibility principle + separation of concerns in action, also it's easy to extend. For example, when a new backend API is added, all you need to do is define it's request structure in the request factory, and it's ready to use:

requestFactory/queue.js
export const create = (queueName) => ({ method: 'post', url: '/queue', data: { queueName } });

(To someone who is reading our code, we are hiding away all that network request making code behind the hook, and he would not need to understand this. All in all, if this design stays for the long term, the first PR might look a bit less intuitive / complex, but it will ease things from then on.)

image

@daltonfury42
Copy link
Collaborator Author

daltonfury42 commented Jan 25, 2021

TLDR; If we don't do this, our components will get cluttered with access token fetching code.

This is true even if we use Okta: okta/okta-oidc-js#825

This looks like the recommended approach from my research. Let me know if you have any other solution in mind, though.

@maaverik
Copy link
Collaborator

I see, so since Auth0 recommends using its services through hooks, we can't get the token outside components. Then, you're right, this is probably the best approach.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

"Login Failed. Please try again" PreventDefault
3 participants