> ## Documentation Index
> Fetch the complete documentation index at: https://docs-dev-fix-docs-5546-update-db-search.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> The Node.js implementation of the API for the Mobile + API architecture scenario

# Node.js API Implementation (Mobile Apps + API)

export const AuthCodeBlock = ({filename, icon, language, highlight, children}) => {
  const [displayText, setDisplayText] = useState(children);
  const [copyText, setCopyText] = useState(children);
  const wrapperRef = React.useRef(null);
  useEffect(() => {
    let unsubscribe = null;
    function init() {
      if (!window.autorun || !window.rootStore) {
        return;
      }
      unsubscribe = window.autorun(() => {
        let processedChildrenForDisplay = children;
        let processedChildrenForCopy = children;
        for (const [key, value] of window.rootStore.variableStore.values.entries()) {
          const escapedKey = key.replaceAll(/[.*+?^${}()|[\]\\]/g, (String.raw)`\$&`);
          let displayValue = value;
          if (key === "{yourClientSecret}" && value !== "{yourClientSecret}") {
            displayValue = value.substring(0, 3) + "*****MASKED*****";
          }
          processedChildrenForDisplay = processedChildrenForDisplay.replaceAll(new RegExp(escapedKey, "g"), displayValue);
          processedChildrenForCopy = processedChildrenForCopy.replaceAll(new RegExp(escapedKey, "g"), value);
        }
        setDisplayText(processedChildrenForDisplay);
        setCopyText(processedChildrenForCopy);
      });
    }
    if (window.rootStore) {
      init();
    } else {
      window.addEventListener("adu:storeReady", init);
    }
    return () => {
      window.removeEventListener("adu:storeReady", init);
      unsubscribe?.();
    };
  }, [children]);
  useEffect(() => {
    if (!wrapperRef.current) return;
    const originalWriteText = navigator.clipboard.writeText.bind(navigator.clipboard);
    let isOverriding = false;
    const handleClick = e => {
      const button = e.target.closest('[data-testid="copy-code-button"]');
      if (!button || !wrapperRef.current.contains(button)) return;
      isOverriding = true;
      navigator.clipboard.writeText = text => {
        if (isOverriding) {
          isOverriding = false;
          navigator.clipboard.writeText = originalWriteText;
          return originalWriteText(copyText);
        }
        return originalWriteText(text);
      };
      setTimeout(() => {
        if (isOverriding) {
          isOverriding = false;
          navigator.clipboard.writeText = originalWriteText;
        }
      }, 100);
    };
    const wrapper = wrapperRef.current;
    wrapper.addEventListener('click', handleClick, true);
    return () => {
      wrapper.removeEventListener('click', handleClick, true);
      if (navigator.clipboard.writeText !== originalWriteText) {
        navigator.clipboard.writeText = originalWriteText;
      }
    };
  }, [copyText]);
  return <div ref={wrapperRef}>
      <CodeBlock filename={filename} icon={icon} language={language} lines highlight={highlight}>
        {displayText}
      </CodeBlock>
    </div>;
};

export const codeExample = `// set dependencies - code omitted

// Enable CORS - code omitted

// Create middleware for checking the JWT
const checkJwt = jwt({
  // Dynamically provide a signing key based on the kid in the header and the signing keys provided by the JWKS endpoint
  secret: jwksRsa.expressJwtSecret({
    cache: true,
    rateLimit: true,
    jwksRequestsPerMinute: 5,
    jwksUri: \`https://{yourDomain}/.well-known/jwks.json\`
  }),

  // Validate the audience and the issuer
  audience: '{YOUR_API_IDENTIFIER}', //replace with your API's audience, available at Dashboard > APIs
  issuer: 'https://{yourDomain}/',
  algorithms: [ 'RS256' ]
});

// Enable the use of request body parsing middleware - code omitted

// create timesheets API endpoint - code omitted
app.post('/timesheets', checkJwt, function(req, res){
  var timesheet = req.body;

  // Save the timesheet to the database...

  //send the response
  res.status(201).send(timesheet);
});
// launch the API Server at localhost:8080 - code omitted`;

