# 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);
}
}
}
```