Keypair Authentication

Engine supports keypair authentication allowing your app to generate short-lived access tokens.

Use cases

  • You don't want an access token to be reused for a long duration if shared or compromised.
  • You want to restrict what each access token can do (e.g. specific calls).

Usage

1. Generate a keypair

Generate a cryptographic keypair in your terminal with openssl.

These commands generate a private and public key with the ES256 algorithm.

openssl ecparam -name prime256v1 -genkey -noout -out private.key
openssl ec -in private.key -pubout -out public.key

2. Add your public key to Engine

  • Navigate to the Engine dashboard.
  • Select Access Tokens.
  • Select Keypair Authentication.
    • Note: If this option is unavailable, your Engine instance may not be configured to support keypairs.
  • Select Add Public Key.
  • Add your public key including the ----- boundary lines.

3. Sign a JWT with your private key

This step must be done for each request.

import jsonwebtoken from "jsonwebtoken";
const payload = {
iss: publicKey,
};
const accessToken = jsonwebtoken.sign(payload, privateKey, {
algorithm: "ES256", // The keypair algorithm
expiresIn: "15s", // Invalidate after 15 seconds
});

4. Authenticate Engine requests

Provide the access token in the Authorization header.

await fetch(`${engineBaseUrl}/backend-wallet/get-all`, {
headers: {
authorization: `Bearer ${accessToken}`,
},
});

Restrict the payload body (Advanced)

To ensure this access token can only execute a specific payload, provide a SHA256 hash of the payload body as the bodyHash argument of the signed object.

Example: This access token is restricted to transfer 0.1 MATIC on Polygon to 0xE68FFAE106cc68A0e36Ba9Fd86f27337E3a71da6.

import { createHash } from "crypto";
import jsonwebtoken from "jsonwebtoken";
// Prepare the request payload body.
const body = JSON.stringify({
to: "0xE68FFAE106cc68A0e36Ba9Fd86f27337E3a71da6",
currencyAddress: "0x0000000000000000000000000000000000000000",
amount: "0.1",
});
// Add a hash of `body` to the signed payload.
const payload = {
iss: publicKey,
bodyHash: createHash("sha256").update(body).digest("hex"),
};
const accessToken = jsonwebtoken.sign(payload, privateKey, {
algorithm: "ES256",
});
// Call Engine with `body`.
await fetch(`${engineBaseUrl}/backend-wallet/137/transfer`, {
headers: {
"Content-Type": "application/json",
authorization: `Bearer ${accessToken}`,
"x-backend-wallet-address": "<engine_backend_wallet_address>",
},
body,
});

FAQ

How do I enable this feature on my self-hosted Engine?

Set the environment variable ENABLE_KEYPAIR_AUTH="true" and restart your Engine.

Which cryptographic algorithms are supported?

The following algorithms can be used.

algorithmDescription
RS256RSASSA-PKCS1-v1_5 using SHA-256 hash algorithm
PS256RSASSA-PSS using SHA-256 hash algorithm
ES256ECDSA using P-256 curve and SHA-256 hash algorithm

Refer to the OpenSSL documentation on generating keypairs for different algorithms.

Remember to change algorithm in the jsonwebtoken.sign() call on Step 3. Sign a JWT with your private key.