Recipe

Route to services based on authentication method

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 like https://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?