logoGERASOFT
Blog

From Idea to App - Implement Authentication in React

Reading time: 10-20 minsTechnicality: 5/5Last updated: Mar 9, 2025
From Idea to App - Implement Authentication in React

Now that the server is ready, we can take a step further into making our frontend live by consuming the API.

Upcoming

Earlier the design team provided us the designs for each authentication screens (you can check them here). We will bring them into code, and make our frontend handle authentication via our GraphQL API. For that we will:

Set up Apollo Client

ClickUp ticket

Creating the auth store

We created a persistent zustand store useAuthStore that has the following properties:

  • auth – The object where we store the user’s details extracted from the decoded JWT accessToken.
  • accessToken – The encoded access token.
  • setTokens(accessToken) – A function where we receive the encoded access token, decode it, and store the decoded contents in auth, and the encoded accessToken itself.
  • reset – A function that resets the auth store, used on logout.

Initialising Apollo Client

First, we set up apolloClient with the appropriate links:

  • httpLink – This is where we configure the GraphQL endpoint, using import.meta.env.VITE_API_URL which is an environment variable we set in the .env file.
  • authLink – This link is responsible for making sure we always supply a valid access token for signed in users in every request. Here’s how it works:
    • If the user is signed in, and has a token but it has expired, we use our refreshToken (stored in HTTP only cookies) to generate a new accessToken via our REST API endpoint /auth/refreshTokens.
    • If the tokens are successfully refreshed, we store our new accessToken in useAuthStore, and continue with our request to the GraphQL API.
    • If we couldn’t refresh the tokens, we sign the user out, but continue with the request to the GraphQL API.
  • errorLink (added with the toast message panel ticket) – Responsible for handling network errors (if the servers are unavailable, or the user has weak internet connection), and giving feedback to the user via toast messages.

Handling logout

We created an util function logout that:

  • Calls the Logout GraphQL mutation, removing the refreshToken from the database, invalidating it.
  • Resets the apolloClient cache.
  • Resets the auth store.

Creating custom Apollo hooks

Our API intelligently lets the frontend know when the user needs to be logged out if they try to access an endpoint they don’t have access to via the extensions.doLogout flag. We created custom hooks that log the user out with the above logout function if we are notified about it.

The hooks are:

  • useQuery
  • useLazyQuery
  • useMutation

Generating hooks via graphql-codegen

This may be familiar as we used codegen in our API to generate types for our resolvers, and in @repo/graphql to generate the schema types.

We are using it in our web app to generate hooks using the above custom hooks as a base, so we don’t have to manually define each query with their types. We reference the queries from @repo/graphql/queries, so every query will have a custom hook generated with types in place.

We will see this in action when implementing the authentication screens.

GitHub changes

Set up toast messages

ClickUp ticket

As per the designs we will have to provide users with feedback across different screens about the API responses. These include error messages, and success messages like letting the users know after setting their passwords that they can use their new credentials on the sign in screen.

Creating the messages store

We created a centralised zustand store for the messages, so they can be accessed from any component.

When messages are created, they can be specified with:

  • type – The type of the message, can be:
    • success
    • error
    • warning
    • info
  • id – Identifies the message, so messages with the same id won’t be duplicated. If not specified, they receive a random uuid.
  • text – The content of the message.
  • href – Optional, users will be redirected to the specified location if provided when clicking the toast card.
  • expireIn – Optional, when set, the message will automatically disappear after the specified time (in milliseconds).

The messages can be created with the addMessages method of the store, which accepts an array of message inputs specified as above. Every message stored will be assigned a remove function which removes itself from the list when called.

The store is storing the messages in messages, and every message can be removed with the store’s removeMessages method.

Creating the toast messages panel component

Following the designs, we created a floating scrollable toast panel with fixed position on the bottom left corner of the screen.

We made sure that the panel won’t make the site inaccessible on smaller devices as the max height is 60% of the device height, and max lg wide.

On top of the messages a Clear button appears that removes all messages.

Extend error handling in GraphQL query hooks

To provide meaningful error messages on request failures, we extended our custom query and mutation hooks so when an error occurs it automatically creates error messages to the store.

We also added a custom option preventToastError to the hooks which when set to true, skips adding new messages to the store, allowing for custom error handling inside the component.

GitHub changes

Sign in screen

ClickUp ticket

