<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Archiv3 by payton</title>
        <link>https://paragraph.com/@payton</link>
        <description>Discussing all things data in Web3.</description>
        <lastBuildDate>Wed, 11 Nov 2026 23:49:04 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <image>
            <title>Archiv3 by payton</title>
            <url>https://storage.googleapis.com/papyrus_images/9d2ebe0341ebf38d97649cfe5ce8a275</url>
            <link>https://paragraph.com/@payton</link>
        </image>
        <copyright>All rights reserved</copyright>
        <item>
            <title><![CDATA[Building a Simple Web3-Native File Sharing Portal]]></title>
            <link>https://paragraph.com/@payton/building-a-simple-web3-native-file-sharing-portal</link>
            <guid>B2au2naRhX4uwbKJ3WvF</guid>
            <pubDate>Sun, 13 Nov 2022 03:43:42 GMT</pubDate>
            <description><![CDATA[A tutorial on how to share static media directly with selected Ethereum addresses. We will utilize AWS services to create a simple portal for serving static media. Users will be authenticated using Sign-In with Etherum and content will only be accessible to authorized users.]]></description>
            <content:encoded><![CDATA[<h3>Motivation</h3><p>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. </p><p>We&apos;ve seen many examples of platforms supporting &quot;bring your own keys&quot;. A common example is through static media that you can &quot;unlock&quot; 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! </p><p>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.</p><p>This tutorial is meant for educational uses only. Code and infrastructure have not been audited for production use.</p><h3>Problem Statement</h3><p>&quot;I have a file that I only want to share with my fren who owns wallet 0x...&quot;</p><h3>Prerequisites</h3><ul><li><p>AWS Account</p></li><li><p>Git</p></li><li><p>(Optional) General knowledge of:</p><ul><li><p>AWS (event-driven architecture, S3, CloudFront, Lambda)</p></li><li><p>OpenID Connect ¹</p></li><li><p>widen/cloudfront-auth ²</p></li></ul></li></ul><div><div class="callout-base callout-info" data-node-view-wrapper="" style="white-space:normal"><img src="https://paragraph.xyz/editor/callout/information-icon.png" class="callout-button"/><div class="callout-content"><div><p><strong>Authentication</strong> is the process of verifying the identity of a user or process.</p><p><strong>Authorization</strong> is the process of assigning permission or authority to an authenticated user.</p></div></div></div></div><h3>Architecture</h3><p>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:</p><ol><li><p>Request file from CloudFront Distribution endpoint</p></li><li><p>CloudFront sends request to Lambda@Edge function</p></li><li><p>Lambda@Edge function authenticates the user with our OIDC Provider</p></li><li><p>Once authenticated, the Lambda@Edge function determines if the user is authorized to access the requested file</p></li><li><p>If they are, we return the file. If they are not, we return an HTTP 401 status code (unauthorized)</p></li></ol><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/efda073b591aa8ba1a25076f4fd3721e.png" blurDataURL="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAACCAIAAADwyuo0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAJUlEQVR4nAEaAOX/AP7+/vDw76ShniYeFADx8fHc29vY2Nfx8fHvqBLE0R1H/gAAAABJRU5ErkJggg==" width="1726" float="none" class="image-node img-center embed"><h3>Tutorial</h3><ol><li><p><strong>Pull the example project from GitHub</strong></p><p>All tutorial files are in my <a target="_blank" rel="noopener noreferrer nofollow" class="dont-break-out dont-break-out" href="https://github.com/payton/examples">examples repository on GitHub</a>. Pull the latest version of that repository and cd into the <code>paragraph.xyz/web3-file-sharing-example</code> directory.</p><pre class="dont-break-out text-sm md:text-base"><code>$ git clone git@github.com:payton/examples.git &amp;&amp; cd examples
$ git submodule update --init paragraph.xyz/web3-file-sharing-example/cloudfront-auth
$ cd paragraph.xyz/web3-file-sharing-example</code></pre></li><li><p><strong>Spin Up Infrastructure</strong></p><p>We are going to create the following AWS resources:</p><ol><li><p>CloudFront Distribution for distributing our static media</p></li><li><p>S3 Bucket for storing our static media</p></li><li><p>Lambda Function for authenticating and authorizing requests to access our static media</p></li></ol><p>To quickly spin up your infrastructure, go to <a target="_blank" rel="noopener noreferrer nofollow" class="dont-break-out dont-break-out" href="https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/new?stackName=web3-file-sharing-example&amp;templateURL=">CloudFormation in the AWS Console</a>, upload <code>paragraph.xyz/web3-file-sharing-example/stack-step-1.yaml</code>, and follow the prompts (everything can be left default). </p><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/ebf95be87b115330e2915d78325553fd.png" blurDataURL="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAABCAIAAAB2XpiaAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAFUlEQVR4nGOITUyytLJas3bt6bNnASC0BjnlIcLKAAAAAElFTkSuQmCC" width="1152" float="none" class="image-node img-center embed"><p>Once your stack creation has been completed, take note of the <code>RedirectURI</code> value in your stack&apos;s <code>Outputs</code> tab. We will use this next.</p><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/9f48a0a9bd35f53c1acd349f472146d5.png" blurDataURL="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAABCAIAAAB2XpiaAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAFUlEQVR4nGNw8/QJDglZs3btufMXACA8BkoZmFrsAAAAAElFTkSuQmCC" width="1155" float="none" class="image-node img-center embed"></li><li><p><strong>Register and Configure Client with OIDC Provider</strong></p><p>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.</p><p>Run the below command to register a new client and take note of the return values:</p><pre class="dont-break-out text-sm md:text-base"><code>$ curl -X POST https://oidc.signinwithethereum.org/register -H &apos;Content-Type: application/json&apos; -d &apos;{&quot;redirect_uris&quot;: [&quot;&lt;REDIRECT_URI&gt;&quot;]}&apos;</code></pre><p>Response example:</p><pre class="dont-break-out text-sm md:text-base"><code>{
    &quot;client_id&quot;:&quot;&lt;CLIENT_ID&gt;&quot;,
    &quot;client_secret&quot;:&quot;&lt;CLIENT_SECRET&gt;&quot;,
    &quot;registration_access_token&quot;:&quot;&lt;REGISTRATION_ACCESS_CODE&gt;&quot;,
    &quot;registration_client_uri&quot;:&quot;https://oidc.signinwithethereum.org/client/&lt;CLIENT_ID&gt;&quot;,
    &quot;redirect_uris&quot;:[&quot;&lt;REDIRECT_URI&gt;&quot;]
}</code></pre><p>And GET the client configuration to verify your registration is complete with the expected redirect URI:</p><pre class="dont-break-out text-sm md:text-base"><code>$ curl http://oidc.signinwithethereum.org/client/&lt;CLIENT_ID&gt;</code></pre><p>If you need to update your redirect URI at any time, you can POST updates to the registration client URI:</p><pre class="dont-break-out text-sm md:text-base"><code>$ curl -X POST http://oidc.signinwithethereum.org/client/&lt;CLIENT_ID&gt; -H &apos;Authorization: Bearer &lt;REGISTRATION_ACCESS_TOKEN&gt;&apos; -H &apos;Content-Type: application/json&apos; -d &apos;{&quot;redirect_uris&quot;: [&quot;&lt;REDIRECT_URI&gt;&quot;]}&apos;</code></pre><p>If you need to delete your client, you can DELETE the registration client URI:</p><pre class="dont-break-out text-sm md:text-base"><code>$ curl -X DELETE http://oidc.signinwithethereum.org/client/&lt;CLIENT_ID&gt; -H &apos;Authorization: Bearer &lt;REGISTRATION_ACCESS_TOKEN&gt;&apos;</code></pre></li><li><p><strong>Build, Upload, and Deploy Lambda Function Code</strong></p><p>In the <code>paragraph.xyz/web3-file-sharing-example</code> directory, run:</p><pre class="dont-break-out text-sm md:text-base"><code>$ cd cloudfront-auth &amp;&amp; ./build.sh</code></pre><p>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). </p><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/afff1da2e3e1b1223bf6a9903f18b0a8.png" blurDataURL="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAABCAIAAAB2XpiaAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAFUlEQVR4nGP4/+x/oWNSoWOSObsGAC7WBXMdRUPTAAAAAElFTkSuQmCC" width="1362" float="none" class="image-node img-center embed"><p>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. </p><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/f039aa0fcb6ba1471f2faa571d39b704.png" blurDataURL="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAABCAIAAAB2XpiaAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAFUlEQVR4nGNw8/QJDgnpnzDxzdu3AB9ABk2eM82FAAAAAElFTkSuQmCC" width="100%" float="none" class="image-node img-center embed"><p>Upload your distribution&apos;s zip file to the bucket root.</p><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/8f05035ac7be5777512705ad5a7170dc.png" blurDataURL="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAABCAIAAAB2XpiaAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAFUlEQVR4nGPg4ORqam45fOjQqRPHAR2TBkWEjkHOAAAAAElFTkSuQmCC" width="1268" float="none" class="image-node img-center embed"><p>In your CloudFormation Stack, update the existing stack with <code>paragraph.xyz/web3-file-sharing-example/stack-step-2.yaml</code>. </p><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/50375e51bbe16f6fa486f86138e83bd4.png" blurDataURL="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAACCAIAAADwyuo0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAJUlEQVR4nAEaAOX/AB4fIHt8fKSnp5aYmQBfX1+4ubnp6ur19vaV5A5v5QS0KQAAAABJRU5ErkJggg==" width="989" float="none" class="image-node img-center embed"><p>There will be a new parameter for the name of the zip file you uploaded in your S3 bucket&apos;s root. </p><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/d43a3065981e7c7ab4a52d1e1d122ca2.png" blurDataURL="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAABCAIAAAB2XpiaAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAFUlEQVR4nGPQMzAwNDKaPXv2uQsXABi7BWO+ZKbqAAAAAElFTkSuQmCC" width="100%" float="none" class="image-node img-center embed"></li><li><p><strong>Upload Static Media to Share</strong></p><p>Back in your S3 bucket, create a new folder called <code>cdn</code> and upload any content you want to share. </p><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/454f894d8c88ff134e2e4ce0bc2100b9.png" blurDataURL="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAIAAAAmkwkpAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAP0lEQVR4nAE0AMv/AAQLDyktMUxRVHh7fQBla25dYmRxd3qcoKQAgIOGwsjL3ODh4+XnAIiMja2vr62vr2xlYBLvGLzNl3mYAAAAAElFTkSuQmCC" width="576" float="none" class="image-node img-center embed"><p>For the sake of the tutorial, upload <code>mellon.txt</code> to the <code>cdn</code> folder.</p><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/adc7a9cca6f366257f05449f61809ae3.png" blurDataURL="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAACCAIAAADwyuo0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAJUlEQVR4nAEaAOX/ADk7O42PkKipqZWVlACoqanx8vLCwsR7eHWr7w6TDx6YvAAAAABJRU5ErkJggg==" width="100%" float="none" class="image-node img-center embed"><div><div class="callout-base callout-info" data-node-view-wrapper="" style="white-space:normal"><img src="https://paragraph.xyz/editor/callout/information-icon.png" class="callout-button"/><div class="callout-content"><div><p>&quot;Mellon&quot; is the Elvish word for friend, which was the key to entering the Mines of Moria in the Fellowship of the Ring.</p></div></div></div></div></li><li><p><strong>Access Your Static Media</strong></p><p>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 <code>mellon.txt</code>, we would go to <code>https://abc123.cloudfront.net/mellon.txt</code>.</p><p>Authorized flow:</p><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/84122f8726a152d0fd87a03e072b144d.gif" blurDataURL="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAACXBIWXMAAAPoAAAD6AG1e1JrAAAAMklEQVR4nAEnANj/AOfs5vb6/tzr8+P4/QAjNTN9lI8ASWUAQ1UAAA0MVmNaHFJiVsrIwLASj/aZ11QAAAAASUVORK5CYII=" width="781" float="none" class="image-node img-center embed"><p>Unauthorized flow:</p><img src="https://img.paragraph.com/cdn-cgi/image/format=auto/https://storage.googleapis.com/papyrus_images/f9e5585132869fa4a91efa978315ddfb.gif" blurDataURL="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAIAAAA7ljmRAAAACXBIWXMAAAPoAAAD6AG1e1JrAAAAMklEQVR4nAEnANj/AOfs5vb6/tzr8+P4/QAjNTN9lI8ASWUAQ1UAAA0MVmNaHFJiVsrIwLASj/aZ11QAAAAASUVORK5CYII=" width="764" float="none" class="image-node img-center embed"></li></ol><h3>What&apos;s next?</h3><p>You&apos;ve successfully deployed your own CloudFront distribution that retrieves any static media for a pre-approved set of Ethereum addresses. Congratulations!</p><p>A clear next question is, &quot;Well, how do I share file A with address 0x1... and file B with address 0x2... exclusively&quot;. That&apos;s where a more feature-full authorization layer comes in. We&apos;ll go in-depth on this in the next technical tutorial.</p><p>Until then, if you have any questions or comments, please reach out to me on any of the following platforms:</p><ul><li><p>Farcaster @payton</p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow" class="dont-break-out dont-break-out" href="https://twitter.com/0xpayton">Twitter @0xpayton</a></p></li><li><p>Other platform links at <a target="_blank" rel="noopener noreferrer nofollow" class="dont-break-out dont-break-out" href="http://nf.td/payton">nf.td/payton</a></p></li></ul><p>Thank you for reading!</p><h3>Appendix</h3><ol><li><p><a target="_blank" rel="noopener noreferrer nofollow" class="dont-break-out dont-break-out" href="https://openid.net/specs/openid-connect-core-1_0.html">https://openid.net/specs/openid-connect-core-1_0.html</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow" class="dont-break-out dont-break-out" href="https://engineering.widen.com/blog/AWS-CloudFront-User-Authentication-using-Lambda@Edge/">https://engineering.widen.com/blog/AWS-CloudFront-User-Authentication-using-Lambda@Edge/</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow" class="dont-break-out dont-break-out" href="https://snapshot.org/#/ens.eth/proposal/0x104eb11d42813fadc2b408856e8fa2c10e34dbb4a87abaa2f089ece124263f16">https://snapshot.org/#/ens.eth/proposal/0x104eb11d42813fadc2b408856e8fa2c10e34dbb4a87abaa2f089ece124263f16</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow" class="dont-break-out dont-break-out" href="https://docs.login.xyz/servers/oidc-provider/hosted-oidc-provider">https://docs.login.xyz/servers/oidc-provider/hosted-oidc-provider</a></p></li><li><p><a target="_blank" rel="noopener noreferrer nofollow" class="dont-break-out dont-break-out" href="https://eips.ethereum.org/EIPS/eip-4361">https://eips.ethereum.org/EIPS/eip-4361</a></p></li></ol><p></p>]]></content:encoded>
            <author>payton@newsletter.paragraph.com (payton)</author>
            <category>web3</category>
            <category>aws</category>
            <category>identity</category>
            <category>lambda@edge</category>
            <category>cloudfront</category>
            <category>data</category>
            <category>siwe</category>
        </item>
    </channel>
</rss>