# 3.E.6: Carousell Auth

## Learning Objectives

1. Know how to implement industry-standard authentication in an app with a SQL backend

## Introduction

We will implement authentication in a simple Carousell clone that allows users to login, view, list and buy items. Users can browse items without logging in, but will need to login to list or buy items.

## Setup

### Fork and clone repos

Fork and clone Rocket Academy's [Carousell Frontend ](https://github.com/rocketacademy/carousell-frontend-3.2)and [Carousell Backend](https://github.com/rocketacademy/carousell-backend-bootcamp) repos.

Rocket has set up starter code such that users can:

1. View listings from the home page
2. List new items by clicking on "Sell" from the home page
3. Buy items by clicking "Buy" at the bottom of item-specific pages

The starter code follows the structure of our Bigfoot frontend and backend. Rocket followed backend setup instructions in Bigfoot SQL to set up the backend.

### Setup frontend

Run `npm i` to install packages

### Setup backend

1. Run `npm i` to install packages
2. Create and update a .env to pass crendials to  `config/database.js` Model the sample that is part of the repo, feel free to remove the sample when finished.
3. Run `npx sequelize db:create` to create the `carousell_development` database
4. Run `npx sequelize db:migrate` to set up database schema
5. Run `npx sequelize db:seed:all` to seed users and listings in the database
6. Verify seed data was added by viewing the `users` and `listings` tables in our SQL client

### Verify starter code is working

1. Start the backend with `npm start`
2. Start the frontend with `npm run dev`, and then open a browser of your choice and navigate to "<http://localhost:5173>".
3. Verify we do not get errors in our backend or frontend, and we can see 3 seed listings at "<http://localhost:5173>" in our browser. Click on a listing to view its details. Buy and sell listings to observe what happens, and notice that the buyer and seller IDs have been hard-coded to 1 (the seed user).

## Base: Require authentication to create listings and buy items

We will now put the theory we learnt in Rocket's Authentication submodule into practice.

### Setup backend API authorisation

Refer to the following official Auth0 Express Authorization guide as a reference.

{% embed url="<https://auth0.com/docs/quickstart/backend/nodejs/01-authorization>" %}
Official Auth0 route-authorisation guide for Express apps
{% endembed %}

#### Setup Auth0 API in Auth0 dashboard