Now that we wired the frontend and API together, it’s time to implement screens. We will start with the sign in screen.

Creating the screen

We created the SignIn component. It allows users to enter their emails and passwords to authenticate themselves so we can show their content in the app. Here’s a summary of its key features:

  1. Form Handling with Validation
    • Uses react-hook-form with zod for schema-based form validation.
    • The form requires an email (validated as a proper email) and a password.
  2. Authentication Logic
    • Integrates with useSignInMutation to send login credentials to the backend.
    • On successful authentication, it resets Apollo Client’s cache, and stores the access token in useAuthStore which will extract user information from it, authenticating the user.
  3. User Experience Enhancements
    • Provides inline validation error messages for email and password.
    • A button allows users to toggle password visibility (IoEye and IoEyeOff icons).
    • All form controls, including inputs, are disabled during submission.
    • Displays a loading state and disables the submit button when necessary.
    • Shows error messages from the authentication API.
    • Uses react-i18next to support multilingual text in both the form and confirmation message.
  4. Navigation & Links
    • Includes links to password reset and account creation pages.

Creating routes

We extended our existing routing utility (ROUTES), adding all necessary route paths as variables for authentication:

  • SIGN_UP
  • SIGN_IN
  • SET_PASSWORD({passwordTokenId})
  • RESET_PASSWORD
  • VERIFY_EMAIL({verificationTokenId})

Some of these are constants, storing the path as string (like SIGN_UP resolving to /signUp), and some are dynamic route functions, replacing the parameters with the provided value (SET_PASSWORD({ passwordTokenId: 'pwt' }) returning /setPassword/pwt).

Creating the router

We created a new router UnauthenticatedRoutes, which matches the SIGN_IN route with the SignIn component, and redirects users to it in case they navigate to an unknown route.

We are also conditionally rendering different routers based on the user’s authentication status, which is extracted from useAuthStore's auth object.

We render:

  • AuthenticatedRoutes – if auth is set
  • UnauthenticatedRoutes – otherwise

GitHub changes

Sign up screen

ClickUp ticket

We implemented the SignUp component to handle user registration. Below is a breakdown of its key features:

  1. Form Handling & Validation
    • Uses react-hook-form with zod for schema-based validation.
    • The form requires an email address, which is validated as a properly formatted email.
  2. Authentication Logic
    • Integrates with useSignUpMutation to send user details to the backend.
    • Prevents duplicate toast error messages by using preventToastErrors: true.
    • On successful sign-up, updates the state to display a confirmation message.
  3. User Experience Enhancements
    • Provides inline validation error messages for incorrect email input.
    • Disables form submission when:
      • The form is incomplete or invalid.
      • The request is in progress (isLoading state).
    • Displays server error messages when the mutation fails.
    • Uses react-i18next to support multilingual text in both the form and confirmation message.
  4. Navigation & Links
    • Includes a redirect link to the sign-in page via the AuthLink component.
    • The route is matched with ROUTES.SIGN_UP in UnauthenticatedRoutes.

GitHub changes

Reset password screen

ClickUp ticket

We implemented the ResetPassword component to allow users to request a password reset. They can enter their email address to which we send out a link that allows them to securely set a new password. Below is a breakdown of its key features:

  1. Form Handling & Validation
    • Uses react-hook-form with zod for schema-based validation.
    • Requires an email address, ensuring it is properly formatted.
  2. Password Reset Logic
    • Integrates with useResetPasswordMutation to send a reset password request to the backend.
    • Prevents duplicate toast error messages using preventToastErrors: true.
    • On successful submission, updates the state to display a confirmation message.
  3. User Experience Enhancements
    • Provides inline validation error messages for incorrect email input.
    • Disables form submission when:
      • The form is incomplete or invalid.
      • The request is in progress (isLoading state).
    • Displays server error messages when the mutation fails.
    • Supports multilingual text via react-i18next.
  4. Navigation & Links
    • Includes links to the sign-in and account creation pages via the AuthLink component.
    • The route is matched with ROUTES.RESET_PASSWORD in UnauthenticatedRoutes.

GitHub changes

Set password screen

ClickUp ticket

