Building a Simple Web3-Native File Sharing Portal

A tutorial on how to share static media directly with selected Ethereum addresses.

User avatar



Web3 revolves around key pair ownership (Ethereum account). As a user, you bring your own key pair to use decentralized applications and therefore have total control over your identity. Web2 applications, on the other hand, store user keys and passwords with the application itself.

We've seen many examples of platforms supporting "bring your own keys". A common example is through static media that you can "unlock" by being a member of a DAO or owning a particular NFT. In these cases, the key pair owner needs to sign a message that proves they do indeed own the private key associated with their public key. You can do the same thing with your own static media!

ENS DAO has funded Spruce ³ to deploy and maintain a hosted OIDC provider ⁴ that supports Sign-In with Ethereum ⁵. We will take advantage of this publicly funded service to share our own static media with Ethereum frens.

This tutorial is meant for educational uses only. Code and infrastructure have not been audited for production use.

Problem Statement

"I have a file that I only want to share with my fren who owns wallet 0x..."


  • AWS Account

  • Git

  • (Optional) General knowledge of:

    • AWS (event-driven architecture, S3, CloudFront, Lambda)

    • OpenID Connect ¹

    • widen/cloudfront-auth ²

Authentication is the process of verifying the identity of a user or process.

Authorization is the process of assigning permission or authority to an authenticated user.


We are making heavy use of AWS services. However, keep in mind that most cloud providers offer nearly identical services that can also be utilized. All infrastructure will be contained within a single CloudFormation stack. The user flow is as follows:

  1. Request file from CloudFront Distribution endpoint

  2. CloudFront sends request to Lambda@Edge function

  3. Lambda@Edge function authenticates the user with our OIDC Provider

  4. Once authenticated, the Lambda@Edge function determines if the user is authorized to access the requested file

  5. If they are, we return the file. If they are not, we return an HTTP 401 status code (unauthorized)


  1. Pull the example project from GitHub

    All tutorial files are in my examples repository on GitHub. Pull the latest version of that repository and cd into the directory.

    $ git clone && cd examples
    $ git submodule update --init
    $ cd
  2. Spin Up Infrastructure

    We are going to create the following AWS resources:

    1. CloudFront Distribution for distributing our static media

    2. S3 Bucket for storing our static media

    3. Lambda Function for authenticating and authorizing requests to access our static media

    To quickly spin up your infrastructure, go to CloudFormation in the AWS Console, upload, and follow the prompts (everything can be left default).

    Once your stack creation has been completed, take note of the RedirectURI value in your stack's Outputs tab. We will use this next.

  3. Register and Configure Client with OIDC Provider

    We are using OpenID Connect to authenticate incoming users. Spruce has built siwe-oidc ⁴, and they are running a provider for public usage that is funded by ENS DAO.

    Run the below command to register a new client and take note of the return values:

    $ curl -X POST -H 'Content-Type: application/json' -d '{"redirect_uris": ["<REDIRECT_URI>"]}'

    Response example:


    And GET the client configuration to verify your registration is complete with the expected redirect URI:

    $ curl<CLIENT_ID>

    If you need to update your redirect URI at any time, you can POST updates to the registration client URI:

    $ curl -X POST<CLIENT_ID> -H 'Authorization: Bearer <REGISTRATION_ACCESS_TOKEN>' -H 'Content-Type: application/json' -d '{"redirect_uris": ["<REDIRECT_URI>"]}'

    If you need to delete your client, you can DELETE the registration client URI:

    $ curl -X DELETE<CLIENT_ID> -H 'Authorization: Bearer <REGISTRATION_ACCESS_TOKEN>'
  4. Build, Upload, and Deploy Lambda Function Code

    In the directory, run:

    $ cd cloudfront-auth && ./

    This is going to build our Lambda code that interacts with our OIDC provider to authenticate and authorize incoming requests to view your static media. Follow the prompts for Sign-In with Ethereum. You will be asked for outputs from steps 1 and 2 along with a list of Ethereum addresses that should have access (add your own to the list).

    After the build script finishes, you will have a zip file in the distribution folder. In your CloudFormation Stack resources, go to the Resources tab and select the S3 bucket.

    Upload your distribution's zip file to the bucket root.

    In your CloudFormation Stack, update the existing stack with

    There will be a new parameter for the name of the zip file you uploaded in your S3 bucket's root.

  5. Upload Static Media to Share

    Back in your S3 bucket, create a new folder called cdn and upload any content you want to share.

    For the sake of the tutorial, upload mellon.txt to the cdn folder.

    "Mellon" is the Elvish word for friend, which was the key to entering the Mines of Moria in the Fellowship of the Ring.

  6. Access Your Static Media

    Now, we will try to access your now-protected static media. Find your CloudFront distribution domain name in the Outputs of your CloudFormation stack. Go to that link with your static media appended to the name. For example, if we want to access a file named mellon.txt, we would go to

    Authorized flow:

    Unauthorized flow:

What's next?

You've successfully deployed your own CloudFront distribution that retrieves any static media for a pre-approved set of Ethereum addresses. Congratulations!

A clear next question is, "Well, how do I share file A with address 0x1... and file B with address 0x2... exclusively". That's where a more feature-full authorization layer comes in. We'll go in-depth on this in the next technical tutorial.

Until then, if you have any questions or comments, please reach out to me on any of the following platforms:

Thank you for reading!







  • Loading comments...