Internet-Draft Capability URIs and IRIs September 2026
Thierry Expires 24 March 2027 [Page]
Workgroup:
Network Working Group
Internet-Draft:
draft-thierry-cap-uris-00
Published:
Intended Status:
Experimental
Expires:
Author:
P. Thierry
Comonad Dev

Capability URIs and IRIs

Abstract

This specification describes several URI schemes to safely embed capabilities within URIs.

Status of This Memo

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 24 March 2027.

▲

Table of Contents

1. Introduction

1.1. Rationale

The research on access control has shown that between the two major paradigms of access control, access control lists (ACLs) are less expressive and usually open the possibility of confused deputy attacks, which include some the most problematic attack surfaces of the Web, like XSS and CSRF (see ACLs Don't). The ambient authority inherent to ACLs is also often critical in enabling privilege escalation, making one security flaw vastly more damaging.

The other paradigm, capability-based security, combines designation and authority, making it possible to prevent confused deputy attacks and create new patterns of access control, including composable security policies and safe delegation mechanisms. (see Capability Myths Demolished)

HTTP URIs could be used as capabilities, but as HTTP URIs are not treated as sensitive data by most systems, except for their authorization part (but the classical format login:password has been deprecated since, see [RFC3986], section 3.2.1), it opens up the possibility that capabilities would get leaked in various problematic places, including being stored in server logs and shown in browser address bars.

This specification describes URI schemes that make it possible to safely encode capabilities as URIs and IRIs (see [RFC3987]) by combining an existing URI or IRI and some authorization data into a URI or IRI, called capability-URI (for readability, we don't distinguish capability-URIs and capability-IRIs, as they don't have different security properties).

1.2. Architectural benefits

These URIs and IRIs also deliver on the promises of several architectural decisions at the core of the World Wide Web. [webarch] notes that

It is a strength of Web Architecture that links can be made and shared; a user who has found an interesting part of the Web can share this experience just by republishing a URI.

When authority is separated from designation, sharing a link is often not enough. And several platforms only let their users share content among themselves. When the only tool of access control is user identity, it is logical that you can only share with another user. But fined grained capabilities make it possible again to share "an interesting part of the Web".

Capability-URIs are also a better fit than identity-based authorization in REST APIs (see Architectural Styles and the Design of Network-based Software Architectures, especially chapter 5). The hypermedia nature of REST means that a payload may contain state transitions going to a variety of different servers and in the context of identity-based authorization, it is unclear what credentials should be used where and it creates the risk of using some credentials with the wrong server, possibly leaking credentials to an untrusted system.

But if a REST server uses capability-URIs in the state transitions, the client knows with certainy which credentials to use for each request, and each capability-URI can hold the bare minimum of authority needed to trigger just that transition (see Attenuation (Section 4.6)).

Capability-URIS also give the server more control over the trade-off between safety and latency: when the client should be able to trigger a state transition often and/or over some period of time, the server can embed a long-lived authorization in that state transition with only the authority for that transition. It makes the capability easier to reuse by a malicious agent, but with a very limited attack surface. On the other hand, whenever some state transitions require that the client be very strictly verified to be the right one, capabilities could have a very short TTL and the client would need to renew them by requesting again the resource where they appeared.

1.3. Conventions and Terminology

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [BCP14].

1.4. Syntax Notation

This specification uses the Augmented Backus-Naur Form (ABNF) notation of [RFC5234].

1.5. Prior work

The first use of URIs as capabilities was in the Waterken project, with web-keys, which placed an unguessable token in the fragment of an https URI, and YURLs, with the httpy URI scheme, which introduced the Y-property (Section 4.8).

Introducing a new URI scheme proved to be difficult with Web browsers at the time, so the Waterken project ended up using https URIs with the fingerprint for the Y-property embedded in the host.

Recently, several proposals were made about embedding capabilities as URIs with a new URI scheme (see acknoledgements (Section 6)).

While this specification describes new URI schemes, the principle of web-keys remain a useful alternative in use cases where using the new schemes is not possible, and it can interoperate with capability-URIs. Appendix C explains the basic mechanism of web-keys and Section 3 their evolution with capability-URIs.

2. The capability schemes

2.1. Principle of operation

When a capability-URI-aware HTTP agent (like a browser or library) is asked to make an HTTP request to a resource identified by a capability-URI (with parameters like the method or the request body), it extracts from that capability-URI the authorization data and the request target, prepares the HTTP request with the user-given parameters but augments the connection establishment and the request headers based on the authorization data of the capability-URI.

In the following example, it puts the bearer token in the Authorization header with the Bearer authorization scheme [RFC6750]:

http.post("cap-https://bt=AViRTF9ppWiAtdnpVtVHDQ@example.com/foo", body="bar",
  headers={"Content-Type": "text/plain"})

would produce the following HTTP/1.1 [RFC9112] request:

POST /foo HTTP/1.1
Host: example.com
Content-Type: text/plain
Authorization: Bearer AViRTF9ppWiAtdnpVtVHDQ

bar

or the following HTTP/2 [RFC9113] or HTTP/3 [RFC9114] request:

HEADERS
  + END_HEADERS
    :method = POST
    :scheme = cap-https
    :authority = example.com
    :path = /foo
    content-type = text/plain
    authorization = Bearer AViRTF9ppWiAtdnpVtVHDQ

DATA
  + END_STREAM
bar

Each URI scheme corresponds to an existing HTTP URI scheme, extended with rich authorization data. In a target capability-URI, except for the userinfo component which maps to the capability-URI's authorization component, every component has the same syntax and semantics than the corresponding URI scheme (and maps to the same request components, headers and pseudo-headers).

Table 1
Capability URI scheme Corresponding URI scheme
cap-http http [RFC9110] (see Using bearer tokens without TLS about the security considerations)
cap-https https [RFC9110]
cap-ws ws [RFC6455] (see Using bearer tokens without TLS about the security considerations)
cap-wss wss [RFC6455]

As far as the capability-URI-aware agent is concerned, the URI of the designated resource MUST NOT be the embedded HTTP URI, but the capability-URI (the retrieval URI is one source for a base URI).

2.2. Relative URI references

The normal algorithm from [RFC3986] (section 5.2) applies to relative references, which means that except for network-path references with a userinfo component, relative references reuse the same authorization data used to retrieve the resource representation where they appear.

For that reason, the capability schemes include a dedicated syntax to use a relative path with a different token: cap-https:bt=NVW9YpUuEkZ367AyA-3MZw@./bar. Note that when such a relative capability-URI is parsed by a generic URI parser, the whole content after the scheme will match the path-rootless rule of [RFC3986] (and the token won't be parsed by the authority rule).

To make a relative reference with an absolute path, a network-path reference can be used: //D-nX3h-tDq_VtaYQKYYlDw@/baz. If there could be ambiguity whether the base URI is a capability-URI, it is valid for capability-URIs to have an empty host, which creates a URI that is equivalent to a network-path reference, but with the scheme specified: cap-https://bt=D-nX3h-tDq_VtaYQKYYlDw@/baz.

These relative capability-URI, like relative URI references, refer to a target capability-URI.

2.3. Definitions

The four URI schemes are specified by the rules

  • cap-http-uri

  • cap-https-uri

  • cap-ws-uri

  • cap-wss-uri

The corresponding IRIs are specified by the rules

  • cap-http-iri

  • cap-https-iri

  • cap-ws-iri

  • cap-wss-iri

cap-http-uri        = "cap-http" ":" cap-path [ "?" query ] [ "#" fragment ]
cap-https-uri       = "cap-https" ":" cap-path [ "?" query ] [ "#" fragment ]
cap-ws-uri          = "cap-ws" ":" cap-path [ "?" query ]
cap-wss-uri         = "cap-wss" ":" cap-path [ "?" query ]
cap-path            = absolute-cap-path / relative-cap-path
absolute-cap-path   = "//" authorization "@" host [ ":" port ] path-abempty
relative-cap-path   = authorization "@" path-rel
path-rel            = segment-rel "/" path-rootless
authorization       = [ param *( ";" param ) ]
param               = 1*param-char
param-char          = unreserved / pct-encoded / param-delims
param-delims        = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / "=" / ":"
bearer-token-param  = "bt=" token
token               = 1*param-char
token-format-param  = [ "*" ] "tf=" token-format
token-format        = 1*param-char
y-cert-param        = "yc=" y-cert-alg ":" y-cert-hash
y-cert-alg          = 1*unreserved
y-cert-hash         = base32
segment-rel         = "." / ".."
base32              =  "A" / "B" / "C" / "D" / "E" / "F" / "G" / "H" / "I" / "J" / "K"
base32              =/ "L" / "M" / "N" / "O" / "P" / "Q" / "R" / "S" / "T" / "U" / "V"
base32              =/ "W" / "X" / "Y" / "Z" / "2" / "3" / "4" / "5" / "6" / "7"

cap-http-iri        = "cap-http" ":" cap-ipath [ "?" iquery ] [ "#" ifragment ]
cap-https-iri       = "cap-https" ":" cap-ipath [ "?" iquery ] [ "#" ifragment ]
cap-ws-iri          = "cap-ws" ":" cap-ipath [ "?" iquery ]
cap-wss-iri         = "cap-wss" ":" cap-ipath [ "?" iquery ]
cap-ipath           = absolute-cap-ipath / relative-cap-ipath
absolute-cap-ipath  = "//" iauthorization "@" ihost [ ":" port ] ipath-abempty
relative-cap-ipath  = iauthorization "@" ipath-rel
ipath-rel           = isegment-rel "/" ipath-rootless
iauthorization      = [ iparam *( ";" iparam ) ]
iparam              = 1*param-ichar
param-ichar         = iunreserved / pct-encoded / param-delims
bearer-token-iparam = "bt=" itoken
itoken              = 1*param-ichar

Note that the rule param-delims corresponds to the rule sub-delims (see [RFC3986], section 2.2), minus the ";" alternative, plus the ":" alternative.

The definitions of the following rules are adopted from the generic syntax of URIs and IRIs:

host           = <host, see [RFC3986], Section 3.2.2>
port           = <port, see [RFC3986], Section 3.2.3>
path-abempty   = <path-abempty, see [RFC3986], Section 3.3>
path-rootless  = <path-rootless, see [RFC3986], Section 3.3>
unreserved     = <unreserved, see [RFC3986], Section 2.3>
pct-encoded    = <pct-encoded, see [RFC3986], Section 2.1>
query          = <query, see [RFC3986], Section 3.4>
fragment       = <fragment, see [RFC3986], Section 3.5>

ihost          = <ihost, see [RFC3987], Section 2.2>
iport          = <iport, see [RFC3987], Section 2.2>
ipath-abempty  = <ipath-abempty, see [RFC3987], Section 2.2>
ipath-rootless = <ipath-rootless, see [RFC3987], Section 2.2>
iunreserved    = <iunreserved, see [RFC3987], Section 2.2>
iquery         = <iquery, see [RFC3987], Section 2.2>
ifragment      = <ifragment, see [RFC3987], Section 2.2>

If the capability-URI has an absolute-cap-path where the host component is empty or an absolute-cap-ipath where the ihost component is empty, the URI is relative and is valid only if there is a known base URI (see [RFC3986], section 5.1). Resolving this relative capability-URI consists in creating a target capability-URI where every component is taken from the relative capability-URI, except the host, taken from the base URI.

If the capability-URI has a relative-cap-path or relative-cap-ipath, resolving it consists in creating a target capability-URI where every component is taken from the URI produced by resolving the relative reference present in the path-rel or path-irel component, except the authorization or iauthorization component, taken from the authorization or iauthorization component of the relative capability-URI.

2.4. Parameters

The authorization or iauthorization component of the capability-URI is made of a sequence of parameters. This specification defines three such parameters, but a future version can define more parameters, to support different authorization mechanisms, or deprecate exisiting ones.

An application that doesn't implement some parameter MUST NOT make a request for a capability-URI containing that parameter, except if that parameter's handling is optional. If a parameter's handling is optional, it MUST begin with the character *. If a parameter's handling is not optional, it MUST NOT begin with the character *.

2.4.1. Bearer token

When the bearer-token-param (in a URI) or bearer-token-iparam (in an IRI) component is present, the request MUST contain an Authorization header containing the token or itoken component as a bearer token used with the Bearer authorization scheme ([RFC6750]).

When the bearer-token-param or bearer-token-iparam component is absent, the request MUST NOT contain such an Authorization header with an empty bearer token or one created from another source than the capability-URI's parameters. Another parameter MAY add an Authorization header, including one using the Bearer authorization scheme.

Examples:

  • cap-https://bt=7w66swRJlKme6P3mXY3qww@foo.io

  • cap-https://bt=disallow-hacksaw-repaint-unguarded@bar.app/welcome

  • cap-wss://bt=mugol-majup-mupul-tizaj@baz.net/updates

  • cap-https://bt=衛停烈央察燒迅境若印洲刻@quux.com/login

When a capability-IRI contains a bearer token with characters outside US-ASCII, they SHOULD be encoded as UTF-8 for the HTTP header, as the valid characters for itoken encoded in UTF-8 are also valid for the field-vchar rule of HTTP.

2.4.2. Token format

When the token-format-param component is present, it gives the format used for the bearer token.

This specification defines one value for token-format but a future version can define more values or deprecate exisiting ones:

Table 2
token-format Token format
bisc Biscut (see [biscuit])

Example:

  • cap-https://bt=EokBCh8KBmFjdGlvbhgDIggKBggCEgIQeyIJCgcIgAgSAhgAEiQIABIgncUlEtlWvSDNfM3zZF1i7uA0FipKD48IAKga_AlY3E0aQHJGTX-BrG0UwWHFFT231A0JEBtMWVBgKS5pUdMsISTjRH9DKY9PcFhQ2TqczdhGar8D6NCiZIoDeQvrZtkHJgwiIgog4qv2B3AC4VhoLybiDGxobq-POPtW_JxhRlRLjDJY7DM=;*tf=bisc@foo.io

  • cap-https://bt=EokBCh8KBmFjdGlvbhgDIggKBggCEgIQeyIJCgcIgAgSAhgAEiQIABIg32JaSzOOkDZeLRbnfN5qRQ0fPmT7fFaVzQ96zVffuzYaQOyfgWJUTAuJtuIgI8Y8ToW7mGPsQztQQ_WWHIzJMH1B6yCtncRiaBtAn9yYRuZ62JFiDYp8RyRmM3PPdETlwAIiIgog25_3GfCiu_5kjg-dEq05B7WY5IcoNFAo8P8s1XHlAIU=;tf=bisc@foo.io

This parameter's handling can be optional (if it starts with the * character). An application SHOULD make this parameter's handling mandatory if actions like revocation (Section 4.5) and attenuation (Section 4.6) of the capability-URI are not possible without it.

2.4.3. Y-property certificate

This parameter enables the Y-property.

When the y-cert-param component is present, the client MUST check during TLS handshake that in one X.509 certificate in the certificate chain of the server, the DER encoding of the SubjectPublicKeyInfo field (see [RFC3279]), hashed with the algorithm y-cert-alg and encoded in base32 (see [RFC4648], section 6) corresponds to y-cert-hash, or abort the TLS handshake with a bad_certificate alert (see [RFC9846], section 6.2). As the size of the hash is known, the base32 encoding MUST NOT contain padding.

This parameter MUST NOT be present in a cap-http or cap-ws URI, where it wouldn't make sense.

This specification defines one value for y-cert-alg but a future version can define more values or deprecate exisiting ones:

Table 3
y-cert-alg Hashing algorithm
sha256 SHA-256 (see [RFC6234])

This parameter MAY be used without any other, to get the benefits of the Y-property even in a URI that doesn't combine designation and authority.

Examples:

  • cap-https://yc=sha256:OHYQO7NTO76XXDLIHAG2M35GQI4KGA5SSEN4W5YLHMVEEZUTZ6CA@foo.io/public/doc

  • cap-https://yc=sha256:HBRWGMZTG4ZDIN3DHE3TQYLEMQYTOODCGZRWGZDGMIYDAMJZMYFA;bt=NvjXBbwz6YlkI-nYIlhH5w@bar.app/welcome

base32 is used because it is more resilient to transcription, leaving few possible ambiguities between the characters of the alphabet. To account for the possibility of erroneous transcription, the following characters not present in base32's alphabet SHOULD be substituted:

Table 4
invalid characteur substitution
0 O
1 I
8 B

3. Web-caps

While it can somtimes be straightforward to update frontend and backend code to be able to handle new URI schemes, their adoption by browsers is likely to take time. Also, backward compatibility with legacy browsers can be paramount.

This specification describes a way to embed a capability in a legacy https URI, for its use in resource representations handled by a web browser, called web-caps. Web-caps are a direct evolution of web-keys (Appendix C).

A web-cap is a https URI where the fragment is a cap-https URI. This capability-URI MUST NOT contain a host or ihost component, it MUST be a relative capability-URI with a cap-https scheme. The https URI without its fragment is called the trampoline URI, and it is the base URI for the capability-URI.

When the browser makes a request for the trampoline URI, the representation in the server's response SHOULD be a document with client-side code that will extract the capability-URI, resolve it, and replace the document in the browser with the result of requesting the capability-URI. A document that implementents these steps is called a trampoline document.

When the server doesn't respond with a trampoline document, it MUST respond with an HTTP error or with a document showing an error message.

Once loaded, a trampoline document MUST attempt to modify the browser's address bar to remove the fragment as soon as possible, to limit the possibility of an attacker to observe the capability-URI on the user's screen or with malicious client-side code (through the Location interface of the browser).

A trampoline document is a critical piece of the Trusted Computing Base (TCB) for the system using it. As such, a trampoline document SHOULD be as minimal as possible and SHOULD be audited with the most thorough methods available. A trampoline document MUST NOT load any external resources. Any request it makes apart from the request for the capability-URI may compromize the safety of that capability, even in indirect ways, so any stylesheet, image, or executable code MUST be embedded as data in the document, either as regular content (like a <style> or <script> element) or with a data: URL. To minimize the surface attack, a trampoline document MUST be served with the following header:

It is RECOMMENDED that the resource identified by a trampoline URI be effectively immutable. If any security issue is found with a trampoline document, it is called a compromised trampoline document, and any web-cap whose trampoline URI identifies a compromised trampoline document is called a trampoline-compromised web-cap. A compromised trampoline document SHOULD not be served anymore and an application SHOULD NOT generate trampoline-compromised web-caps. Any new web-caps SHOULD be issued with a new trampoline URI that identifies a new, fixed trampoline document. It is RECOMMENDED that any capability that is suspected to be reachable through a trampoline-compromised web-cap be revoked immediately (see Section 4.5).

To limit browsers requesting the trampoline document more than once, it is RECOMMENDED to serve the trampoline document with the following header:

Because the fragment of a web-cap can be reliably identified as a capability-URI, a capability-URI-aware agent given a web-cap SHOULD resolve the capability-URI directly and request only that URI.

Examples:

4. Security Considerations

4.1. Capability-URIs are credentials

While the intent of this specification is for the most sensitive part of a capability-URI to be the userinfo component of the generic URI syntax, any capability-URI MUST be treated as a whole as a credential. As such, the default behavior of an application using capability-URIs SHOULD be to never show a capability-URI unredacted.

Is is NOT RECOMMENDED to put any sensitive authorization data outside of the userinfo component because the URI embedded in the capability-URI is vastly more likely to end up being leaked, first and foremost in application and server logs.

It is NOT RECOMMENDED to rely on other applications to treat the userinfo component as sensitive information and handle it safely. An application using capabilty-URIs SHOULD redact them internally as soon as possible if it doesn't prevent the application from working correctly. When there is a safety trade-off on an application keeping capability-URIs vs. redacting them internally, an application SHOULD present users a choice.

The use of attenuation (Section 4.6) and of revocable (Section 4.5) capabilities with short lifetimes constitutes a second line of defence against leaks.

As with other credentials, in a web service exposing capability-URIs, their sensitive components like the bearer token SHOULD NOT be stored unprotected. For example, as with passwords, tokens can be stored hashed, which enables the service to find the authority associated with an incoming request containing a valid token, but if the stored hashes get leaked, they cannot be used as-is to make authenticated requests (and if they have been properly generated, a brute-force attack has to search through a bigger and more uniform space than with passwords).

4.1.1. Redacted URI scheme

When an application redacts a capability-URI, to prevent any consumer of the redacted URI to confuse it with a valid URI, an application MAY use the redacted: URI scheme. The syntax of the redacted URI is simple: after the scheme goes the redacted URI reference, unescaped, but with any part deemed sensitive substituted.

It is RECOMMENDED to substitute each sensitive portion of the original URI with the percent-encoded control character "substitute" (%1A), as it is highly unlikely to appear in any unredacted URI. An application SHOULD NOT substitute a sensitive portion with the same number of substitution characters as the length of the sensitive portion, but rather with a single reckognizable character or character sequence.

An application displaying a redacted URI SHOULD display a substitution character or character sequence as a block wider than a character.

The syntax of redacted URIs and IRIs is the following:

redacted-uri   = "redacted:" URI-reference
redacted-iri   = "redacted:" IRI-reference

URI-reference  = <URI-reference, see [RFC3986], Section 4.1>
IRI-reference  = <IRI-reference, see [RFC3987], Section 2.2>

Examples:

Table 5
Redacted URI Possible display
redacted:cap-https:%1A@foo.io/quux/1234 cap-https:████@foo.io/quux/1234
redacted:cap-https:bt=%1A;*tf=bisc@bar.net/?search=%1A cap-https:bt=████;*tf=bisc@bar.net/?search=████
redacted:cap-https:%1A@baz.com/%1A cap-https:████@baz.com/████
redacted:data:,John %1A, born %1A John ████, born ████

Note that by using data: URIs, application can use the redacted: URI scheme to mark non-URI data as redacted.

An application providing multiple ways to redact capability-URIs SHOULD provide a default operation where the whole authoritzation component is redacted. When implementing the handling of capability-URIs with respect to an existing API to handle generic URIs, this default operation SHOULD be how capability-URIs are rendered as strings or in serialization, to ensure that code that isn't specifically designed to handle capability-URIs doesn't leak sensitive information:

> foo = parse_URI("cap-https://bt=zk3fv2E5KsjyYdT9I0rzPg@example.com/foo/bar?baz=quux")
> foo.to_string()
"redacted:cap-https://%1A@example.com/foo/bar?baz=quux"

> {target: foo}.to_json()
'{"target":"redacted:cap-https://%1A@example.com/foo/bar?baz=quux"}'

Beware that this breaks the expectation that string parsing/rendering or serialization/deserialization are couples of inverse operations.

4.2. Using bearer tokens without TLS

[RFC6750] mandates the use of TLS [RFC9846] because transmitting bearer tokens on an unprotected channel is deemed too risky in the use case of OAuth. But fine grained capabilities have a wider use case than OAuth and may be used between services where security is guaranteed at a different layer, like IPsec [RFC6071] or when services are communicating on a dedicated virtual network (which is typical in some cloud deployments). For this reason, this specification describes the use of bearer tokens both with and without TLS.

Of course, cap-http and cap-ws URIs SHOULD NOT, in general, be used over an unprotected channel. Doing so would make it possible for an observer anywhere on the path between the client and the server to store and reuse any capability that isn't otherwise limited in its use (including the capability in the request and any capabilities in the response).

There are a few scenarios where using capability-URIs on an unprotected channel would be safe, though. Sending a request to a single-use capability-URI where the server's response isn't private (and especially doesn't contain other capability-URIs) would give an observer almost no surface attack (it would reveal one of the server's routes as well as the shape of some of the tokens used).

4.3. Capabilities must be unforgeable

For capabilities to enforce their security model, an attacker must not be able to create a capability not explicitly given to them.

In an Object-Capability system (as first formalized in Robust Composition: Towards a Unified Approach to Access Control and Concurrency Control), capabilities cannot be created from arbitrary data, they are not a secret sequence of bits. But capability-URIs are just secret sequences of characters, so their safety resides in being unguessable by an attacker.

While this specification doesn't impose any constraint on the nature of the bearer tokens used (beyond being encoded to only use the available characters for their part of the URI), any application creating capability-URIs MUST apply the currently known techniques to generate tokens that are sufficiently difficult to guess, according to the threat model considered.

Among the known techniques typically used to make tokens unguessable, they can be:

  • A number generated within a wide enough range by a cryptographically secure PRNG, sometimes called a Swiss number (in reference to Swiss numbered bank accounts). A Swiss number is usually encoded in base32 or base64. (see Section 4.9 for the use of different encodings)

  • The permissions as data, protected with cryptography: encrypted such that only the destination service can encrypt and decrypt it, and/or signed. Biscuit is a recent example of the latter, and it is capable of offline attenuation (Section 4.6.3).

4.3.1. The limits on confinement

Because capability-URIs are secret sequences of characters, they can be transformed and sliced in such ways that their nature cannot be recognized reliably. For that reason, most access abstractions that limit the transmission of capability-URIs can be defeated if an agent on one side of the abstraction is cooperating with an agent on the other side in order to leak authority.

The ability to guarantee that an agent doesn't have authority to observe or create side effects is called confinement. It is possible within an Object-Capability system even between mutually untrusting agents, but not with capability-URIs.

For example, even if agent Mallet is running inside a membrane (Section 4.5.2), agent Ned has a capability-URI to Mallet wrapped by the membrane, and there is no other channel between them, when Ned invokes the capability-URI, agent Mallet can respond with an encrypted capability-URI and the secret key needed to decrypt it. No membrane can expect to detect every way that this scenario could play out and trying to detect many of them might be too expensive in terms of computation and latency.

Still, to protect services that always give authority through full capability-URIs, a membrane remains a reliable tool to provide safety and flexibility. Such services SHOULD only use capability-URIs with their scheme, and SHOULD NOT rely on relative references, except when the media type is unambiguous about which part of the response should be resolved as a URI (like HTML or JSON-LD). This implies following REST, instead of having clients construct URIs from out-of-band logic and pieces of data.

4.4. POLA

The Principle of Least Authority says that every part of a system should only get the least amount of authority needed to operate correctly, and no amount more. While widely recommended, POLA is rarely put in practice, because in identity-based security frameworks, POLA sits somewhere between impossible and impractical. But with capabilities, POLA can become trivial to follow.

The basic design principles to adhere to POLA are the following:

  1. Every part of the system starts with no authority that can create or observe side-effects (basically, only read immutable files), except:

    • the authority needed to be invoked by its callers, which includes receiving data and capabilities and replying with data and capabilities

    • its "own" authority, that it exposes (e.g. the permissions to read and write to some part of a filesystem for a storage service); that authority is doled out to its callers with the process of attenuation (Section 4.6).

  2. Any authority needed to process an invocation by a caller should come from capabilities contained in the invocation.

Mainstream operating systems make it harder to enforce POLA at the process level. By default, any process can read and write mutable files, listen on network ports and initiate network connections. Still, sandboxing and containerization tools make it possible to remove those ambient authorities.

But at the web service level, authority often comes from authenticated HTTP requests, which makes it easier to start a service with no authority, by giving it no credentials in its configuration. Again, the exception to that rule would be its "own" authority, like some service exposing a cache that has the credentials to connect to a database in its configuration. Appendix A shows how this works in practice in a full scenario starting with the first deployment of a web service.

4.5. Revocation

If a web service just lets you create capabilities and send them out, they would be irrevocable delegations of your authority. The corresponding issue with ACLs is that you can't have an ACL about the ACL, or you would need an ACL about the ACL about the ACL, and so on…

4.5.1. Caretaker

Fortunately, with capabilities, we have several solutions that don't have this issue of infinite recursion. The simplest is the pattern named the "caretaker". If Alice wants to share with Bob a revocable access to Carol, Alice can create a proxy object CarolCaretaker giving it their capability to Carol. A caretaker has two facets, each with a separate capability: a proxy facet that just forwards invocation to the capability given to it, and a revocation facet that, when invoked, removes from its internal data the capability.

The architecture of the caretaker is an interesting illustration of POLA, more specifically of one of its deeper design principles and one of its benefits. The deeper design principle is to avoid having code make a policy decision when that decision could be wrong. Better have the graph of authority support the desired policy by construction. The caretaker doesn't decide that it should act as a proxy or not. When revoked, it cannot act as a proxy anymore. The benefit is that a revoked caretaker cannot be abused, confused or hacked later to exploit the authority initially given to it: it doesn't hold that authority anymore.

In the case of the caretaker, this design principle emerges necessarily from POLA: when the caretaker should stop working, it doesn't need the authority anymore. So it shouldn't keep it. The trick is to look for other cases where a decision could be transformed into a dynamic graph of capabilities that embodies the policy behind the decision. For a concrete method to achieve this kind of design, the invariant that the code shouldn't receive or keep excess authority is a kind a invariant that perfectly lends itself to Edsger Dijkstra's method of "correct by construction".

Appendix B shows an example of creating, sending, using and revoking a caretaker.

4.5.2. Membrane

When the response from requesting a capability-URI may contain further capabilities, a simple caretaker isn't sufficient to safely revoke authority. With just a caretaker, the initial capability would stop being proxied, but the user of the caretaker could continue to use any authority received through the caretaker as capabilities (which would not be proxied).

In such a case, a safer and slightly more complex solution is the membrane (described in Robust Composition: Towards a Unified Approach to Access Control and Concurrency Control, figure 9.3, and in Proxies: design principles for robust object-oriented intercession APIs, section 5). When a membrane sees a capability-URI in the response, it substitutes it with a capability-URI to a caretaker. When the membrane is revoked, all the caretakers it created are revoked as well (but see the limits on confinement (Section 4.3.1)).

4.5.3. Certificate revocation

When using certificate chains as capabilities (see offline attenuation (Section 4.6.3)), they usually provide an automatic membrane: certificates each can have an identifier and the service can keep a list of revoked certificates. Any capability delegated from a revoked capability is also revoked. This is how revocation of Biscuits works.

4.6. Attenuation

Capability attenuation is a process that takes a capability as input and produces a new capabiliy as output, whose authority is a subset of the input capability's authority.

The use of attenuation is another design principle that emerges necessarily from POLA. Most agents start with one or several capabilities with a broad authority. When they delegate authority to other agents or services to act on their behalf, it almost always needs to be vastly narrower, so attenuated capabilities are used.

For example, let's say Alice has a capability with read/write authority to her whole online file storage, and a capability to a photo annotation service. She obviously doesn't want that service to be able to read her private files, about her finances, her health, or her diaries. She doesn't want to lose her valuable photo collection to a bug in the service or a data ransom attack, so she doesn't trust the service with read/write access to the photos.

Alice would then create two attenuated capabilities to her file storage, one with read-only authority over /Photos/Family, where the subset or her photo collection is that she wants annotated, and one with read/write authority over /Photos/Famly_annotations, an empty folder where the service can write the metadata it produces.

4.6.1. Attenuation by default

When a service exposes authority through capability-URIs, like a file storage or a business dashboard, it SHOULD by default expose each element through an attenuated capability. For example, using bearer tokens, requesting a directory listing from a file storage SHOULD result in a list with each file in the directory with a corresponding capability-URI whose token can only be used to access that file. If that file is itself a directory, listing its contents SHOULD also work the same way. When accessed with read-write authority, such a storage service MAY either include two capabilities for each file in a listing, one for read/write authority, the other for read-only authority, or MAY provide a way to get a read-only version of any read/write capability. Of course, in almost all cases, listing a directory through a capability-URI with read-only authority SHOULD only produce other capabilities with read-only authority (there are very few exceptions that would make sense with respect to security).

This way of getting attenuated capabilities has the benefit that it usually matches the users' intention and intuition. Even if I navigate through my business dashboard and share with someone the direct, irrevocable capability-URI to something, it would not be different than if, in a physical office, I gave someone the key to a storage room, telling them to do some task in there, like shredding all files older than 10 years.

Of course, most services that expose such a graph of attenuated capability-URIs SHOULD include features to wrap them in a membrane to enable revocation (Section 4.5).

4.6.2. Attenuation by intermediary

A service may provide access abstractions (like the caretaker) that aren't expressive enough to implement your desired policies. For example, a file storage service may provide a tree of attenuated read/write and read-only capabilities, and the ability to wrap any of them in a membrane, but this is not expressive enough to implement the following policies:

  • The authority to read a specific file, only once

  • The authority to append to a specific file, but not to read or modify the existing contents

  • The authority to create a new writable file, but not to read of modify any existing file in the same directory

  • The authority to walk only once through a tree of directories, read only one file, only once

One solution is to expose another service, that provides the missing access abstrations. In some cases, like the single-use restriction, such a service can be trivially composed with others without knowledge about their API. Such a wrapper needs only to know the target capability-URI and will, only once, forward any request and forward the response back.

Many variants of the single-use wrapper can be designed, including the simplest which forwards the response untouched, one that enforces a truly single-use authority by stripping all capability-URIs from the response, or one where the single-use restriction only applies to the initial capability, but all capabilities in the initial response get wrapped in a membrane for future revocation (but see the limits on confinement (Section 4.3.1) for they are limits on those last two). One of the common use cases for capabilty-URIs that are single-use while giving access to less restricted capability-URIs is when the single-use capability-URI is sent through an unprotected channel (like email), or to ensure forward secrecy.

In contrast, the three other policies in the example above would need knowledge of the storage API to be able to implement the access abstraction.

A service providing access abstractions is a way to expose capability-URIs that give access to a legacy service that isn't capability-based itself.

4.6.3. Offline attenation

Several capability systems use a cryptographical chain of certificates as a capability, designed in such a way that when an agent has a chain [C0,C1,C2], it cannot extract the chain [C0,C1] and use it. Either it can extract it and cannot use it (e.g. because C1 designates a public key whose private key is needed to use the capability, which is the case with zcaps and UCANs), or it cannot extract it (e.g. because the chains actually look like [C0,C1,K1] and [C0,C1,C2,K2] and so the agent receiving the second capability misses K1 to reconstruct the full parent capability, which is the case with biscuits).

When such a system is used to create capability-URIs, then an agent holding a capability-URI can create attenuated capability-URIs without contacting any web service. To advertise this possibility, capability-URIs can use the tf parameter (Section 2.4.2).

Offline attenuation has several benefits. It makes it possible to use some new access abstractions with limited or no change in the web service's code. It means that the web service doesn't need to store capabilities itself, and the proliferation of attenuated capabilities doesn't create a burden on the web service. It also removes the web service as a point of failure: when the holder of a capability is offline or even when the web service itself is unavailable, capabilities can still be attenuated and communicated between agents for later use with the web service. The cost is the size of the tokens.

4.7. Responsibility tracking

As much as capabilities make it easier and safer to design systems that better prevent unwanted accesses and are more robust in the face of negligent or malicious behavior, they also make it possible to track complex chains of responsibility in cases where unwanted accesses happen.

The difference with ACL security is important. In an ACL system, permissions are usually granted broadly and often in advance. For example, all members of a lab might get added to a group that has write access to the files of all active projects of the lab, or maybe to groups linked to projects they participate in if security is tighter. If Mallet want some file overwritten without logs pointing to them, they set things up so that Bob will instruct Alice to write data in the targeted file. When it is later discovered that it wasn't the right file for what she wrote and she overwrote and lost valuable information, in the system itself, we may have only limited evidence:

  • when user X put Alice in the relevant group

  • when user Y gave that group write permissions on the file

  • when Alice modified the file

Users X and Y probably did their changes long before the unwanted access happened and none of them is Bob or Mallet. Depending on the system, on top of Alice being indirectly duped to write to some path, another person might have been indirectly duped so that this unassumng file path is actually a link to something else. The paths could have been communicated with all kinds of services, or even given by speech or written by hand, so they may not be anywhere in the security logs. Ultimately, Mallet might not have himself the authority in the system to write the file, nor Bob. In this scenario, even if permissions are given narrowly, they would not show any evidence against Mallet.

In a capability system, people cannot be duped to enter a path as the target of a link or write operation when those require a capability, not a path. This means that Mallet needs to have access to the targeted file himself. When Alice writes the file, the system can be designed to log that it's with a capability given by Bob, itself using a capability given by Mallet.

4.7.1. Responsability tracking protocols

Capabilities in general and REST share two features: being layered systems and having a uniform interface. Those make it possible to implement intermediate layers adding a feature to the overall system that are independent of the service behind them. Revocation (see Section 4.5) may be the simmplest. A responsibility tracking protocol is another.

TODO

4.8. Y-Property

If Alice has a capability to Bob and Carol, and Alice sends a message to Bob with the capability to Carol, the exchange has the Y-property if Alice's message to Bob is sufficient for Bob to establish a protected channel with Carol.

Simple https URIs don't support the Y-property for two reasons:

  • Bob also need to know in advance the certificate authority used by the target server.

  • If an attacker can have Bob contact a malicious server with a certificate using a certificate authority known to Bob, Bob will establish a channel with the attacker, not Carol.

Using the y-cert-param component, the client doesn't need to know a shared certificate authority in advance, and it can verify during TLS handshake that it is establishing a connection with the intended server.

Even if an attacker manages to alter the DNS records of the name used for the server, making it possible to have a valid certificate from a widely known certificate authority, the client would be able to detect that the server is not the one that Alice intended.

This also enables a safe use of self-signed certificates, for more self-sufficient systems.

4.9. Out-of-band communication

Transcription is an important design goal of URIs (see [RFC3986], section 1.2.2). Capability-URIs are designed to retain that ability, hence having the embedded URI or IRI unchanged (and not needing to percent-encode any part of it).

Capability-URIs and web-caps can be designed to facilitate two out-of-band communications: written transcription and dictation, but there is a trade-off between this facilitation and safety.

When the authorization data is a bearer token, it can be encoded as a series of words like an XCKD password. Some algorithms of this kind use a word list optimized for memorization of the phrase, or designed so that no two entries of the word list are likely to be mixed when reading, writing or saying them out loud, making manual transcription or dictation of a capability-URI both easy and reliable (other examples include the PGP word list or [RFC1751]).

Some word lists are localized, like in BIP39, and those can be used in capability-URIs that are IRIs.

Other algorithms don't use a word list but encode sequences of bits as syllables, usually with an emphasis on ensuring unambiguous pronounciation (examples include proquints and Sing-song).

The trade-off comes from the fact that manually writing or saying aloud a very long list of words or syllables can be daunting and error-prone. When generating and encoding a Swiss number (see Section 4.3), an application MAY choose to reduce the size, making the resulting token easier to write and say, but also more vulnerable to several attacks. Such capability-URIs or web-caps SHOULD have an appropriately short time of use and/or be usable only once.

4.10. Protection against CSRF

The use of capability-URIS according to POLA (Section 4.4) is resistant to CSRF attacks because an attacker cannot forge a request that the targeted user is authorized to make but the attacker isn't. By definition, when you combine authority with designation, noone can request what they are not authorized to do.

Of course, like any security framework, the design of an application can go against its principles and be unsafe despite the framework. If a service A holds a capabiliy-URI to service B with a broad authority, where part of the embedded URI is a guessable resource name, and service A uses resource names from its client requests, then service A has succesfully circumvented POLA and made itself vulnerable to confused deputy attacks, despite the use of capabilities.

An example of such a bad design would be (using URI templates, see [RFC6570]):

  • Service B is accessed by Service A through cap-https://K_qCxe8msbJGU4kkIIbSpA@quux.org/serviceB{?resource}

  • Service A is accessed by its clients through https://quux.org/serviceA{?resource}

4.11. Protection against XSS

Most previous uses of bearer tokens that protect against CSRF are vulnerable to XSS because some tokens with broad authority are stored in a global mutable store where XSS-injected malicious code can trivially read them (like the localStorage of a Web browser).

But with the combined use of REST and capability-URIs, client-side code can use capability-URIs in such a way that they are never stored except in the local variables of the functions using them, where XSS-injected malicious code cannot read them without the support of attacks like Meltdown or Spectre.

When using capability-URIs in a browser's address bar, either with a browser capable of using capability-URI directly, or with web-caps, a Web application SHOULD redact the address bar as soon as possible, as XSS-injected malicious code can trivially read it.

5. IANA Considerations

This specification defines several new URI schemes. Here are the informations for their registration to IANA according to [BCP35].

5.1. cap-http

Scheme name
cap-http
Status
Provisional
Applications/protocols that use this scheme name
TBD

5.2. cap-https

Scheme name
cap-https
Status
Provisional
Applications/protocols that use this scheme name
TBD

5.3. cap-ws

Scheme name
cap-ws
Status
Provisional
Applications/protocols that use this scheme name
TBD

5.4. cap-wss

Scheme name
cap-wss
Status
Provisional
Applications/protocols that use this scheme name
TBD

5.5. redacted

Scheme name
redacted
Status
Provisional
Applications/protocols that use this scheme name
TBD

6. Acknowledgements

The idea of a capability-URI was independently discovered by several people before this specification was imagined and written, and its final form draws a lot of elements from the work of Tyler Close (Waterken™ YURL, Mashing with permission), Neil Madden (Towards a standard for bearer token URLs), Ariadne Conill (Demystifying Bearer Capability URIs) and Christine Lemmer-Webber.

The bearcap URI scheme was broader in that it was supposed to embed any URI where a bearer token might be used to create a capability URI. Using dedicated URI schemes to embed HTTP URIs has several desirable properties:

7. References

7.1. Normative References

[BCP14]
Best Current Practice 14, <https://www.rfc-editor.org/info/bcp14>.
At the time of writing, this BCP comprises the following:
Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, DOI 10.17487/RFC2119, , <https://www.rfc-editor.org/info/rfc2119>.
Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174, , <https://www.rfc-editor.org/info/rfc8174>.
[BCP35]
Best Current Practice 35, <https://www.rfc-editor.org/info/bcp35>.
At the time of writing, this BCP comprises the following:
Thaler, D., Ed., Hansen, T., and T. Hardie, "Guidelines and Registration Procedures for URI Schemes", BCP 35, RFC 7595, DOI 10.17487/RFC7595, , <https://www.rfc-editor.org/info/rfc7595>.
[RFC3279]
Bassham, L., Polk, W., and R. Housley, "Algorithms and Identifiers for the Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile", RFC 3279, DOI 10.17487/RFC3279, , <https://www.rfc-editor.org/info/rfc3279>.
[RFC3986]
Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform Resource Identifier (URI): Generic Syntax", STD 66, RFC 3986, DOI 10.17487/RFC3986, , <https://www.rfc-editor.org/info/rfc3986>.
[RFC3987]
Duerst, M. and M. Suignard, "Internationalized Resource Identifiers (IRIs)", RFC 3987, DOI 10.17487/RFC3987, , <https://www.rfc-editor.org/info/rfc3987>.
[RFC4648]
Josefsson, S., "The Base16, Base32, and Base64 Data Encodings", RFC 4648, DOI 10.17487/RFC4648, , <https://www.rfc-editor.org/info/rfc4648>.
[RFC5234]
Crocker, D., Ed. and P. Overell, "Augmented BNF for Syntax Specifications: ABNF", STD 68, RFC 5234, DOI 10.17487/RFC5234, , <https://www.rfc-editor.org/info/rfc5234>.
[RFC6234]
Eastlake 3rd, D. and T. Hansen, "US Secure Hash Algorithms (SHA and SHA-based HMAC and HKDF)", RFC 6234, DOI 10.17487/RFC6234, , <https://www.rfc-editor.org/info/rfc6234>.
[RFC6455]
Fette, I. and A. Melnikov, "The WebSocket Protocol", RFC 6455, DOI 10.17487/RFC6455, , <https://www.rfc-editor.org/info/rfc6455>.
[RFC6750]
Jones, M. and D. Hardt, "The OAuth 2.0 Authorization Framework: Bearer Token Usage", RFC 6750, DOI 10.17487/RFC6750, , <https://www.rfc-editor.org/info/rfc6750>.
[RFC8246]
McManus, P., "HTTP Immutable Responses", RFC 8246, DOI 10.17487/RFC8246, , <https://www.rfc-editor.org/info/rfc8246>.
[RFC9110]
Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, Ed., "HTTP Semantics", STD 97, RFC 9110, DOI 10.17487/RFC9110, , <https://www.rfc-editor.org/info/rfc9110>.
[RFC9111]
Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, Ed., "HTTP Caching", STD 98, RFC 9111, DOI 10.17487/RFC9111, , <https://www.rfc-editor.org/info/rfc9111>.
[RFC9112]
Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke, Ed., "HTTP/1.1", STD 99, RFC 9112, DOI 10.17487/RFC9112, , <https://www.rfc-editor.org/info/rfc9112>.
[RFC9113]
Thomson, M., Ed. and C. Benfield, Ed., "HTTP/2", RFC 9113, DOI 10.17487/RFC9113, , <https://www.rfc-editor.org/info/rfc9113>.
[RFC9114]
Bishop, M., Ed., "HTTP/3", RFC 9114, DOI 10.17487/RFC9114, , <https://www.rfc-editor.org/info/rfc9114>.
[RFC9846]
Rescorla, E., "The Transport Layer Security (TLS) Protocol Version 1.3", RFC 9846, DOI 10.17487/RFC9846, , <https://www.rfc-editor.org/info/rfc9846>.

7.2. Informative references

[aclsdont]
Close, T., "ACLs Don't", <https://papers.agoric.com/papers/acls-dont/abstract/>.
[ariadneconill]
Conill, A., "Demystifying Bearer Capability URIs", <https://ariadne.space/2019/10/10/demystifying-bearer-capability-uris.html>.
[bip39]
Palatinus, M., Rusnak, P., Voisine, A., and S. Bowe, "Mnemonic code for generating deterministic keys", <https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki>.
[biscuit]
Couprie, G., Delafargue, C., and C. Corbière, "Eclipse Biscuit", <https://www.biscuitsec.org/>.
[biscuit-revocation]
Couprie, G., Delafargue, C., and C. Corbière, "Biscuit — Revocation", <https://www.biscuitsec.org/docs/guides/revocation/>.
[capmyths]
Miller, M. S., Yee, K., and J. Shapiro, "Capability Myths Demolished", <https://papers.agoric.com/papers/capability-myths-demolished/abstract/>.
[csp]
West, M. and A. Sartori, "Content Security Policy Level 3", <https://w3c.github.io/webappsec-csp/>.
[html-living]
"HTML Living Standard", <https://html.spec.whatwg.org/>.
[jsonld]
Sporny, M., Longley, D., Kellogg, G., Lanthaler, M., Champin, P., and N. Lindström, "JSON-LD 1.1 A JSON-based Serialization for Linked Data", <https://www.w3.org/TR/json-ld/>.
[neilmadden]
Madden, N., "Towards a standard for bearer token URLs", <https://neilmadden.blog/2021/03/20/towards-a-standard-for-bearer-token-urls/>.
[proquints]
Wilkerson, D. S., "A Proposal for Proquints: Identifiers that are Readable, Spellable, and Pronounceable", <https://arxiv.org/abs/0901.4016v2>.
[proxies]
Cutsem, T. V. and M. S. Miller, "Proxies: design principles for robust object-oriented intercession APIs", <https://dl.acm.org/doi/10.1145/1899661.1869638>.
[rest]
Fielding, R., "Architectural Styles and the Design of Network-based Software Architectures", <https://roy.gbiv.com/pubs/dissertation/top.htm>.
[RFC1751]
McDonald, D., "A Convention for Human-Readable 128-bit Keys", RFC 1751, DOI 10.17487/RFC1751, , <https://www.rfc-editor.org/info/rfc1751>.
[RFC6071]
Frankel, S. and S. Krishnan, "IP Security (IPsec) and Internet Key Exchange (IKE) Document Roadmap", RFC 6071, DOI 10.17487/RFC6071, , <https://www.rfc-editor.org/info/rfc6071>.
[RFC6570]
Gregorio, J., Fielding, R., Hadley, M., Nottingham, M., and D. Orchard, "URI Template", RFC 6570, DOI 10.17487/RFC6570, , <https://www.rfc-editor.org/info/rfc6570>.
[robust]
Miller, M. S., "Robust Composition: Towards a Unified Approach to Access Control and Concurrency Control", <https://papers.agoric.com/papers/robust-composition/abstract/>.
[singsong]
Vryonis, P., "Sing-song: a speakable encoding for long numbers and keys", <https://blog.vrypan.net/2026/08/19/260819-sing-song/>.
[ucan]
Gozalishvili, I., Holmgren, D., Krüger, P., and B. Zelenka, "User Controlled Authorization Network (UCAN) Specification", <https://github.com/ucan-wg/spec/>.
[web-key]
Close, T., "Mashing with permission", <https://waterken.sourceforge.net/web-key/>.
[webarch]
Berners-Lee, T., Bray, T., Connolly, D., Cotton, P., Fielding, R., Jeckle, M., Lilley, C., Mendelsohn, N., Orchard, D., Walsh, N., and S. Williams, "Architecture of the World Wide Web, Volume One", <https://www.w3.org/TR/webarch/>.
[xkcd]
Munroe, R., "Password Strength", <https://xkcd.com/936/>.
[yurl]
Close, T., "Waterken™ YURL", <https://web.archive.org/web/20040221205825/http://www.waterken.com/dev/YURL/httpsy/>.
[zcap]
Lemmer-Webber, C., Sporny, M., and M. S. Miller, "Authorization Capabilities for Linked Data", <https://w3c-ccg.github.io/zcap-spec/>.

Appendix A. Bootstrapping a web service with POLA

When a service exposes authority, it might be counter-intuitive that every invocation of the service should still provide the authority needed for the service to respond. This example shows how this could work with a file storage service.

The service is first deployed with only permissions to:

The service's administrator will use the root capability to create folders, named according to usernames allocated to users. Creating a folder alice with the root capability would have the service create a folder /srv/storage/alice. This means that the administrator doesn't need special tools to create folders that are private to users. Its power doesn't come from special operations, only from where the root capability gives authority to read and write.

The user Alice would receive the capability-URI to use her own storage space, unaware if, under the hood, her files are in /srv/storage/alice, /srv/storage/user1 or any other path. Through her capability-URI, no parent directory and no full path is visible. When she in turn wants to create a folder projects, and later a folder projects/report, she would not use a path like alice/projects/report. She would use her initial capability to create a folder projects. This would in turn give her a capability to that folder, and she would use that capability to create a folder report.

This means that the storage service doesn't need to decide if a user has the right to access something. It is inherent to the capability that they can. In its internal database, the service maintains a mapping between capabilities, operations and paths. If it finds a path associated with the capability and the operation, it operates in that path. Using capabilities to interact with the service although it obviously has the authority to act on the underlying storage is what makes the whole thing actually secure: the service cannot be confused to use its own authority to do operations that the user isn't authorized to do. And an attacker cannot construct a request to have a user with more authorization send it to the service.

This also means that the root capability isn't special. It will just be associated on startup with the root of the space. This means that full administrative rights can be delegated in folders just by giving a read/write capability-URI for that folder. A user without access to the configuration file, given a capability-URI and instructed to be the administrator, wouldn't know if they are administering the full storage space or a subspace.

If it is desirable that some administrators cannot access the users' private folders, one additional attenuation available from a folder with read/write access would be to make it so the only operation is to create new folders in it but the resulting read/write capability-URI is sent to an inbox given as a parameter, verifiable through the storage service for the recipient. The service could even be configured so that the root capability is that kind of capability, and no level of administrator can read everyone's files.

Appendix B. Weather service caretaker

Here's an example of creating, sending, using and revoking a caretaker. The APIs seen in this example are hypothetical, not part of this specification. The APIs use a JSON-like data model and the pseudo-code may or may not be valid in some existing programming language, and omits the boilerplate that may be needed to ensure encoding and decoding of native data structures to a format that supports that data model, like JSON or CBOR.

Let's say user Alice has three capability-URIs, to Bob's inbox, to a service to create caretakers that she trusts, and to a high-resolution weather service she's paying for:

Alice makes the following request to create the caretaker:

http.post("cap-https://bt=x5bFnods8Dq_pKhfDP-1Dw@alice.io/new-caretaker",
         {target: "cap-https://bt=oKFejFiwgz-JDYbh2OOR7g@high-res-weather.io/report"})

The response from the server is:

{invoke: "cap-https://bt=i1NLl48cmHZllBHmdA70UQ@alice.io/caretaker",
 revoke: "cap-https://bt=VgOO0MrxbcAHHLp3_nH_GQ@alice.io/caretaker"}

Then Alice sends both of the received capabilities to Bob's inbox:

http.post("cap-https://bt=ZseIJun3ksCGGpIelt53zg@bob.net/inbox",
          {message: "Here's the high-res weather API, I'll let you use it during your
                     week at sea. You can revoke your access yourself when you're
                     back on land."
           attachments:
            [{label: "invoke":,
              type: "cap",
              content: "cap-https://bt=i1NLl48cmHZllBHmdA70UQ@alice.io/caretaker"},
             {label: "revoke",
              type: "cap",
              content: "cap-https://bt=VgOO0MrxbcAHHLp3_nH_GQ@alice.io/caretaker"}]})

Alice can feel safe that she didn't give Bob any dangerous authority. He can only use the caretaker and render it useless. If Bob forgets to do so, or tries to extend his use of the caretaker, at any point, Alice can use the revoke capability so that the caretaker loses its internal capability.

The caretaker is simple enough that it makes composition with other tools trivial. For example, Alice could set up a timed worker, e.g. as a cron job, to invoke the revoke capability after some time. Again, that worker wouldn't need a complex and possibly dangerous authority to do its job. It wouldn't need the authority to make arbitrary network connections or even arbitrary HTTP requests. In terms of both authority, persistent state and code complexity, it would not need to deal with any multi-step authentication process, and the storage of cookies and temporary tokens. It would only need access to time (and not even that when the worker is a cron job), the ability to make a request for a capability-URI, and the revoke capability-URI.

Appendix C. Web-keys

The Waterken project figured that if you're forced to use legacy https URIs as capabilities in the browser, one major and very problematic source of leakage would be the Referer header, because clicking such a link to a different server could leak the URI containing the token.

The solution used in web-keys was to put the token in the fragment, because HTTP/1.1 precludes the fragment being in the Referer header (and browsers were seen to respect the RFC on that point, see [RFC9110], section 10.1.3).

Because the fragment is not sent to the server, the mechanism of the web-key is for the representation of the resource to be a Web page with client-side code that will read the token in the fragment and make a second request, replacing the content of the page with the response to the second request.

The intermediary Web page is a very simple piece of code, and can have a reasonably long cache expiration, which means that after the first request for a web-key on one server, every following request for a web-key is a single request.

Author's Address

Pierre Thierry
Comonad Dev