# 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/