
Route to services based on authentication method
You want to deploy a single endpoint that automatically forwards web and API traffic to the right services based on whether requests use OAuth or JWTs, which guarantees both your web- and API-based services are protected.
To route traffic based on the AuthN method, you can use ngrok’s Cloud Endpoints, internal Agent Endpoints, and Traffic Policy system to match traffic based on the user agent (whether or not it contains the string Mozilla
), which then applies the appropriate AuthN method, verifies access, and forwards requests to the appropriate upstream service.
How to use this recipe
1. Create two internal agent endpoints
On a server for your webapp, create an internal agent endpoint.
ngrok http 8080 --url https://web-service.internal
On a server for your API service, create an internal agent endpoint.
ngrok http 8081 --url https://api-service.internal
2. Reserve a domain
Navigate to the Domains section of the ngrok dashboard and click New + to reserve a domain like https://your-api.ngrok.app
, which we’ll refer to $YOUR_NGROK_DOMAIN
from here on out.
3. Create a cloud endpoint
Navigate to the Endpoints section of the ngrok dashboard, then click New + and Cloud Endpoint to create a new cloud endpoint on $YOUR_NGROK_DOMAIN
.
4. Apply Traffic Policy to your cloud endpoint
Apply this chain of Traffic Policy rules to your public cloud endpoint.
You’ll need to change:
$YOUR_JWT_DOMAIN
: The domain name for your tenant at your JWT provider—for example, with Auth0, it looks something likehttps://example.us.auth0
.$YOUR_NGROK_DOMAIN
: The domain name you reserved in step 2.
on_http_request:
- expressions:
# Check if the client's user agent does not contain 'Mozilla', which means they are an API user
- "!(req.user_agent.contains('Mozilla'))"
actions:
# If true, apply the `jwt-validation` action
- type: "jwt-validation"
config:
issuer:
allow_list:
- value: "https://$YOUR_JWT_DOMAIN"
http:
tokens:
- type: "jwt"
method: "header"
name: "Authorization"
prefix: "Bearer "
jws:
allowed_algorithms:
- "RS256"
keys:
sources:
additional_jkus:
- "https://$YOUR_JWT_DOMAIN/.well-known/jwks.json"
- type: "forward-internal"
config:
url: https://api-service.internal
- expressions:
# Check if the client's user agent contains 'Mozilla,' which means they are a browser user
- "req.user_agent.contains('Mozilla')"
actions:
# If true, apply the oauth action
- type: oauth
config:
provider: google
- expressions:
# Check again whether the client's user agent contains 'Mozilla,' then also check whether the email they used in the OAuth flow matches <YOUR_DOMAIN>, and if both are `true`, then forward to your
- "req.user_agent.contains('Mozilla')"
- "actions.ngrok.oauth.identity != null && actions.ngrok.oauth.identity.email.endsWith('$YOUR_DOMAIN')"
actions:
- type: "forward-internal"
config:
url: https://web-service.internal
- expressions:
# In all other situations, use `custom-response` to respond with a generaic unauthorized error
- type: "custom-response"
config:
content: "Your email ${actions.ngrok.oauth.identity.email} is not allowed!"
status_code: 400
What you get from this recipe
If ngrok identifies a request as coming from curl
or a machine-to-machine method, it automatically applies the jwt-validation
action and forwards to an internal endpoint for an API service.
If the request comes from a web browser, it applies the oauth
action and forwards authenticated users to a different internal agent endpoint.
In both cases, you support multiple authentication methods out of the box, apply them appropriately based on the type of request, and don't force your users to remember specific paths to reach the service they're looking for.
What's next?
- Read about dynamic routing with CEL expressions to expand this template to any number of services.
- Add rate limiting to the beginning of this chain of rules to prevent abuse.