Skip to content

rfc: Attested Authority#7

Open
Peeja wants to merge 2 commits into
mainfrom
2026-05-attested-authority
Open

rfc: Attested Authority#7
Peeja wants to merge 2 commits into
mainfrom
2026-05-attested-authority

Conversation

@Peeja
Copy link
Copy Markdown

@Peeja Peeja commented May 11, 2026

📖 Preview

An attempt to solve the weirdness of how we do attestations, and to extend into OAuth and other systems. I'm going to hold a discussion session to talk through this.

@Peeja Peeja marked this pull request as ready for review May 11, 2026 22:36
Copy link
Copy Markdown
Member

@alanshaw alanshaw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a really neat way to communicate the attestation. I didn't think of actually putting it in the signature 👏

Comment thread rfcs/2026-05-attested-authority.md Outdated
"alg": <bytes: authority's Varsig header for its own key type, e.g. Ed25519+DAG-CBOR>
},
"sig": <bytes: authority's raw signature over the canonical DAG-CBOR encoding of "payload">
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks a lot like an invocation. I'm curious why we would not just put an invocation here? We'd get to re-use a lot of machinery to encode/decode and validate it, and it would be similar in concept to receipts, which are also invocations, with a /ucan/attest/receipt command.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I had the same thought but didn't pursue it. That might be the best version of this.

Comment thread rfcs/2026-05-attested-authority.md Outdated
```
{
"payload": {
"subject": <string: the attested subject DID>,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need subject repeated here?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, you're right, it's part of what's hashed in payload_hash as it is. We can scratch it here.

Comment thread rfcs/2026-05-attested-authority.md Outdated
{
"payload": {
"subject": <string: the attested subject DID>,
"payload_hash": <bytes: SHA-256 hash of the canonical DAG-CBOR encoding of the delegation payload>,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multihash?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's what that should be really.

Copy link
Copy Markdown
Member

@alanshaw alanshaw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy for this to merge as is. I have left comments for your consideration.


## Abstract

This document proposes a generic scheme for using externally-verified identities as UCAN delegation subjects. Such identities, including email addresses, OAuth-based identities, and others, have no associated keypair under user control, and therefore cannot sign UCAN delegations directly. A trusted authority performs an out-of-band verification (email loop, OAuth exchange, etc.) and produces a cryptographic attestation on behalf of the subject identity. A generic Varsig signature type (`authority-attestation`) is defined that encodes the authority's attestation in place of a conventional asymmetric signature, with the specific verification method encoded in the attestation payload rather than the type. This allows attested identities to appear as `iss` in root UCAN delegations while remaining structurally honest about the nature of the verification performed.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This document proposes a generic scheme for using externally-verified identities as UCAN delegation subjects. Such identities, including email addresses, OAuth-based identities, and others, have no associated keypair under user control, and therefore cannot sign UCAN delegations directly. A trusted authority performs an out-of-band verification (email loop, OAuth exchange, etc.) and produces a cryptographic attestation on behalf of the subject identity. A generic Varsig signature type (`authority-attestation`) is defined that encodes the authority's attestation in place of a conventional asymmetric signature, with the specific verification method encoded in the attestation payload rather than the type. This allows attested identities to appear as `iss` in root UCAN delegations while remaining structurally honest about the nature of the verification performed.
This document proposes a generic scheme for using externally-verified identities as UCAN delegation subjects. Such identities, including email addresses, OAuth-based identities, and others, have no associated keypair under user control, and therefore cannot sign UCAN delegations directly. A trusted authority performs an out-of-band verification (email loop, OAuth exchange, etc.) and produces a cryptographic attestation on behalf of the subject identity. A new Varsig signature type (`authority-attestation`) is defined that encodes the authority's attestation in place of a conventional asymmetric signature, with the specific verification method encoded in the attestation payload rather than the signature type. This allows attested identities to appear as `iss` in root UCAN delegations while remaining structurally honest about the nature of the verification performed.


### 3.1 `did:mailto`

1. The client authors a delegation payload in which the `did:mailto` issues some capability to the client's agent identity, and issues an invocation to the authority asking for it to be signed.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
1. The client authors a delegation payload in which the `did:mailto` issues some capability to the client's agent identity, and issues an invocation to the authority asking for it to be signed.
1. The client authors a payload that describes a delegation in which the `did:mailto` issues some capability to the client's agent identity, and issues an invocation to the authority asking for it to be signed.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Delegation payload" here is meant to refer to a literal payload portion of a UCAN delegation, but it sounds like it's coming across as "a payload which is an entire delegation". Maybe I should make it "the payload portion of a delegation"?

1. The client authors a delegation payload in which the `did:mailto` issues some capability to the client's agent identity, and issues an invocation to the authority asking for it to be signed.
2. The authority computes a an HMAC over `(delegation_payload, iat, exp)` using a key the authority controls, where `delegation_payload` is the canonical (DAG-CBOR) encoding of the delegation payload, `iat` is the time the link was issued, and `exp` is some expiration time for the link. `iat` is optional, but will result in a timestamp on the final signature for tracking purposes.
3. The authority sends an email to the address encoded in the `did:mailto` DID, containing a verification link pointing back to the authority's web server. The URL's params include the delegation payload, the `exp`, and the HMAC, suitably encoded.
4. When the user clicks the link, the authority validates the HMAC.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not how things currently are implemented (we use a signed invocation not HMAC), and I'd probably adjust the spec to allow this process to be done differently, perhaps within some guide rails.

I think you already know, but just incase, there's a description of the current process here: https://hackmd.io/VUuW15CeRI6J1ZIR2yZwpA?view#accessconfirm

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I've been thinking this through, and I was sure there was a reason an invocation didn't make sense, but…I don't see it anymore. This section's evolved a bit so maybe I just worked myself into reimplementing a worse version of the invocation. 😅


The authority MAY define a policy governing what delegations it will verify and attest to, and reject requests which are not permitted by that policy. For instance, the authority may reject delegations without a recent `nbf` ("not before"), or with an `exp` ("expiration") that is `null` or too far in the future. To reject an attestation request, the authority MUST return a failure for the original attestation request invocation. If it does not reject the request, the authority MUST use the delegation payload as given, with no changes.

The verification link should have a reasonably short expiration. 15–60 minutes is RECOMMENDED.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think 60 minutes is a long time. I would say 10 minutes (or shorter) is usual for this type of thing. It really shouldn't take that long to log into your email nowadays. I'd perhaps rephrase to say something like "recommended to be no longer than 15 minutes".

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I like that.


The verification link should have a reasonably short expiration. 15–60 minutes is RECOMMENDED.

The authority MAY use a digest in place of the full payload in the URL, but this requires it to store the pending payload while waiting for verification. In this configuration, the stored payload can be evicted from the store when the link expires.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I think enumerating all the different ways to do this is not necessary - just saying you get to choose how to do this (out of scope) is fine IMHO.

0x34 Varsig prefix
0x01 Varsig version 1
0x300001 authority-attestation algorithm discriminant (varint)
0x71 Payload encoding: DAG-CBOR
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A previous version of this RFC included the authority DID here. Why would we not do that?

If we continue to not, then there is an earlier mention of it in the intro I think that would need to be removed.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, shoot, I thought I got them all.

I couldn't come up with a reason it was still useful, but maybe it is? You don't need to know it to interpret and validate the signature. On the other hand, it does correspond with the verification method's content. But back on the first hand, a public key appears in a Multikey verification method, and the key doesn't appear in a corresponding Varsig; you're expected to know the public key you expect already.

Comment on lines +215 to +216
* **`nonce`** is empty. As an assertion of fact, the invocation is inherently idempotent.
* **`exp`** is `null`. As an assertion of fact, the invocation cannot expire: the signature cannot have not happened because time has passed. The delegation's `exp` controls the expiration of the delegation.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about this. It feels valuable to be able to expire the attestation, but conversely I think "the signature cannot have not happened because time has passed" is a fair point.

Don't oauth access tokens have time bounds that would be useful to echo in exp?

Login session lengths are a thing - logging in again after a certain period of time seems like a good idea...

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but all of that is the delegation's exp, which is why the service is allowed to constrain what exps are allowed. But the signing still happened either way. It gets a bit weird if you can have a delegation which hasn't expired, but whose signature somehow has. I suppose the closest analog is when a principal rotates keys and invalidates their old signatures, but that's not a fun situation.


## 5. Verification

When a verifier encounters a delegation with a Varsig header with the algorithm discriminant `0x300001`, it should:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just thinking about this, I'd implement as a varsig codec and did verifier resolver in libforge that we use in sprue (and elsewhere). That is to say, I'd not add it to ucantone - we have spec'd this here, but it is by no means canon and is certainly not part of the UCAN/varsig or DID specs.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense to me. I also think it makes sense in ucantone, since the verifier resolvers are all registered extensions anyway, not core to the library—they could just as easily each be their own separate modules. But if we're worried about ucantone vouching for it too much by including the implementation in the module, libforge makes sense. The registry architecture makes it simple to move at any time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants