ADR-44: Authentication mechanism for HTTP requests: Signed Fetch

More details about this document
Latest published version:
https://adr.decentraland.org/adr/ADR-44
Authors:
2fd
nachomazzara
cazala
Feedback:
GitHub decentraland/adr (pull requests, new issue, open issues)
Edit this documentation:
GitHub View commits View commits on githistory.xyz

Abstract

This document presents the problem of authenticating requests in http servers at Decentraland. It presents two alternatives: Ephemeral keys or Private keys. Ephemeral keys are decided to improve the user experience to not require N interactions on the wallet.

Context and Problem Statement

We want to integrate our dApps with the explorer in order to provide a better experience for our users. To do so we need to send request authenticated by the user's wallet.

Considered options

Alt 1

Ask the user to sign each request

Pros

Cons

Alt 2

Use decentraland-crypto as intermediary to sign each request

Pros

Cons

Decision

The option to use decentraland-crypto to interact with our services was chosen in order to provide a better experience to our users

How to sign a request

Generate a payload

In order to minimize the reutilization of request each payload must include data about the request and the moment it is made

/**
 * Request method
 */
const METHOD: "GET" | "POST" | "PUT" | "PATCH" | "DELETE" = `POST`

/**
 * request path without domain, query string or hash
 * @example
 * - `/ping`
 * - `new URL('https://decentraland.org/ping').pathname`
 */
const PATH: string = `/ping`

/**
 * request timestamp
 * each service decide how long it will consider valid a request using this timestamp,
 * if timestamp is greater than now the request should fail
 */
const TIMESTAMP: number = Date.now()

/**
 * request metadata
 * can include extra data about the service that is making the request
 * shouldn't contains data required to make the request, if it is empty
 * and empty object should be use
 */
const METADATA: string = JSON.stringify({
  /* extra data */
})

/**
 * Payload
 */
const payload = [METHOD, PATH, TIMESTAMP, METADATA].join(":").toLowerCase()

Sign the payload

Once you have an Identity from decentraland-crypto use signPayload to get an AuthLink

import { Authenticator } from "decentraland-crypto"

const auth = await Authenticator.signPayload(identity, data)

Sign the request

To sign a request the AuthChain should be included as a header in the request along with the timestamp and the metadata

fetch("https://decentraland.org/ping", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-Identity-Auth-Chain-0": JSON.stringify(auth[0]),
    "X-Identity-Auth-Chain-1": JSON.stringify(auth[1]),
    "X-Identity-Auth-Chain-2": JSON.stringify(auth[2]),
    "X-Identity-Timestamp": TIMESTAMP,
    "X-Identity-Metadata": METADATA
  },
  body: JSON.stringify({})
})

License

Copyright and related rights waived via CC0-1.0. Living