ZuploZuplo
LoginStart for Free
  • Documentation
  • API Reference

Require User Claims Policy

Authorize requests by validating claims on the authenticated user against a configurable rule — allowlists (in), exact values (eq), and prefixes (startsWith), combined with and/or.

Run this policy after any authentication policy (for example Open ID JWT Auth or one of its provider-specific variants): the authentication policy proves who the caller is, and this policy decides what they may call — for example, pinning a route to specific service accounts, OAuth clients, tenants, or groups without writing custom code.

Configuration

The configuration shows how to configure the policy in the 'policies.json' document.

Code
{ "name": "my-require-user-claims-inbound-policy", "policyType": "require-user-claims-inbound", "handler": { "export": "RequireUserClaimsInboundPolicy", "module": "$import(@zuplo/runtime)", "options": { "rule": { "or": [ { "claim": "sub", "in": [ "ci-deployer@my-project.iam.gserviceaccount.com", "112233445566778899000" ] }, { "claim": "email", "eq": "ci-deployer@my-project.iam.gserviceaccount.com" } ] } } } }

Policy Configuration

  • name <string> - The name of your policy instance. This is used as a reference in your routes.
  • policyType <string> - The identifier of the policy. This is used by the Zuplo UI. Value should be require-user-claims-inbound.
  • handler.export <string> - The name of the exported type. Value should be RequireUserClaimsInboundPolicy.
  • handler.module <string> - The module containing the policy. Value should be $import(@zuplo/runtime).
  • handler.options <object> - The options for this policy. See Policy Options below.

Policy Options

The options for this policy are specified below. All properties are optional unless specifically marked as required.

  • rule (required) <undefined> - The authorization rule evaluated against the authenticated user's claims (request.user.data, populated by the authentication policy that runs before this one — a JWT policy, API key auth, mTLS, etc.). A rule is either a single claim check (eq, in, or startsWith) or an and/or combination of nested rules. Combinators may nest up to three levels deep. Requests that fail the rule receive a 403 Forbidden response.

Using the Policy

Overview

The require-user-claims-inbound policy authorizes requests by evaluating a rule against the claims of the authenticated user (request.user). It separates authentication from authorization: an authentication policy — a JWT policy such as open-id-jwt-auth-inbound, API key auth, mTLS, or any other — proves who the caller is; this policy then decides what they may call.

Typical uses:

  • Pin a route to specific caller identities — service accounts, OAuth clients, or workload identities — by allowlisting the sub and/or email claims.
  • Require an exact value on any claim, including nested claims (for example an AWS STS token's lambda_source_function_arn under the https://sts.amazonaws.com/ namespace).
  • Match prefixes with startsWith, for example AWS assumed-role subjects like arn:aws:sts::123456789012:assumed-role/my-role/<session> where the session name varies.
  • Require membership in a group or role carried in an array-valued claim.

Configuration

Code
{ "name": "require-known-agents", "policyType": "require-user-claims-inbound", "handler": { "module": "$import(@zuplo/runtime)", "export": "RequireUserClaimsInboundPolicy", "options": { "rule": { "or": [ { "claim": "sub", "in": [ "ci-deployer@my-project.iam.gserviceaccount.com", "112233445566778899000" ] }, { "claim": "email", "eq": "ci-deployer@my-project.iam.gserviceaccount.com" } ] } } } }

This policy must run after an authentication policy that populates request.user. If no authenticated user is present on the request, the policy returns 401 Unauthorized. If the rule evaluates to false, it returns 403 Forbidden.

Rules

A rule is either a single claim check or an and/or combination of nested rules (and/or may nest up to three levels deep; a claim check may appear at any level):

  • { "and": [rule, ...] } — every nested rule must pass.
  • { "or": [rule, ...] } — at least one nested rule must pass.
  • { "claim": <claim>, "eq": value } — the claim equals the value.
  • { "claim": <claim>, "in": [value, ...] } — the claim equals any listed value.
  • { "claim": <claim>, "startsWith": "prefix" } — the claim is a string starting with the prefix.

Each claim check uses exactly one operator. To combine operators on the same claim, use and/or with one check per operator.

Selecting claims

Claims are read from the authenticated user's data (request.user.data), which is set by whichever authentication policy ran before this one. For a JWT auth policy this is the full decoded token payload; for API key auth it is the key's metadata.

claim is a light JSON-path selector:

  • Dot notation addresses top-level and nested claims — email, address.country.
  • Bracket notation with quotes addresses claim names that contain dots or slashes (namespaced claims), which dot notation cannot express — ['https://example.com/roles'], or nested: ['https://sts.amazonaws.com/']['lambda_source_function_arn']. Single or double quotes both work; single quotes avoid backslash-escaping when the selector is written in a JSON policies.json. The two notations may be mixed, e.g. realm_access['some.key'].
  • sub falls back to request.user.sub when the user data has no sub property, so subject checks also work with authentication policies that only set the subject (for example API key authentication).

Evaluation semantics

Every check fails closed:

  • A missing claim never matches.
  • Object-valued claims never match.
  • Comparisons are strict and type-sensitive: the string "42" does not equal the number 42. Note that Google-issued tokens carry sub as a string of digits, and $env(...) substitution always produces strings — quote numeric values accordingly.
  • startsWith only matches string claim values, and an empty prefix is rejected as a configuration error.
  • When a claim value is an array (for example groups or roles), a check passes if any element satisfies the operator.

Denied responses never echo claim values or expected values. The failing claim checks and the caller's sub are written to the request log instead.

Related policies

  • Open ID JWT Auth — verifies token signature, issuer, and audience, and populates request.user.
  • JWT Scope Validation — requires that the space-delimited scope claim contains all configured scopes; use it for OAuth scope enforcement.

Read more about how policies work

Edit this page
Last modified on July 10, 2026
On this page
  • Configuration
    • Policy Configuration
    • Policy Options
  • Using the Policy
  • Overview
  • Configuration
  • Rules
    • Selecting claims
    • Evaluation semantics
  • Related policies
JSON
JSON