This document is part of the Mobile + API Architecture Scenario and it explains how to implement the API in Node.js. The full source code for the Node.js API implementation can be found in [this GitHub repository](https://github.com/auth0-samples/auth0-pnp-exampleco-timesheets/tree/master/timesheets-api/node).

Please refer to the scenario for information on the implemented solution.

<AccordionGroup>
  <Accordion title="1 Define the API endpoints">
    This implementation uses the [Express web application framework](http://expressjs.com/) to build a Node.js API.

    ##### Create a package.json file

    Create a folder for your API, navigate into it, and run `npm init`. This sets up your `package.json` file.

    Leave the default settings or change them as you see fit.

    Our sample's `package.json` looks like the following:

    ```json lines theme={null}
    {
      "name": "timesheets-api",
      "version": "1.0.0",
      "description": "API used to add timesheet entries for employees and contractors",
      "main": "index.js",
      "scripts": {
        "start": "node index.js",
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "dependencies": {
        "body-parser": "^1.20.0",
        "cors": "^2.8.5",
        "express": "^4.18.0",
        "express-oauth2-jwt-bearer": "^1.6.0"
      },
      "author": "Auth0",
      "license": "MIT"
    }
    ```

    ##### Install the dependencies

    Next, set the dependencies with the following modules:

    * **express**: This module adds the [Express web application framework](https://expressjs.com/).
    * **cors**: This module adds support for enabling [CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing), which is required since the API is called from a Single-Page Application that runs on a different domain inside a web browser.
    * **jwks-rsa**: This library retrieves RSA signing keys from a JWKS (JSON Web Key Set) endpoint. Using `expressJwtSecret`, we can generate a secret provider that provides the right signing key to `express-jwt` based on the `kid` in the JWT header. To learn more, refer to the [node-jwks-rsa GitHub repository](https://github.com/auth0/node-jwks-rsa).
    * **express-jwt**: This module authenticates HTTP requests using JWT tokens in your Node.js applications. It provides several functions that make working with JWTs easier. For more information, refer to the [express-jwt GitHub repository](https://github.com/auth0/express-jwt).
    * **body-parser**: This is a Node.js body parsing middleware. It extracts the entire body portion of an incoming request stream and exposes it on `req.body` as something easier with which to interface.

    To install these dependencies run the following:

    ```bash wrap lines theme={null}
    npm install express cors express-jwt jwks-rsa body-parser express-jwt-authz --save
    ```

    ##### Implement the endpoints

    Navigate to your API directory and create a `server.js` file. Your code needs to:

    * Get the dependencies.
    * Implement the endpoint(s).
    * Launch the API server.

    This is our sample implementation:

    ```javascript lines theme={null}
    const express = require('express');
    const app = express();
    const { expressjwt: jwt } = require('express-jwt');
    const jwksRsa = require('jwks-rsa');
    const cors = require('cors');
    const bodyParser = require('body-parser');

    // Enable CORS
    app.use(cors());

    // Enable the use of request body parsing middleware
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({
      extended: true
    }));

    // Create timesheets API endpoint
    app.post('/timesheets', function(req, res){
      res.status(201).send({message: "This is the POST /timesheets endpoint"});
    })

    // Launch the API Server at localhost:8080
    app.listen(8080);
    ```

    Launch your API server using `node server` and make an HTTP POST request to `localhost:8080/timesheets`. You should see a JSON response with the message `This is the POST /timesheets endpoint`.

    So now we have our endpoint but anyone can call it. Continue to the next step to see how we can fix this.
  </Accordion>

  <Accordion title="2 Secure the API endpoints">
    In order to validate our token, use the `jwt` function, provided by the [express-jwt middleware](https://github.com/auth0/express-jwt#usage), and the `jwks-rsa` to retrieve our secret. The libraries do the following:

    1. `express-jwt` decodes the token and pass the request, the header, and the payload to `jwksRsa.expressJwtSecret`.
    2. `jwks-rsa` downloads all signing keys from the JWKS endpoint and see if a one of the signing keys matches the `kid` in the header of the JWT. If none of the signing keys match the incoming `kid`, an error will be thrown. If we have a match, pass the right signing key to `express-jwt`.
    3. `express-jwt` continues its own logic to validate the signature of the token, the expiration, `audience` and the `issuer`.

    The steps we will follow in our code are:

    * Create the middleware function to validate the access token.
    * Enable the use of the middleware in our routes.

    You can also write some code to actually save the timesheet to a database. This is our sample implementation (some code is omitted for brevity):

    <AuthCodeBlock children={codeExample} language="javascript" />

    If we launch our server now and do an HTTP POST to `localhost:8080/timesheets` we should get the error message `Missing or invalid token` (which is accurate since we didn’t send an access token in our request).

    In order to test the working scenario as well we need to:

    * Get an access token. For details on how to do so refer to: [Get an Access Token](/docs/secure/tokens/access-tokens/get-access-tokens).
    * Invoke the API while adding an `Authorization` header to our request with the value `Bearer ACCESS_TOKEN` (where `ACCESS_TOKEN` is the value of the token we retrieved in the first step).
  </Accordion>

  <Accordion title="3 Check the app permissions">
    In this step, we add the ability to check if the application has permissions (or scopes) and use our endpoint in order to create a timesheet. In particular, we want to ensure that the token has the correct scope, which is `batch:upload`.

    In order to do this, we make use of the `express-jwt-authz` Node.js package, so add that to your project:

    ```bash lines theme={null}
    npm install express-jwt-authz --save
    ```

    Now add a call to `jwtAuthz(...)` to your middleware to ensure that the JWT contain a particular scope in order to execute a particular endpoint.

    We add an additional dependency. The **express-jwt-authz** library, which is used in conjunction with express-jwt, validates the [JWT](/docs/secure/tokens/json-web-tokens) and ensures it bears the correct permissions to call the desired endpoint. For more information, refer to the [express-jwt-authz GitHub repository](https://github.com/auth0/express-jwt-authz).

    This is our sample implementation (some code is omitted for brevity):

    ```javascript lines theme={null}
    // set dependencies - some code omitted
    const jwtAuthz = require('express-jwt-authz');

    // Enable CORS - code omitted

    // Create middleware for checking the JWT - code omitted

    // Enable the use of request body parsing middleware - code omitted

    // create timesheets API endpoint
    app.post('/timesheets', checkJwt, jwtAuthz(['create:timesheets'], { customUserKey: 'auth' }), function(req, res){
      var timesheet = req.body;

      // Save the timesheet to the database...

      //send the response
      res.status(201).send(timesheet);
    })

    // launch the API Server at localhost:8080 - code omitted
    ```

    If we invoke our API with a token that does not include this scope, then we should get the error message Forbidden with the HTTP status code `403`. You can test this by removing this scope from your API.
  </Accordion>

  <Accordion title="4 Determine User Identity">
    The `express-jwt` middleware that is used to validate the JWT also sets `req.user` with the information contained in the JWT. If you want to use the `sub` claim to identify the user uniquely, you can use `req.user.sub`. For the timesheets application, we want to use the email address of the user as a unique identifier.

    ##### Create an Action

    First, [create a new Action](/docs/customize/actions/write-your-first-action) that will add the email address of the user to the access token.

    1. Navigate to [Auth0 Dashboard > Actions > Library](https://manage.auth0.com/#/actions/library), and select **Build Custom**.
    2. Enter a descriptive **Name** for your Action (for example, `Add email to access token`), select the **Login / Post Login** trigger, and select **Create**.
    3. Locate the Actions Code Editor, copy the following JavaScript code into it, and select **Save Draft** to save your changes:

       ```text lines theme={null}
       exports.onExecutePostLogin = async (event, api) => {
         const namespace = 'https://my-app.example.com';
         api.accessToken.setCustomClaim(`${namespace}/email`, event.user.email);
       }
       ```

           <Callout icon="file-lines" color="#0EA5E9" iconType="regular">
             The `namespace` is used to ensure the claim has a unique name that does not clash with standard OIDC claims or internal services. To learn more about restrictions and guidelines with namespaced and non-namespaced claims, read [Create Custom Claims](/docs/secure/tokens/json-web-tokens/create-custom-claims).
           </Callout>
    4. From the Actions Code Editor sidebar, select Test (play icon), then select **Run** to [test your code](/docs/customize/actions/test-actions).
    5. When you’re ready for the Action to go live, select **Deploy**.

    ##### Add your Action to the Post-Login Trigger

    Next, add the Action you created to the [Post-Login Trigger](https://manage.auth0.com/#/actions/triggers/post-login/). To learn how to attach Actions to Triggers, read [Write Your First Action](/docs/customize/actions/write-your-first-action).

    ##### Retrieve the unique identifier

    Finally, from inside your API, retrieve the value of the claim from `req.auth`. Use that value as the unique user identifier to associate with timesheet entries.

    ```js lines theme={null}
    app.get('/timesheets', checkJwt, jwtAuthz(['read:timesheets'], { customUserKey: 'auth' }), function(req, res) {
      var timesheet = req.body;

      // Associate the timesheet entry with the current user
      var userId = req.auth['https://api.exampleco.com/email'];
      timesheet.user_id = userId;

      // Save the timesheet to the database...

      //send the response
      res.status(201).send(timesheet);
    });
    ```
  </Accordion>
</AccordionGroup>