1. Navigate to the API section of the Auth0 dashboard (under Applications in the left navbar) and click Create API
2. Choose a name and identifier for our API. Rocket named ours Carousell API and used `https://carousell/api` as our identifier. Leave the signing algorithm as RS256.
3. After creating the API we should see a Quick Start page. Ignore sample code on that page because it uses outdated libraries. We will be following setup instructions in the official Auth0 guide instead, which uses a [new library that replaced the outdated libraries](https://auth0.com/blog/introducing-oauth2-express-sdk-protecting-api-with-jwt/).
4. Back in the official Auth0 guide, skip the section on defining permissions for now. By default all authenticated users will be able to list and buy items in our app.

#### Install Auth0 library in our backend and use Auth0 middleware to verify authentication on protected routes

1. Install `express-oauth2-jwt-bearer`, Auth0's new, simplified library for verifying authentication
2. Import the `auth` property of `express-oauth2-jwt-bearer` in our app like in the code example in the Auth0 guide. Note that Rocket's app setup requires `import` syntax instead of `require` syntax. Copy the `checkJwt` variable definition and replace `YOUR_API_IDENTIFIER` with the API identifier we chose above.
3. Add `checkJwt` as a middleware between the route path and route handler function for our create-new-listing and buy-listing routes. See sample code under Protect API Endpoints section of Auth0 guide for reference. `checkJwt` will validate authentication before Express runs our route handler functions.
4. No need to check scopes because we are not using scopes for now.

Testing the security of our API independently is [more work than it's worth right now](https://auth0.com/docs/quickstart/backend/nodejs/02-using#obtaining-an-access-token), so we will move on and test our API together with our frontend!

### Login from frontend

We will follow the following official Auth0 guide for React apps.

{% embed url="<https://auth0.com/docs/quickstart/spa/react/01-login>" %}
Official Auth0 setup guide for React apps
{% endembed %}

#### Setup Auth0 application in Auth0 dashboard

1. Create a new Auth0 application in the Auth0 dashboard. Give it the name "Carousell" and choose the Single Page Web Application application type.
2. Add `http://localhost:5173` to Allowed Callback URLs, Allowed Logout URLs and Allowed Web Origins in the new application's Application Settings page. Save changes at bottom of page. If we deploy our app later, we will also need to add the deployed URL to these sections.

#### Install Auth0 React library and configure Auth0 resources to be available in our app

1. Install Auth0 React SDK with `npm i @auth0/auth0-react`
2. Configure the `Auth0Provider` higher-order component that wraps all other components in `index.js`. If we are logged in when viewing the Auth0 guide, we can choose the relevant Auth0 Application and Auth0 will auto-populate the properties we need for `Auth0Provider` in the guide for us to copy.

#### Retrieve Auth0 resources from React context and use them to login in our app

We want our users to be able to view all listings and individual listings without logging in. We only want them to login when they have to, in our case when they wish to list or buy items.&#x20;

We will use Auth0's `loginWithRedirect` function in 2 places: when unauthenticated users click "Sell" to list items and when unauthenticated users click "Buy" to buy items.

1. Add a `useEffect` hook in `NewListingForm` component that calls `loginWithRedirect` if the current user is not authenticated. We can check authentication status with `isAuthenticated` boolean property returned by `useAuth0` hook
   1. Retrieve the logged-in user's email with the `user` object property returned by `useAuth0` hook and send that email as the seller's email to our backend in `handleSubmit` with the other listing data.
   2. Update the relevant method within the controller attached to the  routing middleware in our backend to find or create the seller user in our backend before creating a new listing associated with that seller. Use the seller's email to retrieve their user ID in our backend. You may find Sequelize's [`findOrCreate`](https://sequelize.org/docs/v6/core-concepts/model-querying-finders/#findorcreate) class method helpful for finding a user with a given email, or creating a new user if no user exists with that email.
2. Add logic in `handleClick` in the `Listing` component to `loginWithRedirect` a user if they are not yet authenticated. This will allow users to view items without authentication, but force them to authenticate before buying an item.
   1. Retrieve the logged-in user's email with the `user` object property returned by `useAuth0` hook and send that email as the buyer's email to our backend in `handleClick`.
   2. Update the relevant route controller in our backend listingController to find or create the buyer user in our backend before updating the listing with the specified buyer's ID. Use the buyer's email to retrieve their user ID in our backend. You may find Sequelize's [`findOrCreate`](https://sequelize.org/docs/v6/core-concepts/model-querying-finders/#findorcreate) class method helpful.

Even though our users have logged in before selling or buying, our API server will still return 401 errors on sell or buy requests because we have not yet updated our requests to include auth information.

We are now ready to update our "sell" and "buy" API calls to include our auth tokens!

### Send auth tokens from frontend

We will follow the following official Auth0 API-calling guide for React apps.

{% embed url="<https://auth0.com/docs/quickstart/spa/react/02-calling-an-api>" %}
Official Auth0 API-calling guide for React apps
{% endembed %}

#### Setup frontend to retrieve auth tokens

1. Add `audience` and `scope` props to `Auth0Provider` Component in `index.jsx` in our frontend. The `audience` value should be the API identifier that we used to initialise `checkJwt` in our backend above. This may be different from the `audience` value in the Auth0 docs, because the Auth0 docs reference the Auth0 management API, not our custom API for this app.

#### Update sell and buy functionality on frontend to retrieve and send access token with API requests

1. Retrieve `getAccessTokenSilently` function from `useAuth0` hook in `NewListingForm` and `Listing` components to send the access token in sell and buy API requests respectively
2. In `NewListingForm` component, in the `handleSubmit` function that runs on form submit, call `getAccessTokenSilently` to retrieve the access token, before passing the access token in a request header to the API call with Axios. See [this tutorial](https://masteringjs.io/tutorials/axios/post-headers) and Axios docs on request method [aliases](https://axios-http.com/docs/api_intro) and [configs](https://axios-http.com/docs/req_config) for how to set request headers in Axios requests.
   1. The `audience` parameter value should be the API identifier that we used to initialise `checkJwt` in our backend above.
   2. We will need to pass the access token as an Authorization header whose value starts with "Bearer ", followed by the access token. Sample code below.
3. Do the same on `handleClick` for buy functionality in the `Listing` component. When the user clicks "buy", we should get the access token and send it with the API request to buy the listing.

Sample code from Rocket's `handleSubmit` function in `NewListingForm` component.

{% code title="NewListingForm.jsx" %}

```javascript
// Retrieve access token
const accessToken = await getAccessTokenSilently({
  // TODO: Replace with your own app's audience. Should be same as API identifier in above steps.
  audience: "https://carousell/api",
});

// Send request to create new listing in backend
const response = await axios.post(
  `${BACKEND_URL}/listings`,
  {
    title,
    category,
    condition,
    price,
    description,
    shippingDetails,
    // User is currently logged-in user
    sellerEmail: user.email,
  },
  {
    headers: {
      Authorization: `Bearer ${accessToken}`,
    },
  }
);
```

{% endcode %}

#### Verify backend can authenticate with access tokens

1. Run backend and frontend servers locally
2. List a new item on our frontend and verify that the listing gets created with the seller ID of the current user
3. Buy the newly-created listing on our frontend and verify that the listing now shows the buyer ID of the current user

Congratulations! We now have an app that allows us to buy and sell items with authenticated users!

## Submission

Submit pull requests to the `main` branches of Rocket's Carousell Frontend and Carousell Backend repos respectively, and share your PR links in your section Slack channel.

If you would like to deploy, follow deployment instructions in Bigfoot SQL M-M.

## Reference Solution

Here is reference code for the [frontend](https://github.com/rocketacademy/carousell-frontend-3.2/tree/solution-auth-base) and the [backend](https://github.com/rocketacademy/carousell-backend-bootcamp/pull/1/files) for this exercise. You can do better!
