Skip to content

Instantly share code, notes, and snippets.

@payton
Created December 19, 2022 03:24
Show Gist options
  • Save payton/b6688728bc47f28cf414ed6d92f05474 to your computer and use it in GitHub Desktop.
Save payton/b6688728bc47f28cf414ed6d92f05474 to your computer and use it in GitHub Desktop.
An Explanation of Web3 Authentication

Let's start with some foundations...

  • An Ethereum wallet is a private key and a public key pair.

  • A message is any string of text.

  • A signature is a string of text that is a function of a message and your private key.

  • When you have a message and a signature, you can recover the public key of the private key that created the signature.

Synonmys...

  • public key == Ethereum address
  • (public key, private key) == Ethereum wallet (in the context of this Gist... sometimes Ethereum wallet is used interchangably with Ethereum address)

Sign-In with Ethereum (EIP-4361)

SIWE only defines the format of the message that gets signed. The message is generated by the server and signed by the client. If the server generates the message per SIWE, then the resulting signature will be a secure form of authentication.

  1. Client requests a SIWE message to start authentication flow
  2. Server sends a SIWE message to client
  3. Client signs SIWE message
  4. Client sends resulting signature back to server
  5. Server recovers public key from signature (and original SIWE message) to verify if the recovered public key matches who they intitially said they were
  6. Server sends back something private for that public key

At the end of this flow, the server now knows that the client does indeed own a given Ethereum wallet.

Session Tokens (stateful) and JWTs (stateless)

Both session tokens and JWTs can be used as a form of persistent authentication. However, they are very different. Session tokens are stateful in that they require a session token to be stored in a database for the duration of the session. JWTs are stateless in that the server does not need to "remember" anything about an issued token.

(note: JWTs can be used in a stateful way if you want to be able to "revoke" an existing token... this will hopefully make more sense later)

After steps 1-6, if the client attempts to make a second HTTP request, we can't trust them unless they do the ENTIRE SIWE authentication flow again. That sounds like a whole lot of hooplah. This is where Session Tokens and JWTs come in. After a client sends an HTTP request with their signature, we will return back a Session Token or JWT that they can use for subsequent requests.

Session Tokens

Simply put, a session token is a unique string (password) that the server generates for a client. In the server's database, we create a new row with the session token, an expiration date, and the public key.

Continuing from step 5, the user flow looks like this:

  1. Server recovers public key from signature (and original SIWE message) to verify if the recovered public key matches who they intitially said they were
  2. Server generates session token that's valid for 3 hours
  3. Server stores session token, expiration date, and public key in database
  4. Server returns session token to client
  5. Client makes a new request for private data and includes the session token in their HTTP request
  6. Server checks database for if the session token exists and the expiration date has not passed
  7. If it does, we can send an HTTP response with private data associated with that session token's public key

JWTs are stateless, so we don't need a database. The server will have its own public key and private key. Both of these are kept private to the server (nobody needs to know the server's public key right now).

Instead of returning a session token that's stored in the database, the server will return a signed payload (a JWT). It is a standardized JSON object that contains data AND the signature that attests to the origin of the data along with the fact that it has not been tampered with.

Continuing from step 5, the user flow looks like this:

  1. Server recovers public key from signature (and original SIWE message) to verify if the recovered public key matches who they intitially said they were
  2. Server creates a JWT with its own private key (Instead of storing the expiration date and public key in a database, that information is part of the JWT. Since we signed this data, we can later assert that the data is from our public key and has not been tampered with.)
  3. Server returns JWT to client
  4. Client makes a new request for private data and includes the JWT in their HTTP request
  5. Server validates that the JWT's signature came from the server's public key
  6. If it does, we can send an HTTP response with private data associated with that JWT's public key
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment