# Auth Idea
## Motivation
Current [authorization] protocol allows application to obtain access to all account capabilities by creating a session within which (application local) [`did:key`] can be used to sign [UCAN]s issued by account's [`did:mailto`] identifier.
```mermaid
flowchart LR
Space("📦 did:key:zAliSpace
{ can: * with: did:key:zAliSpace }")
BobSpace("📦 did:key:zBobSpace
{ can: store/list with: did:key:zBobSpace }")
Alice("👩💻 did:mailto:web.mail:alice
{ can: * with: * }")
Agent("🔑 did:key:zAppAgent")
Agent --> Alice
Alice --> Space
Alice --> BobSpace
```
This is not good default, applications SHOULD only have access to set of capabilities they need instead. To address this we have been thinking of an intermediate application "keyring" that CAN obtain complete access to the account and share only required set of capabilities with the other applications.
```mermaid
flowchart LR
Space("📦 did:key:zAliSpace
{ can: * with: did:key:zAliSpace }")
BobSpace("📦 did:key:zBobSpace
{ can: store/list with: did:key:zBobSpace }")
Alice("👩💻 did:mailto:web.mail:alice
{ can: * with: * }")
Agent("🔑 did:key:zAppAgent")
Keyring("🚦 did:key:zKeyRing
{ can: store/* with: did:key:zAliSpace}
")
Agent --> Keyring
Keyring --> Alice
Alice --> Space
Alice --> BobSpace
```
However this creates situation that some apps MAY choose to authenticate through "keyring" while others MAY choose not to, or choose alternative "keyring" application. This is not the desired outcome user should be choosing how they want to manage access not apps.
Limiting authorization SHOULD happen at the protocol level instead, that way user gets to choose what application can do and not the other way round.
```mermaid
flowchart LR
Space("📦 did:key:zAliSpace
{ can: * with: did:key:zAliSpace }")
BobSpace("📦 did:key:zBobSpace
{ can: store/list with: did:key:zBobSpace }")
Alice("👩💻 did:mailto:web.mail:alice
{ can: * with: * }")
Agent("🔑 did:key:zAppAgent")
Keyring("🚦 did:key:zSession
{ can: store/* with: did:key:zAliSpace}
")
Agent --> Keyring
Keyring --> Alice
Alice --> Space
Alice --> BobSpace
```
Problem is that there needs to be an intermediary delegation like `did:key:zSession` that can restrict set of capabilities, yet there is no principal that should have keys for it.
## Idea
What if we introduce one-off unique [UCAN] principals that can only re-delegate specific set of capabilities to another principal. That principal could represent JSON document similar to a UCAN e.g:
```json
{
"v": "0.10.0",
"aud": "did:key:zAppAgent",
"att": [
{ can: "store/*", with: "did:key:zAliSpace" }
],
"fct": [],
"exp": 16742455386
}
```
We could identify such a document by CID:
`bafyreihjuj3ubcdt73wmcm3yl3rc5c2sbau7bpaso5vzjbxh6v623sil5a`
We could then create an actual UCAN like:
```ts
{
"iss": "did:ipld:bafyreihjuj3ubcdt73wmcm3yl3rc5c2sbau7bpaso5vzjbxh6v623sil5a"
"aud": "did:key:zAppAgent",
"att": [
{ can: "store/*", with: "did:key:zAliSpace" }
],
"fct": [],
"exp": 16742455386,
"prf": [
{
"iss": "did:mailto:web.mail:alice",
"aud": "did:ipld:bafyreihjuj3ubcdt73wmcm3yl3rc5c2sbau7bpaso5vzjbxh6v623sil5a",
"att": [
{ can: "*", with: "*" }
],
"prf": [{
"iss": "did:web:web3.storage",
"aud": "did:mailto:web.mail:alice",
"att": [{
"can": "./update"
"with": "did:web:web3.storage",
"nb": {
"key": "bafyreihjuj3ubcdt73wmcm3yl3rc5c2sbau7bpaso5vzjbxh6v623sil5a"
}
}]
}]
}
]
"s": "bafyreigt7mlvnncgg7qhfe4jcpgrvqkjdjir7yfod6yhfjt2hudzzprcoq"
}
```
Please note that signature in this UCAN is non-cryptographic it is basically a CID of the UCAN (without `s` signature field).
This meant to allow a verifier to check whether `iss` points to the CID to the same UCAN execpt `prf` and `s` and `iss` fields.
It is basically represents a filter, because you can update `prf` as you wish yet it will filter them based on `att` encoded in both UCAN and it's `iss` identifier.
It may not be clear how is this better than just specifying `att` when delegating from `did:mailto:web.mail:alice`. The thing is that we do not actually sign that delegation
---------
So we create authorization here that delegates from `did:mailto:web.mail:alice` to a local `did:key:zAppAgent` agent. This delegation does not have a signature with an asymetric key, instead it's signature is a CID of itself without `sig` and `prf` fields.
Our session `./update` also says that CID of the delegation is the key (repeating these CIDs seems redundunt perhaps we should drop the one in auth)
Session is attestation from US that we have got approval from `did:mailto:web.mail:alice` to issue `bafy...proxy` delegation.
With DKIM we would basically put `bafy...proxy` in the subject line as an approval.
This implies that:
1. Anyone could take `bafy...auth` and update `prf` as they see fit without affecting a signature.
2. If the omit `bafy...ses` from the `prf` it will no longer be valid as we'll have no way of resolving the key.
3. How would you revoke `auth` if you could constantly change it's CID ?
```json
{
"roots": [
{"/": "bafy..auth"}
],
"blocks": {
"bafy...auth": {
"iss": "did:mailto:web.mail:alice",
"aud": "did:key:zAppAgent",
"att": [{
"can": "store/*",
"with": "did:key:zAliSpace"
}],
"prf": [{ "/": "bafy..ses" }],
"sig": "bafy...proxy"
},
"bafy..ses": {
"iss": "did:web:web3.storage",
"aud": "did:mailto:web.mail:alice",
"att": [
{
"with": "did:mailto:web.mail:alice",
"can": "./update",
"nb": {
"key": "bafy...proxy"
}
}
]
},
"bafy...proxy": {
"iss": "did:mailto:web.mail:alice",
"aud": "did:key:zAppAgent",
"att": [{
"can": "store/*",
"with": "did:key:zAliSpace"
}]
}
}
}
```
-----------
## Request
```json
{
"bafy...auth": {
"iss": "did:key:zAppAgent",
"aud": "did:web:web3.storage",
"att": [{
"with": "did:key:zAppAgent",
"can": "access/authorize",
"nb": {
"aud": "did:key:zAppAgent",
"iss": "did:mailto:web.mail:alice",
"att": [
{
"can": "store/*",
// providers match if knows space can ask
// specific otherwise just match all and
// during negotiation user chooses
"with": "*"
}
],
"fct": [],
"exp": 16742455386
}
}]
},
}
```
## Response
```json
{
"bafy...grant": {
"iss": "did:web:web3.storage",
"aud": "did:mailto:web.mail:alice",
"att": [{
"can": "./update",
"with": "did:web:web3.storage",
"nb": {
"aud": "did:key:zAppAgent",
"att": [
{ "can": "store/*",
"with": "did:key:zAliSpace"
}
],
"fct": [],
"exp": 16742455386
}
}]
}
}
```
## Agent auth
```json
{
"bafy..proof": {
"iss": "did:mailto:web.mail:alice",
"aud": "did:key:zAppAgent",
"att": [
{
"can": "store/*",
"with": "did:key:zAliSpace"
}
],
"fct": [],
"exp": 16742455386,
"prf": [
{ "/": "bafy..grant" }
]
},
"bafy..use": {
"iss": "did:key:zAppAgent",
"aud": "did:web:web3.storage",
"att": [
{
"can": "store/add",
"with": "did:key:zAliSpace",
"link": "bag...bla"
}
],
"prf": [
{ "/": "bafy..proof" }
]
}
}
```
[authorization]: ./w3-session.md
[ucan]: https://github.com/ucan-wg/spec/
[`did:mailto`]: https://github.com/ucan-wg/did-mailto/
[`did:key`]: https://w3c-ccg.github.io/did-method-key/