Skip to main content

Check permissions by URL with regex URL mapping

Check whether a user can call an HTTP URL by matching the URL against a regular expression (regex) mapping rule. This page is for developers who enforce permissions at the API layer and have URLs that a simple URL template can't describe, such as optional segments, varying subdomains, or query parameters in any order.

How regex URL mapping works

A regex mapping rule links an HTTP method and a regex URL pattern to a Permit resource and action. When the policy decision point (PDP) receives a URL check:

  1. The PDP finds the rules with the same HTTP method (case-insensitive) whose pattern matches the request URL, starting from the first character.
  2. When several rules match, the PDP uses the rule with the highest priority.
  3. The PDP passes the values of the pattern's named capture groups to the check as resource attributes.
  4. The PDP checks whether the user can perform the rule's action on the rule's resource in the given tenant, and returns the decision.

Use regex rules to extract several values from paths and query strings, to match optional components, subdomains, or both http and https, and to cover many URL shapes with one rule.

Named capture groups pass URL values to your policy

The PDP evaluates patterns with Python regex syntax. A named capture group, (?P<param_name>pattern), captures part of the URL. The PDP passes each captured value as a resource attribute named param_name, so attribute-based access control (ABAC) rules in your policy can use the value.

Prerequisites

  • A Permit.io policy with the resources and actions your URLs map to
  • Your environment API key, and your project and environment IDs or keys (Get your API key)
  • Docker, to run a container PDP version 0.8.0 or later

1. Create a regex mapping rule with the API

Create regex mapping rules with the proxy configs endpoint of the Permit API. Replace {project_id} and {env_id} with your project and environment, and YOUR_PERMIT_KEY with your API key. This request creates a rule that maps POST requests for a user profile URL to the update action on the users resource:

curl --location 'https://api.permit.io/v2/facts/{project_id}/{env_id}/proxy_configs' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer YOUR_PERMIT_KEY' \
--data '{
"name": "Users API",
"key": "users_api",
"mapping_rules": [
{
"url": "^https://api\\.example\\.com/api/v1/users/(?P<user_id>[0-9]+)/profile$",
"http_method": "post",
"resource": "users",
"action": "update",
"url_type": "regex"
}
],
"secret":"abc123notUsed"
}'

In this rule:

  • url_type is "regex". Without url_type, the PDP treats url as a simple URL template.
  • (?P<user_id>[0-9]+) captures a numeric user ID from the path. The PDP passes the value as the user_id resource attribute.
  • secret is required by the proxy configs API. The PDP's URL check doesn't use the secret value.
Escape backslashes in the JSON body

The pattern is a JSON string, so every regex backslash needs a second backslash: \. becomes \\.. A single backslash before . is an invalid JSON escape, and the API rejects the request body.

2. Run the PDP

URL checks run on a container PDP. Start one with your environment API key:

docker run -it \
-p 7766:7000 \
--env PDP_API_KEY=permit_key_{your_permit_env_key} \
--env PDP_DEBUG=True \
permitio/pdp-v2:latest

Regex URL mapping requires PDP version 0.8.0 or later.

3. Check a URL

Send the user, the HTTP method, the full request URL, and the tenant to the PDP's POST /allowed_url endpoint. This example assumes the PDP runs locally on port 7766. The url must match the rule's pattern. For the rule in step 1, a matching URL is https://api.example.com/api/v1/users/123/profile.

curl --location 'http://localhost:7766/allowed_url' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer permit_key_{your_key}' \
--data '{
"user": {
"key": "raz@permit.io"
},
"url": "https://api.example.com/api/v1/users/123/profile",
"http_method": "post",
"tenant": "default"
}'

In Java, call permit.checkUrl(user, httpMethod, url, tenant) instead. The other SDKs don't have a URL check function, so call the /allowed_url endpoint over HTTP.

Verify the URL check result

The response contains an allow field:

  • "allow": true means a rule matched and the user can perform the mapped action on the mapped resource.
  • "allow": false with a debug.reason of Matched mapping rule not found for the requested URL and HTTP method means no pattern matches the URL and method. Test the pattern against the full URL, including scheme and host, and compare the HTTP method.
  • "allow": false without that reason means a rule matched, and the policy denies the action.

Common regex patterns

Each of the following patterns is JSON-escaped, ready for the url field of a mapping rule. The tables explain the unescaped pattern.

1

Match a numeric path parameter

//JSON Escaped Regex URL
"url": "^https://api\\.example\\.com/users/(?P<user_id>[0-9]+)/profile$"
Matches

This pattern matches URLs with a numeric user ID in the path:

https://api.example.com/users/123/profile

https://api.example.com/users/456/profile

Pattern PartDescription
^Matches the start of the string
https://Matches the protocol literally
api\.example\.comMatches the domain (dots are escaped with backslashes)
/users/Matches the literal path segment
(?P<user_id>[0-9]+)Named capture group:
- ?P<user_id>: Names the captured value "user_id"
- [0-9]+: Matches one or more digits
/profileMatches the literal path segment
$Matches the end of the string

Use this pattern for REST endpoints with a numeric ID in a fixed path.

2

Match an optional path segment

//JSON Escaped Regex URL
"url": "^https://api\\.example\\.com/api/users(?:/(?P<user_id>[0-9]+))?$"
Matches

This pattern matches URLs with or without a user ID:

https://api.example.com/api/users

https://api.example.com/api/users/123

