| Internet-Draft | Rego Policy in OAuth | March 2026 |
| Liu, et al. | Expires 18 September 2026 | [Page] |
This specification defines how to use the Rego policy language in OAuth 2.0 authorization flows using Rich Authorization Requests (RAR). It defines the rego_policy authorization data type for carrying policy proposals in authorization_details, enabling fine-grained, dynamic authorization decisions that go beyond traditional scope-based access control.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 18 September 2026.¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Revised BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Revised BSD License.¶
Traditional OAuth 2.0 authorization relies on static scopes to define access permissions. While scopes work well for coarse-grained access control, they are insufficient for scenarios requiring dynamic, context-aware authorization decisions—particularly in AI agent systems where the permitted operations may depend on runtime conditions such as transaction amounts, time windows, or resource attributes.¶
This specification extends OAuth 2.0 to support the Rego policy language, as used by Open Policy Agent (OPA). Rego enables expressive, declarative policies that can encode complex authorization rules. By integrating Rego into OAuth flows, this specification enables:¶
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals.¶
This specification is designed to work with Rich Authorization Requests (RAR) [RFC9396] as the primary mechanism for carrying policy proposals. The rego_policy authorization data type MAY be used with various OAuth 2.0 authorization flows, including but not limited to:¶
Different authorization flows may have different security considerations when using policy proposals. Implementations SHOULD carefully evaluate the security implications discussed in Section 7 based on their specific deployment scenario.¶
This specification defines the rego_policy
authorization data type for use with Rich Authorization Requests (RAR)
[RFC9396]. The policy proposal is carried within the
authorization_details parameter as the primary mechanism.¶
Key aspects of the RAR integration:¶
rego_policy type is registered in the RAR
Authorization Data Types registry (Section 9.4 of RFC 9396).
It contains the policy content, entry point, and optional context.¶
policy_ref claim referencing the registered policy.
Per RFC 9396 Section 7.1, the token response MAY include
enriched authorization information such as the full policy content.¶
Implementations SHOULD ensure consistency between
authorization_details requirements and Rego policy evaluations.
The Authorization Server MAY use RAR types to determine applicable
policy templates or validation rules.¶
authorization_details parameter.¶
authorization_details parameter.¶
policy_ref
claim.¶
+--------+ +--------+ +--------+ +--------+
| Client | | AS | | RS | | OPA |
+--------+ +--------+ +--------+ +--------+
| | | |
| (1) AuthZ Req | | |
| with | | |
| authorization_ | | |
| details | | |
| (type=rego_ | | |
| policy) | | |
|--------------->| | |
| | | |
| | (2) Validate | |
| | policy syntax | |
| | | |
| | (3) Evaluate | |
| | policy | |
| | | |
| (4) Enriched | | |
| Access Token| | |
| with | | |
| policy_ref | | |
|<---------------| | |
| | | |
| (5) API Request| | |
| with token | | |
|-------------------------------->| |
| | | |
| | | (6) Fetch |
| | | policy via |
| | | policy_ref |
| | |--------------->|
| | | |
| | | (7) Evaluate |
| | | policy |
| | |<---------------|
| | | |
| | | (8) Enforce |
| | | decision |
| | | |
| (9) Response | | |
|<--------------------------------| |
authorization_details containing an object of type
rego_policy, with policy and optional
context fields. This leverages RAR (RFC 9396) for structured
authorization requests.¶
policy_ref claim
referencing the registered policy. Per RFC 9396 Section 7.1, the
token response MAY include enriched authorization information in
additional JSON fields (e.g., policy containing the full
policy content) alongside the access token.¶
policy_ref from AS
(if not cached). The policy may have been returned directly in the
enriched token response.¶
Traditional OAuth error responses indicate authorization failure without providing guidance on how to obtain valid authorization. In AI agent scenarios, where agents may need to autonomously navigate authorization requirements, resource servers can provide structured guidance that enables agents to construct appropriate authorization requests.¶
When an agent's request lacks sufficient authorization, the resource
server returns an HTTP 403 Forbidden response with a JSON body containing
a rego_profile object. This object provides machine-readable
guidance on the required authorization conditions.¶
HTTP/1.1 403 Forbidden
Content-Type: application/json
{
"error": "insufficient_authorization",
"error_description": "Additional authorization required",
"rego_profile": {
"profile_uri": "https://resource.example/policies/purchase",
"required_scope": ["purchase.create"],
"required_claims": ["agent_id", "user_id"],
"constraints": {
"max_amount": {
"type": "number",
"description": "Maximum transaction amount in USD",
"required": true
},
"trigger_source": {
"type": "string",
"enum": ["user_initiated", "scheduled"],
"description": "Source of the operation trigger"
}
},
"confirmation_required": true,
"evidence_required": true,
"auth_server": "https://as.example.com",
"token_endpoint": "https://as.example.com/token"
}
}
The rego_profile object contains the following fields:¶
Upon receiving a reverse-guided authorization response, the AI agent SHOULD:¶
rego_profile to understand authorization requirements.¶
auth_server is trusted before
proceeding.¶
confirmation_required is true, initiate user consent flow.¶
token_endpoint.¶
This adaptive approach enables agents to "learn" authorization requirements dynamically, reducing the need for pre-programmed knowledge of each resource server's policies.¶
Implementations of reverse-guided authorization MUST consider the following security aspects:¶
auth_server specified in the rego_profile is trusted
before submitting authorization requests. Blindly following
redirects could lead to credential theft.¶
Upon receiving a policy_proposal, the AS MUST perform the following validation steps:¶
allow rule.¶
After validation, the AS registers the policy:¶
If policy validation fails, the AS MUST return an error:¶
{
"error": "invalid_request",
"error_description": "Invalid Rego policy: syntax error at line 5"
}
When the RS receives an access token with a policy_ref claim,
it retrieves the policy using one of the following methods:¶
The RS evaluates the policy using OPA:¶
POST /v1/data/agent/allow HTTP/1.1
Host: opa.example.com
Content-Type: application/json
{
"input": {
"user": {
"id": "user_12345",
"tier": "premium"
},
"action": "add_to_cart",
"resource": {
"type": "cart",
"id": "cart_789"
},
"amount": 45.00
}
}
{
"result": true
}
Based on OPA's response:¶
The policy proposal mechanism defined in this specification is designed to be flexible and applicable to various OAuth 2.0 authorization flows. This section provides examples for common use cases beyond PAR.¶
When using traditional authorization code grant without PAR, the policy proposal and policy_context SHOULD be included in a JWT using the "request" parameter as defined in [RFC9101]. This mechanism, known as JWT-Secured Authorization Request (JAR), is specifically designed for redirect-based flows and provides integrity protection while maintaining consistency with the PAR mechanism.¶
GET /authorize? response_type=code& client_id=spiffe://agent.example/agent& request=eyJhbGciOiJSUzI1NiIsImtpZCI6ImFnZW50LWtleSJ9.ewogICJyZXNwb25zZV90eXBlIjogImNvZGUiLAogICJjbGllbnRfaWQiOiAic3BpZmZlOi8vYWdlbnQuZXhhbXBsZS9hZ2VudCIsCiAgInBvbGljeV9wcm9wb3NhbCI6IHsidHlwZSI6ICJyZWdvIiwiY29udGVudCI6ICIuLi4iLCJlbnRyeV9wb2ludCI6ICJhbGxvdyJ9LAogICJwb2xpY3lfY29udGV4dCI6IHsuLi59LAogICJyZWRpcmVjdF91cmkiOiAiaHR0cHM6Ly9hZ2VudC5leGFtcGxlL2NhbGxiYWNrIiwKICAic3RhdGUiOiAieHl6MTIzIn0.signature& state=xyz123 HTTP/1.1 Host: as.example.com
The JWT payload (decoded) contains:¶
{
"response_type": "code",
"client_id": "spiffe://agent.example/agent",
"authorization_details": [
{
"type": "rego_policy",
"policy": {
"type": "rego",
"content": "package agent\\ndefault allow = false\\n\\nallow {\\n input.action == \"read\"\\n}",
"entry_point": "allow"
},
"context": {
"user": {"id": "user_123"},
"agent": {"id": "spiffe://agent.example/agent"}
}
}
],
"redirect_uri": "https://agent.example/callback",
"state": "xyz123"
}
Using the JWT request parameter provides the following benefits:¶
Implementations MUST validate the JWT signature before processing the policy proposal. The client's public key can be obtained through pre-registration or dynamic client registration.¶
In machine-to-machine scenarios using client credentials, the policy proposal can be included in the token request:¶
POST /token HTTP/1.1
Host: as.example.com
Content-Type: application/json
{
"grant_type": "client_credentials",
"client_id": "spiffe://agent.example/agent",
"client_secret": "...",
"authorization_details": [
{
"type": "rego_policy",
"policy": {
"type": "rego",
"content": "...",
"entry_point": "allow"
}
}
]
}
When exchanging tokens, a new policy proposal can be provided to refine or extend the authorized operations:¶
POST /token HTTP/1.1
Host: as.example.com
Content-Type: application/json
{
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
"subject_token": "eyJhbGciOiJSUzI1NiJ9...",
"authorization_details": [
{
"type": "rego_policy",
"policy": {
"type": "rego",
"content": "...",
"entry_point": "allow"
}
}
]
}
The resulting access token will include both the original permissions and the new policy constraints.¶
Different authorization flows have different security characteristics when using RAR with rego_policy:¶
General security requirements for all flows:¶
Rego policies submitted by clients are executable code. The AS MUST:¶
Clients MUST NOT be able to propose policies that exceed their registered permissions. The AS SHOULD maintain policy templates or scope hierarchies to enforce this.¶
The policy_ref in access tokens MUST reference policies stored securely by the AS. Resource Servers MUST fetch policies only from trusted sources.¶
This specification registers the following authorization data type in the "OAuth Authorization Data Types" registry established by RFC 9396:¶
This specification registers the following claims in the "JSON Web Token Claims" registry:¶
package agent
default allow = false
# Allow transactions up to $50
allow {
input.action == "purchase"
input.amount <= 50.0
}
# Allow cart modifications without amount limit
allow {
input.action == "add_to_cart"
}
package agent
default allow = false
# Allow during business hours
allow {
input.action == "submit_order"
time.clock(time.now_ns())[0] >= 9
time.clock(time.now_ns())[0] < 18
}
package agent
default allow = false
# Premium users can access all features
allow {
input.user.tier == "premium"
}
# Standard users limited to basic actions
allow {
input.user.tier == "standard"
input.action == "read"
}
The authors would like to thank Brian Campbell for his valuable feedback and insightful discussions during the development of this specification. His contributions helped shape key design decisions.¶