# Permission Delegation / Invocation ## Delegation ```sequence participant Alice as A participant Bob as B participant Carol as C participant Daniel as D participant Eric as E A->B: (pg) A B->C: (pg) A.B C->D: (pg) A.B.C D->E: (pg) A.B.C.D ``` ```javascript= const A = { descriptor: { method: "PermissionsGrant", grantedBy: "did:ex:alice", grantedTo: "did:ex:bob", scope: { method: "CollectionsWrite", schema: "https://schema.org/MusicPlaylist" }, conditions: { delegation: true } }, authorization: JWS(grantedByKey, { descriptorCid }) } const B = { descriptor: { method: "PermissionsGrant", grantedBy: "did:ex:bob", grantedTo: "did:ex:carol", delegatedFrom: CID(A), scope: { method: "CollectionsWrite", schema: "https://schema.org/MusicPlaylist" }, conditions: { delegation: true }, dataCid: CID(data) }, data: [A] authorization: JWS(grantedByKey, { descriptorCid }) } const C = { descriptor: { method: "PermissionsGrant", grantedBy: "did:ex:carol", grantedTo: "did:ex:daniel", delegatedFrom: CID(B), scope: { method: "CollectionsWrite", schema: "https://schema.org/MusicPlaylist" }, conditions: { delegation: true }, }, data: [A, B] authorization: JWS(grantedByKey, { descriptorCid }), } const D = { descriptor: { method: "PermissionsGrant", grantedBy: "did:ex:daniel", grantedTo: "did:ex:eric", delegatedFrom: CID(C), scope: { method: "CollectionsWrite", schema: "https://schema.org/MusicPlaylist" }, conditions: { delegation: true } }, data: [A, B, C], authorization: JWS(grantedByKey, { descriptorCid }), } ``` ## Invocation ### Eric Invoking `D` to write a `MusicPlaylist` to Alice's DWN ```javascript= const msg = { descriptor: { method: "CollectionsWrite", schema: "https://schema.org/MusicPlaylist", dataCid: CID(msg.data) }, data: "asdksaklj32kl4j32lk54j", authorization: JWS(senderKey, { descriptorCid: CID(descriptor), permissionsGrantCid: CID(D) }) }; // we can't be certain that Alice has the entire delegation chain, so it's the responsibility of the sender to // include the entire chain as messages const messages = [D, msg] messages.push(msg); const request = { target: 'did:ex:alice', messages } await DWN.send(request) async function unfurlDelegationChain(grant) { const pg = grant; const chain = [grant]; do { const parent = await messageStore.get(pg.descriptor.delegatedFrom); if (!parent) { continue; } chain.push(parent); pg = parent; } while (pg.descriptor.delegatedFrom); } // function unfurlDelegationChain(grant) { // const delegationChain = [grant]; // let pg = D; // const { decode } = new TextDecoder(); // do { // const { data: parentEncoded } = pg; // const parentBytes = base64url.decode(parentEncoded); // const parentStr = decode(parentBytes); // const parent = JSON.parse(parentStr); // delegationChain.push(parent); // pg = parent; // } while(pg.descriptor.delegatedFrom) // return delegationChain; // } ``` ## Capability Validation ```javascript= // assume request from previous code snippet async function processRequest(request) { // we are making the assumption that a seemingly unrelated permission (e.g. pg.descriptor.grantedBy !== request.target || descriptor.grantedTo !== target) was sent to our DWN because it's contextually relevant but we don't want to waste compute/network cycles checking signatures, resolving DIDs etc, so any PermissionsGrant that we come across that wasn't granted to or by the tenant is going to get put into this map. const delegationMap = {}; let rootPermission; // assume target has been validated for (let message of request.messages) { // assume: validate message const { descriptor } = message; if (descriptor.method === 'PermissionsGrant') { // if the grant isn't from or to the tenant, then we have to assume, best case, that it's part of a delegation chain if (descriptor.grantedBy !== request.tenant && descriptor.grantedTo !== request.tenant) { const msgCid = await message.getCid(); delegationMap[msgCid] = message; } } else { messages.push(message); } } } ```