In our Intro to UCAN post, we gave a high-level overview of the new authorization protocol at the heart of our next-generation storage APIs. One of the most exciting things about UCAN is the ability for users to delegate access to their account's resources to other users, services, and devices. This post will highlight some of the ways that delegation can be used with w3up, our new upload API. But first, let's consider some use cases that don't require delegation.

User owned accounts by default

When applications use the w3ui components, or developers run the w3up CLI, the default behavior is provisioning a new "account" for the device and linking to their email address via registration.

If you're uploading files from your laptop using the w3up command line tool, you can just create your UCAN identity key on the laptop and register it with the w3up service using the w3up register command. The service will issue you a UCAN token that grants your UCAN identity some capabilities; in this case, you get the ability to upload data to web3.storage, list your uploads, unlink uploads from your account, etc.

Because your laptop has access to the private key for your registered identity, there's no need to delegate anything. The w3up tool will just sign a UCAN request token with your private key and include the token from the w3up service as proof that you have the right permissions.

Another example is a fully decentralized app with a web frontend that interacts with a smart contract "backend." Let's say you're building a decentralized image sharing application, where users can get rewarded for sharing funny memes. You'd like your app's users to be able to upload images to web3.storage, but you don't want to coordinate their access or handle billing on their behalf.

In that case, you can use the w3ui keyring component to help your users create their own UCAN identity and register it with w3up. There's no need for delegation in this case, because each user has their own registered account with web.storage.

App owned accounts - transparent to the user

Now let's see where web3.storage delegation can improve the experience for end users of traditional apps. By delegating to a user's session the capability to upload to your app's w3up account, you can add uploads to an app without impacting your infrastructure, completely invisibly to users.

Let's say that you run a recipe sharing app written on traditional web2 technologies. You have a sizeable user-base, and you want to add recipe photo uploads without impacting your existing infrastructure. With UCAN delegation, users can upload directly to your web3.storage account, with no visible impact on the user experience.

To do this, you can delegate access to your own web3.storage account, instead of having each user create their own account.

On the client side, each user will still create a UCAN identity key, but instead of registering it with web3.storage, they can send the public ID to your app's web API or cloud function. That service will issue a UCAN that grants some permissions to the user, without needing to coordinate anything between your app's backend and the web3.storage service. In that way, storage access is attached to your application's session.

When a new user signs into your web app, the frontend can use w3up-client to create a new UCAN identity. Instead of registering the identity with w3up directly, the frontend can send the public ID to your backend service instead.

Your backend service can use an instance of w3up-client that's been configured to use your service's registered UCAN identity. The client's makeDelegation method will create a delegation object that you can send to the user.

Here's what your backend code might look like:

import W3Client from '@web3-storage/w3up-client'

async function delegationRequestHandler(request) {
  const userDID = await getDIDFromRequest(request)
  const client = await makeW3Client()
  const delegation = await client.makeDelegation(userDID)

  // The delegation object is a binary "blob" that encodes the UCAN
  // delegation into the CAR format. 
  // Here we're just returning it from our handler and assuming that the 
  // server-side framework will handle the details of setting the right 
  // Content-Type headers, etc.
  // Alternatively, you could encode the delegation to base64 and 
  // include it in a JSON response, etc.

  return delegation
}

async function makeW3Client() {
  // Your real service will need to initialize the client with
  // your registered UCAN identity (omitted for brevity).
  return new W3Client()
}

async function getDIDFromRequest(request) {
  // How to extract the user's public DID from the request will
  // depend on the server-side framework you're using and your
  // service API.
  //
  // Here we'll assume that the `request` object has a `json()`
  // method that does what you'd expect, and that the request
  // body has a "did" field containing the DID.
  const body = await request.json()
  return body.did
}

When the frontend recieves the delegation from your service, they can call importDelegation on their w3up-client instance and pass in the bytes of the delegation object. From then on, any requests that the client makes to w3up will use the delegated permissions from your web3.storage account.

Delegate across apps and services

You can also use delegation to share access with other services, without requiring an explicit agreement between each service provider.

