---
title: "Ensure Users Log in from Trusted Networks with Auth0 Actions"
description: "A quickly implemented Auth0 Action can enhance security by requiring users to log in from trusted IP addresses or blocks."
authors:
  - name: "Joey deVilla"
    url: "https://auth0.com/blog/authors/joey-devilla/"
date: "Nov 30, 2023"
category: "Developers,Tutorial,Actions"
tags: ["actions", "security"]
url: "https://auth0.com/blog/ensure-users-log-in-trusted-networks-auth0-actions/"
---

# Ensure Users Log in from Trusted Networks with Auth0 Actions

One of the most straightforward ways to secure your applications is to limit the IP addresses from which users can log in. This approach reduces your attack surface by allowing you to restrict access to a region, a subnet, a VPN, or even only to devices accessing your on-premises network. It may help your application meet compliance and data protection requirements.

You could build this functionality into your application, but with Auth0’s Actions, you don’t have to. Just as Auth0 saves you from the surprisingly big task of implementing authentication and authorization, Auth0 Actions can save you from doing a lot of work to customize your application’s login flow. With a few lines of JavaScript, you can ensure that users log in only from IP addresses you trust.


## What are Auth0’s Actions?

[Actions](https://auth0.com/docs/customize/actions) are serverless functions hosted by Auth0 that activate in response to specific events during authentication workflows, such as when the user logs in, signs up for an account, or updates their password. You can write your own custom Actions in Node.js (complete with support for NPM Modules) or use one of the many third-party [Actions in the Auth0 Marketplace](https://marketplace.auth0.com/features/actions). 

Once you’ve written or acquired an Action, insert it into the workflow you want to modify using a drag-and-drop editor. This system makes it simple to see what the workflow does and allows administrators who don’t code to change it without requiring developer intervention.

All you need to see how Auth0 Actions can help secure your application is an Auth0 account. If you don’t already have an account, <a href="https://a0.to/blog_signup" 
  data-amp-replace="CLIENT_ID" 
  data-amp-addparams="anonId=CLIENT_ID(cid-scope-cookie-fallback-name)">you can sign up for a free one</a>. 


## Denying Login Requests with Auth0 Actions and the Login Flow

An Action that allows only users from a set of given IP addresses or address blocks to log in relies on two key features:

1. The _Login_ flow
2. The `api.access.deny()` method

The Login flow is the authentication workflow that takes place immediately after the user logs in and before Auth0 issues an ID token. You add Actions to the Login flow to modify its behavior, as shown below:

![Auth0 Actions drag-and-drop editor showing Login Flow with an action titled “Allow login only for on-prem users”](https://images.ctfassets.net/23aumh6u8s0i/5Z2ziFneBeIl8sFu6S7HPF/c2ddc5c9133af97c32bbb7b28b04a19e/actions_drag_and_drop_editor.png)

The `api.access.deny()` method prevents the user from logging in, even though they have already logged in successfully. It allows an Action in the Login flow to apply additional requirements before granting the user access to the application. In this Action, the requirement will be that the user must log in from an approved IP address.

Here’s the syntax for calling `api.access.deny()`:

```
api.access.deny(reason)
```

It expects a single parameter: `reason`, a string value containing the *user-facing* message explaining why their login request was denied.

> For more details about using Actions to allow or deny login requests, see this article: [_Permit or Deny Login Requests Using Auth0 Actions_](https://auth0.com/blog/permit-or-deny-login-requests-using-auth0-actions/).


## Limiting Login to Trusted IP Addresses

Let’s start with an Action that allows the user to log in if they’re doing so from a given IP address or a small number of them (maybe two or three dozen at most). 

### Create the Action

The first step is to log into the Auth0 Dashboard for your tenant and select _Actions_ from the left sidebar menu, followed by _Flows_. This will take you to the _Flows_ page, where you should select the “Login” flow:

![Auth0 "Flows" page, showing instructions to select the "Login" flow.](https://images.ctfassets.net/23aumh6u8s0i/42NRWh03aZ3fJlAfTaRt7v/54da543f5cffc428f804a2902b7b54c2/select_the_login_flow.png)

Once you’re on the _Login_ page, create a new custom Action by clicking on the _+_ button in the _Add Action_ section and then selecting the _Build Custom_ item in the menu that appears:

![Auth0 "Login" flow page, showing instructions to click the "+" button and click the "Build Custom" link.](https://images.ctfassets.net/23aumh6u8s0i/6BgXrFlm3YLL0W1lvBcFSz/0b6c80a11bf19c4844468e9277acf4b9/click_build_custom.png)

The _Create Action_ dialog box will appear. Enter a name for the new Action — I used  `Trusted IP addresses only` — and leave the _Trigger_ and _Runtime_ items at their default settings, _Login / Post Login_ and _Node 18 (Recommended)_, then click the _Create_ button:

![Auth0 "Create Action" dialog box, with the user definition the Action's name as "Trusted IP addresses only".](https://images.ctfassets.net/23aumh6u8s0i/3R7h86vUTONadxQD7WjuBM/d40525e2bbf28015e015101293322895/create_action_dialog.png)

This will create a new Action, and you’ll see this code editor:

![Auth0 Actions code editor displaying the code for the "Trusted IP addresses only" Action.](https://images.ctfassets.net/23aumh6u8s0i/1JldbBgGnNjOKHjoYFC5If/fd135c7b57a01e6a9e65808ec55a33b4/trusted_ip_addresses_only_-_new.png)

Actions can use these two JavaScript functions to affect the Login flow:

* `onExecutePostLogin`: Specifies what should happen immediately after a successful user login and before the access token is issued. This function has the following parameters:
	* `event`: Contains details about the login request, such as information about the user and the login context.
	* `api`: Contains methods to change the login’s behavior.
* `onContinuePostLogin`: Defines what should happen when control returns to the Login Action after a redirect to another web page. It’s used only when the user returns after being redirected, which is why it’s commented out by default.

In this example, we’re only using `onExecutePostLogin`. Update the code in the editor to the following:

```javascript
exports.onExecutePostLogin = async (event, api) => {
    const allowedIPs = [
        '42.42.42.42', // Replace this with your IP address
        // Other trusted IP addresses go here
    ] 

    if (!allowedIPs.includes(event.request.ip)) {
        api.access.deny('Please log in from a trusted IP address.')
    }
}
```

The code above declares `allowedIPs`, an array containing one or more IP addresses in string form. It checks to see if the user’s IP address, contained in the `event.request.ip` property, is in `allowedIPs`. If so, the Action allows the user to continue logging in. If the user’s IP address doesn’t match one of the IP addresses in `allowedIPs`, the Action denies the user’s login request.

Replace the placeholder IP address in `allowedIPs`, `'42.42.42.42'`, with your computer’s IP address. Add any other IP addresses you want to allow users to log in from the array.

After updating the code, click the _Deploy_ button to save your changes and deploy the Action:

![Auth0 Actions code editor displaying the code for the "Trusted IP addresses only" Action, with instructions to click the "Deploy" button.](https://images.ctfassets.net/23aumh6u8s0i/39LPNd028X2vEhAe7cIAXb/170f29e53b0a86a33ec586d541201d20/click_the_deploy_button.png)

### Add the Action to the Login flow

Now that you have created the _Trusted IP addresses only_ Action, you can add it to the Login flow. Return to the Login flow page by selecting _Actions_ → _Flows_ from the left sidebar menu, and then clicking the _Login_ button on the _Flows_ page:

![Auth0 "Login" flow page, with instructions to select "Custom" and to drag the "Trusted IP addresses only" Action.](https://images.ctfassets.net/23aumh6u8s0i/1ERvzclfEuEmRYnzEGsYOF/57aeb77899a71e3ee5b1ee04b3973490/drag_trusted_ip_addresses_only_action_1.png)

In the _Add Action_ section on the right side of the page, click _Custom_. This will display the list of custom Actions that you created for the Login flow. Drag the _Trusted IP addresses only_ Action to the Login flow diagram and drop it between _Start_ and _Complete_.

![Auth0 "Login" flow page, with instructions to drop the "Trusted IP addresses only" Action into the flow between "Start" and "Complete" and to click the "Apply" button.](https://images.ctfassets.net/23aumh6u8s0i/5YXUR0P0YRIz2yDLnYXObl/d6bf3e83a4c121f416d7d5c4f99818d9/drag_trusted_ip_addresses_only_action_2.png)

You must apply the change you just made to the Login flow before leaving the page. Do this by clicking the _Apply_ button near the upper right corner of the page.

### Test the Action

Now that the _Trusted IP addresses only_ Action is part of the Login flow, it’s time to test it.

The quickest way to test your tenant’s Login flow is to use the login box testing facility built into the Dashboard. You’ll find it on the _Getting Started_ page, which you access by clicking _Getting Started_ in the left sidebar menu. About halfway down the page, you’ll see the _Try your Login box_ section, with a link labeled _Try it out_. Click that link to try out your new login flow:

![Auth0 Dashboard’s "Getting Started" page, with instructions to click the "Getting Started" link, followed by the "Try it out" link.](https://images.ctfassets.net/23aumh6u8s0i/7v0eybEzBHdiym92m0od43/c24de9937d1866c5f80d3c0d975a6ca3/getting_started_-_try_it_out.png)

You’ll see a login box if you’re already not logged in as one of your tenant’s users; if this is the case, log in.

If the IP address of your computer is one of the IP addresses in your Action’s `allowedIPs` array, you should see the _It Works!_ page:

![The "It Works!" page, with the JSON containing user details, blurred out.](https://images.ctfassets.net/23aumh6u8s0i/4RGyLYssTcXymTRAEqWlGJ/84b31250aa1c619fd7841c0f6aef335e/it_works.png)

To test the case where a user is trying to log in from an IP address that isn’t trusted, edit the Action so that the `allowedIPs` array doesn’t contain your computer’s IP address. Repeat the _Try your Login box_ process. You should see this page now:

![The "Bummer! Something failed" page showing the "access_denied" error and the "Please log in from a trusted IP address." error description.](https://images.ctfassets.net/23aumh6u8s0i/5wOe5Dlk5VmxdzKBiupfV6/90e12555976da12a80ea8314b3303ac0/bummer_something_failed.png)

Note that the `"error_description"` property of the returned JSON contains the string `Please log in from a trusted IP address.` — the parameter passed to the `api.access.deny()` method call in the Action.


## Limiting Login to Trusted IP Blocks

The _Trusted IP addresses only_ action works well if you want users to log in only from a manageable number of addresses — perhaps two or three dozen at most. What if you wanted to allow logins from a larger set of addresses, such as `42.42.42.x`, `42.42.x.y`, or even `42.x.y.z`, where `x`, `y` and `z` could theoretically be any number in the range of 0 through 255?

Let’s create an Action that determines if the user is logging in from a given IP address block.

### Create the Action

Once again, in the Auth0 Dashboard for your tenant, return to the Login flow page — select _Actions_ from the left sidebar menu, followed by _Flows_, and then select the “Login” flow. Create a new custom Action by clicking on the _+_ button in the _Add Action_ section and then selecting the _Build Custom_ item in the menu that appears:

![Auth0 "Login" flow page, showing instructions to click the "+" button and click the "Build Custom" link.](https://images.ctfassets.net/23aumh6u8s0i/6NyRiYWBge8vxbMW2ROthL/0a479a3a3177e62fc6e2f3e4019994aa/add_another_action.png)

You’ll see the _Create Action_ dialog box will appear. Enter a name for this Action — I used `Trusted IP blocks only` — and leave the Trigger and Runtime items at their default settings, Login / Post Login and Node 18 (Recommended), then click the _Create_ button:

![Auth0 "Create Action" dialog box, with the user definition the action's name as "Trusted IP blocks only".](https://images.ctfassets.net/23aumh6u8s0i/5WeJMjfb9RYyuhqzhVc9s2/61b175ff672b848ef34b2b12131630b2/create_action_dialog_-_trusted_ip_blocks_only.png)

### Add the `netmask` dependency

The “secret sauce” for this Action is the `Netmask` class from Node’s `netmask` module. We can define IP address blocks using `Netmask` instances and then use its `contains()` method to see if a given IP address is part of that block.

Fortunately, you can add Node dependencies to your Actions with a couple of clicks. The first of these clicks is on the _Dependencies_ button — it’s the “package” icon on the left side of the Action code editor:

![Auth0 Actions code editor displaying the code for the "Trusted IP block only" Action, with instructions to click the "Dependencies" button.](https://images.ctfassets.net/23aumh6u8s0i/6H6oSOuoOBCFcWpkxnHJtv/e542c105d51545097a15848f0e150e4d/trusted_ip_blocks_only_-_click_to_add_dependencies.png)

The _Dependencies_ pane will appear on the left side of the code editor.

![Auth0 Actions code editor displaying the code for the "Trusted IP block only" Action, with the "Dependencies" pane now displayed.](https://images.ctfassets.net/23aumh6u8s0i/7sSmiAerD4migvawscL6RW/fad9b6374763bad3d3029d319cd58bd7/trusted_ip_blocks_only_-_new_add_dependencies.png)

The _Dependencies_ pane will appear on the left side of the code editor. Click the _Add Dependency_ button:

![The "Add Dependency" dialog box, with "netmask" entered into the "Name" field.](https://images.ctfassets.net/23aumh6u8s0i/7uNRnI5WWfwFo8zkkoOYUs/0b7a0d0f7c1cfbcc778bd008898cd729/trusted_subnets_only_-_add_netmask_dialog.png)

In the _Add Dependency_ dialog box that appears, enter `netmask` into the _Name_ field. Leave the contents of the _Version_ field alone (you want the default value, `latest`) and click the _Create_ button:

![Auth0 Actions code editor displaying the code for the "Trusted IP block only" Action, with the "Dependencies" pane showing that the "netmask" module is now installed.](https://images.ctfassets.net/23aumh6u8s0i/6Svp9dlMuiuGPvD1cJgE9K/6033a9d379ba0bc35a96ee7928db5d25/trusted_ip_blocks_only_-_netmask_added.png)

The `netmask` module is now available for the Action to use. Let’s do that!

### Code the action

In the editor, update the Action’s code to the following:

```javascript
exports.onExecutePostLogin = async (event, api) => {
    const Netmask = require('netmask').Netmask
    const trustedIPBlocks = [
        new Netmask('42.42.42.0/24'), // Allows IP addresses in 42.42.42.*
        // Other trusted IP blocks go here
    ] 

    for (const trustedIPBlock of trustedIPBlocks) {
        if (trustedIPBlock.contains(event.request.ip)) {
            break
        }
        api.access.deny('Please log in from a trusted IP address.')
    }
}
```

Just as we defined a collection of trusted IP addresses in the _Trusted IP addresses only_ Action, we’re defining a collection of trusted blocks of IP addresses in an array named `trustedIPBlocks `.

The `trustedIPBlocks` array in the code above contains only one block defined by the string `’42.42.42.42/24’`. This specifies the block in [CIDR (Classless Inter-Domain Routing)](https://aws.amazon.com/what-is/cidr/) notation, where:

* The part before the `/` character, `42.42.42.42`, represents the IPv4 address `42.42.42.42`, and
* The part after the `/` character, `24`, represents the number of `1` bits in the [subnet mask](https://www.freecodecamp.org/news/subnet-mask-definition/) `255.255.255.0`, a 32-bit number where the leftmost 24 bits are `1`s and the remaining bits are `0`s.

The `42.42.42.42/24` block contains the addresses `42.42.42.0` through `42.42.42.255`.

Changing the number of bits in the subnet mask can expand or contract the block. For example, `42.42.42.42/16` contains the addresses `42.42.0.0` through `42.42.255.255`.

Replace the placeholder IP block in `trustedIPBlocks`, `'42.42.42.42/24'`, with the string `’{your IP address}/24’`, where `{your IP address}` is your computer’s IP address, then click the _Deploy_ button to save your changes.

![Auth0 Actions code editor displaying the code for the "Trusted IP blocks only" Action, with instructions to click the "Deploy" button.](https://images.ctfassets.net/23aumh6u8s0i/1j1EDs9RSFbbnMxytQU3nM/262ed0c84dcb600d24a0f1d28a3c366c/click_deploy_button_2.png)


### Add the Action to the Login flow

While the _Trusted IP blocks only_ and _Trusted IP addresses only_ Actions can coexist in the same Login flow, let’s remove the latter so we can test the former. Remove the _Trusted IP addresses only_ Action by returning to the Login flow page, selecting the Action, and clicking the _Remove_ button:

![Auth0 "Login" flow page, with instructions to select the "Trusted IP addresses only" Action, followed by the "Remove" button.](https://images.ctfassets.net/23aumh6u8s0i/3ThxiGHZqOocwU9KI5N9a9/8057f5e7c0209dacafb8320feb58a42a/remove_trusted_ip_addresses_only.png)

The Login flow should be empty. Display the list of custom Actions by clicking _Custom_ in the _Add Action_ section…

![Auth0 "Login" flow page, with instructions to click "Custom" and drag the "Trusted IP blocks only" Action.](https://images.ctfassets.net/23aumh6u8s0i/2uVtd4fMbdXCqGsukkM5th/024df72275f6bebda874fcaa5ec1386a/drag_trusted_ip_blocks_only_action_1.png)

...then drag and drop the _Trusted IP blocks only_ Action into the flow. Finalize this change by clicking the _Apply_ button near the upper right corner of the page:

![Auth0 "Login" flow page, with instructions to drop the "Trusted IP blocks only" Action into the flow between "Start" and "Complete" and to click the "Apply" button.](https://images.ctfassets.net/23aumh6u8s0i/3XuBesW9XcbZR5AOZwoTGW/3d050ac27bf1f4ae31f2f1202e60d2d6/drag_trusted_ip_blocks_only_action_2.png)

### Test the Action

It’s time to test the _Trusted IP blocks only_ Action in the same way we tested _Trusted IP addresses only_ .

Once again, navigate to the _Getting Started_ page and click the _Try it out_ link. If the IP address of your computer is in one of the IP blocks defined in your Action’s `trustedIPBlocks` array, you should see the _It Works!_ page:

![The "It Works!" page, with the JSON containing user details, blurred out.](https://images.ctfassets.net/23aumh6u8s0i/4RGyLYssTcXymTRAEqWlGJ/84b31250aa1c619fd7841c0f6aef335e/it_works.png)

You should also test the case where the user is logging in from an IP address outside the trusted blocks. Edit the Action so that the `trustedIPBlocks` array contains a single block that doesn’t contain your computer’s IP address. Repeat the _Try your Login box_ process. You should see this page now:

![The "Bummer! Something failed" page showing the "access_denied" error and the "Please log in from a trusted IP address." error description.](https://images.ctfassets.net/23aumh6u8s0i/5wOe5Dlk5VmxdzKBiupfV6/90e12555976da12a80ea8314b3303ac0/bummer_something_failed.png)

Note that the `"error_description"` property of the returned JSON contains the string `'Please log in from a trusted IP address.'` — the parameter passed to the `api.access.deny()` method call in the Action.

## Summary 

You have just seen a couple of examples of how you can use Auth0 Actions to enhance the security of your applications by limiting the IP addresses that users can log in from. Both examples in this article used the Login flow and the `api.access.deny()` method to achieve these goals:

1. Allowing only users from specific IP addresses to log in
2. Allowing only users whose IP addresses are in specific IP blocks to log in

In building an Action to allow only users from trusted IP blocks to log in, this article covered adding Node packages as dependencies for Actions. This feature greatly expands the power and possibilities of Actions and simplifies their implementation.

This article also covered the basics of using the Auth0 Dashboard’s _Try it out_ feature to confirm that the Actions worked as expected. Make sure you use it as you develop your own actions.

The examples provided above are a good starting point. Experiment and customize Actions to meet your needs. For example, the code above used _allow lists_. With only a little change, you could build Actions based on deny lists to block users from known “troublemaker” IP addresses.

Happy coding!