Pattern PartDescription
^Matches the start of the string
https://Matches the protocol literally
api\.example\.comMatches the domain (dots are escaped with backslashes)
/api/usersMatches the literal path segments
(?:...)Non-capturing group (used for grouping without capturing)
(?P<user_id>[0-9]+)Named capture group:
- ?P<user_id>: Names the captured value "user_id"
- [0-9]+: Matches one or more digits
?Makes the entire group optional
$Matches the end of the string

Use this pattern when one rule covers a list endpoint and its detail endpoint.

3

Match query parameters in any order

//JSON Escaped Regex URL
"url": "^https://api\\.example\\.com/search\\?(?=.*q=(?P<query>[^&]+))(?=.*page=(?P<page>[0-9]+)).*$"
Matches

This pattern matches URLs with both query parameters, in any order:

https://api.example.com/search?q=test&page=1

https://api.example.com/search?page=2&q=example

Pattern PartDescription
^Matches the start of the string
https://Matches the protocol literally
api\.example\.comMatches the domain (dots are escaped with backslashes)
/searchMatches the literal path segment
\?Matches the literal question mark (escaped)
(?=...)Positive lookahead (ensures something exists without consuming it)
.*Matches any characters
[^&]+Matches one or more characters that aren't ampersands (for query values)
$Matches the end of the string

Use this pattern for search endpoints where clients send parameters in any order. Each lookahead captures one parameter as an attribute: query and page.

4

Match any subdomain

//JSON Escaped Regex URL
"url": "^https://[\\w-]+\\.example\\.com/api/v1/users/(?P<user_id>[0-9]+)$"
Matches

This pattern matches URLs on any subdomain:

https://api.example.com/api/v1/users/123

https://staging.example.com/api/v1/users/456

https://dev-api.example.com/api/v1/users/789

Pattern PartDescription
^Matches the start of the string
https://Matches the protocol literally
[\w-]+Matches one or more word characters or hyphens:
- \w: matches letters, numbers, and underscores
- -: allows hyphens in subdomains
\.example\.comMatches the domain (dots are escaped with backslashes)
/api/v1/users/Matches the literal path segments
(?P<user_id>[0-9]+)Named capture group:
- ?P<user_id>: Names the captured value "user_id"
- [0-9]+: Matches one or more digits
$Matches the end of the string

Use this pattern when the same API runs on several subdomains, such as staging and dev.

5

Match a user ID anywhere in the path

//JSON Escaped Regex URL
"url": "^https?:\\/\\/[^\\/]+(?:.*?\\/users(?:\\/(?P<user_id>[^\\/]+))?(?:\\/.*)?)?$"
Matches

This pattern matches any URL with a users segment, and captures the segment after users as user_id when present. It matches:

https://api.example.com/users/123

https://api.example.com/v1/users/123

https://api.example.com/v1/users/123/profile

https://api.example.com/v1/users/123/settings/notifications

Pattern PartDescription
^Matches the start of the string
https?Matches "http" or "https" (the 's' is optional)
:\/\/Matches the literal "://" (forward slashes are escaped)
[^\/]+Matches the domain (any characters except forward slashes)
.*?Non-greedy match of any characters
(?:\/.*)?Optional trailing path segments
$Matches the end of the string

A broad pattern like this one can match URLs you didn't intend. Give narrower rules a higher priority so the PDP picks them first.

6

Match HTTP and HTTPS

//JSON Escaped Regex URL
"url": "^https?:\\/\\/api\\.example\\.com\\/users\\/(?P<user_id>[0-9]+)\\/profile$"
Matches

This pattern matches both HTTP and HTTPS URLs:

http://api.example.com/users/123/profile

https://api.example.com/users/123/profile

Pattern PartDescription
^Matches the start of the string
https?Matches "http" or "https" (the 's' is optional)
:\/\/Matches the literal "://" (forward slashes are escaped)
api\.example\.comMatches the domain (dots are escaped)
/users/Matches the literal path segment
(?P<user_id>[0-9]+)Named capture group:
- ?P<user_id>: Names the captured value "user_id"
- [0-9]+: Matches one or more digits
/profileMatches the literal path segment
$Matches the end of the string

Use this pattern when your API accepts both http and https, for example in development.

7

Match any domain

//JSON Escaped Regex URL
"url": "^https:\\/\\/[^\\/]+\\/api\\/v1\\/users\\/(?P<user_id>[0-9]+)\\/profile$"
Matches

This pattern matches any domain name:

https://api.example.com/api/v1/users/123/profile

https://api.staging.example.com/api/v1/users/123/profile

https://api.production.example.com/api/v1/users/123/profile

Pattern PartDescription
^Matches the start of the string
https:\/\/Matches the literal "https://" (forward slashes are escaped)
[^\/]+Matches the domain (any characters except forward slashes)
/api/v1/Matches the literal API version path
/users/Matches the literal path segment
(?P<user_id>[0-9]+)Named capture group:
- ?P<user_id>: Names the captured value "user_id"
- [0-9]+: Matches one or more digits
/profileMatches the literal path segment
$Matches the end of the string

Use this pattern when the same API runs on several domains, so one rule applies on every domain.

Best practices for regex patterns

  1. Escape special characters. Escape dots in domains (\.), and escape each backslash again in the JSON body.
  2. Anchor patterns. Start with ^ and end with $. The PDP matches from the start of the URL, and without $ a pattern also matches longer URLs.
  3. Use named groups. Only named capture groups become resource attributes.
  4. Test patterns before production. Test each pattern against real request URLs, including URLs that must not match.
  5. Keep patterns narrow. Narrow patterns are easier to read and less likely to match unintended URLs.

Next steps