For example, you or your users could delegate access to an image resizing app that will create thumbnails of each uploaded image and write them back to web3.storage. While you could do this without delegation by having the image app use its own web3.storage account, the uploads would be attached to the account for the app. By using delegation, you'll be able to see the CIDs for the thumbnails in the upload listing for the user's account instead, and the resizing app doesn't need to have any kind of business relationship with web3.storage to use the service.

Link devices through delegation

The last use case we'll look at today involves managing multiple devices belonging to the same user.

Earlier, we used the example of uploading files from your laptop as a case where delegation isn't needed, since the private key for your registered UCAN identity lives on the laptop.

If you also want to upload from your phone, you can use delegation to share access to your account without needing to go through the registration process on the new device. Instead, your laptop can generate a delegation for your phone's UCAN identity and send it to the phone. Because the w3up service doesn't need to be "in the loop," you can even add new devices to your account when you're offline, for example, by having your phone scan a QR code from the laptop's screen.

Step-by-step delegation with w3up-cli

Now that we've seen some reasons why you might want to delegate, let's look at delegation in practice using the w3up command line tool.

If you haven't had a chance to check out the w3up tool yet, see our intro to using w3up from the command line to get an overview.

To get started, make sure you have Node.js version 16 or greater installed on your computer. Then enter the following command at a terminal:

npm install -g @web3-storage/w3up-cli

This will install the w3up command, which we'll be using to create two UCAN identities. The first identity will be registered with the w3up service, and we'll delegate access to the second identity so that it can act on behalf of the first.

We'll be using "profiles" to allow the w3up command to manage multiple identities from a single machine. Profiles enable you to have multiple configurations on one machine, each with their own UCAN identities.

The --profile flag is used to select the profile that's active for the current command. With no --profile flag, the default main profile will be used. Using profiles makes it simpler to follow the instructions on a single machine, but the principle is the same if you're delegating to an identity that was generated on a second machine. In that case, you can ignore the --profile flags in the instructions below and use the default profile for each machine that you're using.

Let's go through the process step-by-step.

1. Create your "main" UCAN identity using w3up id.

Use the w3up id command to generate a new UCAN identity. This will print two DIDs to the console. It should look similar to the output below, but yours will have different keys.

✔ Agent DID: did:key:z6Mkinv7ZyMuqg8H3ctSZW76UKwTQxRR7JLwv5yVtxAi3PKC
✔ Account DID: did:key:z6MknNsrjU1Xajz1egt64hnrMWBsYbxkxg6iyiXqUjDYFA8

You might be wondering why there are two IDs and what the difference between them is.

The Account DID acts as a "namespace" for all the content you upload, sort of like the name of a cloud storage bucket. When you upload content with w3up upload, metadata about your uploads like the CID, file size, etc. are associated with the Account DID. Later if you run w3up list, you'll see all the CIDs that have been linked to your account.

When you delegate access to another UCAN identity, you're giving them permission to upload to your account, or in other words, to act as your "agent." When we ran w3up id, we automatically created an "agent" UCAN identity that can act on behalf of the "account" identity. The private key for the Agent is what's used to sign the UCAN request tokens used when we run w3up upload, but the "namespace" for the uploads will still be the Account DID.

Having separate Account and Agent identities may seem like overkill at first, but it will make more sense once we start delegating to other agents in the next few steps.

2. Register your account with w3up register

So far, we've created a UCAN identity for our account, as well as an Agent identity that can upload to the account. Before we can make requests to the w3up service, we need to register our new IDs using the w3up register command.

Run the following command, filling in your own email address:

w3up register your-email@your-provider.net

You should see a message in the console asking you to check your email. Once you click the link in your inbox, you'll see a success message. Now you're ready to start using the rest of the w3up commands.

3. Upload something to your account

Let's upload a test file to confirm that things are working.

First, make a text file called hello.txt with some simple content:

echo "Hello, web3!" > hello.txt

Now you can use w3up upload hello.txt to upload the file. You should see something like this:

