Skip to content
Ashish.
All posts
Diagram illustrating the structure of authorization_details in RFC 9396.
6 min readDevelopmentBackend DevelopersFeatured#oauth 2.0#rfc 9396#authorization#security#api design#fine grained access#identity

RFC 9396: Structured Granularity in OAuth 2.0

An examination of RFC 9396, detailing how Rich Authorization Requests and authorization_details enable fine-grained authorization in OAuth 2.0.

By Ashish KumarPart 6 of OAuth 2.0 RFCs Every Engineer Should Read

This is Part 6 of the "OAuth 2.0 RFCs Every Engineer Should Read" series.

For years, OAuth 2.0 has suffered from a structural limitation known as scope bloat. In the traditional model, a client requests a space-separated list of scopes (e.g., profile email phone). The Authorization Server (AS) either grants all of them or none. This binary approach forces developers to request excessive permissions to ensure functionality, violating the principle of least privilege. RFC 9396, titled "Rich Authorization Requests," addresses this by introducing the authorization_details parameter. This mechanism allows for precise, fine-grained authorization requests that map directly to specific resource actions, rather than broad data categories.

The Failure of Flat Scopes

To understand the mechanism of rich authorization requests, we must first examine the failure mode of standard OAuth 2.0. In a typical flow, a client application requests access to a user's profile. The client sends a request with scope=profile. The AS grants a token with that scope. However, "profile" is semantically ambiguous. Does it include the user's email? Their phone number? Their home address?

In practice, providers often interpret profile loosely, or clients request profile email to be safe. This creates a trust deficit. The user sees "Access your profile and email" and assumes they are granting limited access, but the token actually permits broad reading capabilities. Furthermore, if the API needs to read the email but not the phone number, the current model offers no way to express that distinction in the initial request. The client must either over-request or make multiple sequential requests, both of which are inefficient and insecure.

Introducing authorization_details

RFC 9396 introduces a new parameter: authorization_details. This parameter is an array of JSON objects that describe the specific authorizations the client is requesting. Unlike the flat string list of scopes, authorization_details provides a structured schema for defining permissions, enabling fine grained authorization that was previously impossible with standard scopes.

Each object in the array typically contains three key fields:

  1. type: A string identifying the type of authorization (e.g., account, payment, contact).
  2. actions: A list of strings specifying the allowed operations (e.g., read, write, delete).
  3. locations: A list of strings indicating where the actions can be performed (e.g., local, remote, specific resource identifiers).

Consider a banking API. Instead of requesting scope=banking, a client can request:

{
  "authorization_details": [
    {
      "type": "account",
      "actions": ["read"],
      "locations": ["https://api.bank.com/accounts"]
    },
    {
      "type": "payment",
      "actions": ["initiate"],
      "locations": ["https://api.bank.com/payments"]
    }
  ]
}

This structure explicitly tells the Authorization Server that the client only needs to read account details and initiate payments. It cannot delete accounts or view transaction history unless those actions and locations are explicitly included. The AS can then issue a token that is strictly bound to these parameters.

Mechanism: Policy Enforcement

The power of authorization_details lies not just in the request, but in how the Authorization Server processes it. RFC 9396 does not define a single universal schema for these details. Instead, it delegates the definition of valid types, actions, and locations to "RAR Profiles." These profiles are defined in separate specifications (such as IETF drafts or vendor-specific documentation) and are referenced by the client or AS.

When the AS receives the authorization_details array, it performs two critical checks:

  1. Syntax Validation: It ensures the JSON is well-formed and adheres to the schema defined by the relevant RAR Profile.
  2. Policy Evaluation: It evaluates whether the requested actions and locations are permitted under the current security policy. For example, a third-party app might be allowed to read account balances but never write transactions. The AS uses the detailed information in authorization_details to make this nuanced decision, rather than relying on a coarse scope bucket. As noted in the RFC specification, this evaluation ensures that the resulting token reflects only the authorized subset of permissions.

If the AS approves the request, the resulting access token can contain an authorization_details claim that mirrors the approved request. This claim is crucial for downstream Resource Servers (RS), alongside standard JWT claims like sub or iss.

Downstream Enforcement at the Resource Server

The true benefit of RFC 9396 is realized at the Resource Server. In traditional OAuth, the RS receives a token and checks if it contains the scope profile. It then has to manually parse the user's data to determine if the specific field (e.g., email) is present. This logic is often duplicated across multiple endpoints and prone to error.

With RFC 9396, the RS receives the token with the authorization_details claim. The RS can now enforce fine-grained access control directly against this claim. For example, a GET /accounts endpoint can verify that the token's authorization_details includes type: account and action: read. A POST /payments endpoint can verify that the token includes type: payment and action: initiate.

This eliminates the need for complex, ad-hoc permission checks. The token itself carries the structured policy. If a client attempts to access a resource without the corresponding authorization_details claim, the RS can reject the request immediately, providing a clear error message rather than a generic "403 Forbidden."

Integration with Resource Indicators

RFC 9396 is designed to work alongside RFC 8707 (Resource Indicators). While authorization_details defines what actions are allowed, RFC 8707 defines which resource server should process the request. This separation of concerns is vital in federated environments. A client might request access to data hosted on multiple servers. The authorization_details specify the permissions, while the resource parameter in the token request specifies the target server. The AS can then issue separate tokens for each resource, each containing only the authorization_details relevant to that specific server. This integration allows RFC 9396 to function as a robust oauth 2.0 extension for complex, multi-resource architectures.

Tradeoffs and Adoption

Implementing RFC 9396 requires significant changes to both Authorization Servers and Resource Servers. The AS must support parsing and validating the authorization_details JSON structure. The RS must be updated to interpret these scope values and enforce policies based on them. Additionally, developers must adopt a new mental model, moving from broad scope buckets to explicit action-location pairs.

However, the long-term benefits outweigh the initial complexity. By reducing the trust surface area, RFC 9396 minimizes the impact of compromised tokens. If a token is stolen, the attacker can only perform the specific actions listed in the authorization_details claim, not the broad set of permissions associated with a legacy scope. This aligns OAuth 2.0 with modern security practices like zero-trust architecture, where every access request is explicitly validated against minimal required permissions, as supported by NIST guidelines on digital identity.

Conclusion

RFC 9396 transforms OAuth 2.0 from a coarse-grained permission system into a fine-grained authorization framework. By introducing the authorization_details parameter, it allows clients to request precise permissions and enables servers to enforce them with surgical accuracy. For backend developers and identity engineers, adopting rich authorization requests is not just about following a new RFC; it is about embracing a more secure, scalable model for API access control. Implementing fine grained authorization reduces risk and improves clarity in permission management. As the ecosystem matures, oauth 2.0 extension profiles will likely become the standard for defining permissions in complex, multi-tenant environments.

Related posts