As the next step in the authentication flow, we created the SetPassword component, which allows users to set a new password using the passwordTokenId from the URL path. Here’s how it works:

  1. Token Verification
    • Uses useVerifyPasswordTokenQuery to validate the passwordTokenId before displaying the form.
    • If the token is invalid, displays an error message with a link to reset the password again.
    • While verifying, an OverlaySpinner is shown to indicate loading.
  2. Form Handling & Validation
    • Uses react-hook-form with zod for schema-based validation.
    • The form requires:
      • A new password, validated against passwordSchema.
      • A confirmation password, which must match the new password.
    • Provides inline validation error messages.
  3. Password Reset Logic
    • Integrates with useSetPasswordMutation to send the new password and token to the backend.
    • Prevents duplicate toast error messages using preventToastErrors: true.
    • On success:
      • Redirects the user to the sign-in page.
      • Displays a success message using useMessageStore.
  4. User Experience Enhancements
    • Provides a password visibility toggle with IoEye and IoEyeOff icons.
    • Disables form submission when:
      • The form is incomplete or invalid.
      • The request is in progress (isLoading state).
    • Displays server error messages when the mutation fails.
    • Uses react-i18next for multilingual support.
  5. Navigation & Links
    • If the password token is invalid, provides a link to reset the password again.
    • The route is matched with ROUTES.SET_PASSWORD({ passwordTokenId: ':passwordTokenId' }) where the passwordTokenId is the parameter that will be replaced with the actual value when visiting /setPassword/pwt (for example, pwt).

We also fixed a mistake where we previously defined VerifyPasswordToken as a mutation while it is a query as it doesn’t have side effects.

GitHub changes

Verify email screen

ClickUp ticket

The VerifyEmail component handles email verification as part of the authentication flow. Below is a breakdown of its functionality:

  1. Automatic Verification
    • Extracts verificationTokenId from the URL parameters.
    • Calls useVerifyEmailMutation on mount to verify the email with the backend.
    • Prevents duplicate toast error messages using preventToastErrors: true.
  2. Redirection on Success
    • If the verification is successful and the response includes a passwordTokenId, redirects the user to the Set password screen (ROUTES.SET_PASSWORD({ passwordTokenId })).
  3. Handling Errors
    • If verification fails, displays an error message with a link to the sign-up page, allowing the user to create an account again.
  4. User Experience Enhancements
    • Displays a loading spinner (OverlaySpinner) while verification is in progress.
    • Uses react-i18next for multilingual support.

GitHub changes

Sidebar & Sign-Out Functionality

ClickUp ticket

With the authentication flow complete—allowing users to create, recover, and authenticate their accounts—we now need to provide a way for them to log out.

Sidebar

To avoid cluttering the main interface with a sign-out button, we implemented a hamburger menu in the navigation bar. This menu toggles a sidebar, which:

  • Appears as a floating overlay.
  • Can be dismissed by clicking outside the sidebar or using the back button.
  • Displays the currently logged-in user's email.
  • Provides a Sign Out button.

Sign-Out Mechanism

Pressing the Sign Out button triggers our previously defined logout utility, which:

  • Calls the API Logout mutation, removing the refreshToken from the database, invalidating it.
  • Resets the apolloClient cache.
  • Resets the auth store.

Implementation Details

  • Added a reusable Sidebar component with a floating layout.
  • Introduced isSidebarOpen state in useAppStore to manage sidebar visibility.
  • Modified the main authenticated screen to include the sidebar.
  • Updated the navigation bar to include a hamburger button for opening the sidebar.
  • Implemented sidebar functionality to display account information and a Sign Out button.

Signing out will trigger a sequence of events:

  1. Calling Logout in the API will remove the refreshToken from both the cookies and the database so it cannot be used anymore to generate accessTokens.
  2. Clearing the authStore will make auth be null, so our Routes component will switch routers from AuthenticatedRoutes to UnauthenticatedRoutes.
  3. The UnauthenticatedRoutes router will redirect the users to /signIn.

GitHub changes

Conclusion

With these authentication flows in place, users can now sign up, reset passwords, verify their emails, and sign out securely. Our frontend is fully integrated with the GraphQL API, ensuring a smooth authentication experience.

A summary of all code changes can be found on this GitHub link.

Next steps

Currently the users can authenticate themselves, but the tasks created don’t belong to their account as we are still using our local mock API for task operations. In the next post we will use Apollo Client to perform task operations with our real API.

Here's how our timeline look like now:

12345
Integrate frontend with GraphQL API for task management
5 d