✔ Succeeded uploading bagbaieraonu5zsmebv2lvvqu7c24ezniyk4dxilrvtrl4oueo7b46tu2jzua with 200: OK
data CIDs:
 bafybeihs2dyjc6pxxyfpgofsca3l4364yrq3qhd65tos3zqhzuyupuqgp

Running w3up list should now show your new upload:

✔ Listing Uploads...
Date         Data CID                                                     
--------     --------                                                     
10/21/2022   bafybeihs2dyjc6pxxyfpgofsca3l4364yrq3qhd65tos3zqhzuyupuqgpi  

You can also use the HTTP gateway at w3s.link to quickly fetch the content and check that it resolves. With the CID above, the gateway URL would be https://w3s.link/ipfs/bafybeihs2dyjc6pxxyfpgofsca3l4364yrq3qhd65tos3zqhzuyupuqgp.

4. Create a second UCAN identity in a new profile

The w3up tool lets us manage multiple accounts (and their agents), using "profiles" to keep track and switch between them. We'll use profiles to demonstrate delegating permissions from our main UCAN identity to a new one.

Create the new identity using the following command:

w3up id --profile second

The --profile flag tells w3up to use a non-default profile using the name we pass in. In this case, we've named our profile "second," since it's our second identity.

This will print out a new Account DID and Agent DID belonging to the new profile, similar to the output in step one:

✔ Agent DID: did:key:z6MkkydmL5x7956KckmKf8nimRFHMuj78zEQvfsKbXKSYBsV
✔ Account DID: did:key:z6MkgnnMHMZLxjW9BW3xRc7LcBiUxv4nxx2kuACec9E8NFLB

Your keys will be different than the example. We'll be using the Agent DID in the following commands, so take note of it for future reference.

If you run w3up list --profile second, you should see a message indicating that your identity has not yet been registered. Instead of registering this second profile, we'll be using delegation to grant the new agent access to the original account.

5. Delegate access to the new Agent DID

Run the w3up delegate to command, passing in the Agent DID for the second profile:

w3up delegate to did:key:z6MkkydmL5x7956KckmKf8nimRFHMuj78zEQvfsKbXKSYBsV

This will create a file called delegation.car in your current directory. Next, import this delegation to our second profile:

w3up import-delegation delegation.car main-account --profile second

Here we're assigning the name "main-account" to the delegation, so we can keep track of things in case we add more delegations in the future.

If you now run w3up acounts --profile second, you should see two acounts listed:

selected   alias          did                                                       
--------   --------       --------                                                  
           main-account   did:key:z6MkfF49n6r2r6tRiVD5XRQZz78u2acc4RB2ymTSCUyc3aso  
*          self           did:key:z6MkgnnMHMZLxjW9BW3xRc7LcBiUxv4nxx2kuACec9E8NFLB  

The "self" account is the default account for the second profile, and the main-account is name we gave when we made the delegation from our main profile.

Running w3up account without the --profile flag will still show a single account:

selected   alias      did                                                       
--------   --------   --------                                                  
*          self       did:key:z6MkfF49n6r2r6tRiVD5XRQZz78u2acc4RB2ymTSCUyc3aso

You can see that the "self" account for the main profile has the same DID as our main-account delegation to the second profile.

6. Use the delegated account

If we run w3up list --profile second, we'll still see a message that our account isn't registered. This is because that profile is still using the "self" account by default.

Use the w3up switch-account command to make the second profile target the delegated main-account:

w3up switch-account main-account --profile second

You should see something like this, showing the DID of your main account:

now using account: did:key:z6MkfF49n6r2r6tRiVD5XRQZz78u2acc4RB2ymTSCUyc3aso

Now, commands that use the --profile second flag can act upon the delegated main account.

Try running w3up list --profile second. You should see the upload listing for the main account again, just as if you'd run w3up list from the main profile.

You can also try making new uploads using the --profile second flag, and they'll be linked to the main account.

Wrapping up

UCAN delegation is a powerful tool for managing access to your w3up account. Hopefully this post has given you some ideas about how delegation can help you build out new experiences using w3up.

Thanks for following along, and feel free to get in touch if you have any questions!