Unauthenticated Requests

To carry out an unauthenticated request, 3 headers are required, X-Scope-Id and X-Encrypted-Key and X-Encrypted-User .

X-Scope-Id is the ID of the application's scope under which the user's account is being registered or authenticated. You can read more about Configuring your scope. Once you've set up your scope and have the ID you can simply past it in the request header here.

To setup an X-Encrypted-Key you need to generate a random AES Key, and then RSA encrypt it with the secure enclaves public key, we'll use TypeScript for the example below.

Here we setup a cryptoObj that can work in both browser and server environments and then a function to generate an AES Key.

const cryptoObj = typeof window !== "undefined" ? window.crypto : crypto;

export const generateAesKey = async () => {
  const cryptoKey = await cryptoObj.subtle.generateKey(
    {
      name: "AES-GCM",
      length: 256,
    },
    true,
    ["encrypt", "decrypt"]
  );
  return await cryptoObj.subtle.exportKey("raw", cryptoKey);
};

Next we setup two function an encrypt function, which we then use in our rsaEncrypt function.

const encrypt = async (algo, key, data) => {
  try {
    const encryptedResult = await cryptoObj.subtle.encrypt(algo, key, data);
    return encryptedResult;
  } catch (error) {
    console.error("Encryption error:", error);
    throw error;
  }
};

const rsaEncrypt = async (
  plainText: string,
  encryptionKey: BufferSource,
  keyFormat: "spki" = "spki",
  hashName: "SHA-256" = "SHA-256"
) => {
  if (!encryptionKey) {
    throw Error("Encryption key not initialized");
  }

  const encoder = new TextEncoder();
  const data = encoder.encode(plainText);

  let cryptoKey: CryptoKey;
  let encrypted: ArrayBuffer;

  try {
    cryptoKey = await importKey(
      keyFormat,
      encryptionKey,
      { name: "RSA-OAEP", hash: { name: hashName } },
      ["encrypt"]
    );
    encrypted = await encrypt({ name: "RSA-OAEP" }, cryptoKey, data);
    return btoa(String.fromCharCode.apply(null, new Uint8Array(encrypted)));
  } catch (error) {
    console.error("RSA-OAEP Encryption error:", error);
    throw error;
  }
};


export const aesEncrypt = async (
  plainText: string,
  encryptionKey: BufferSource,
  keyFormat: "raw" = "raw",
  keyLength: number = 256
) => {
  if (!encryptionKey) {
    throw Error("Encryption key not initialized");
  }

  const encoder = new TextEncoder();
  const data = encoder.encode(plainText);
  let cryptoKey: CryptoKey;
  let encrypted: ArrayBuffer;

  try {
    cryptoKey = await importKey(keyFormat, encryptionKey, { name: "AES-GCM", length: keyLength }, [
      "encrypt",
    ]);

    const iv = cryptoObj.getRandomValues(new Uint8Array(12)); // Initialization vector

    encrypted = await encrypt({ name: "AES-GCM", iv }, cryptoKey, data);

    const combined = new Uint8Array(iv.length + encrypted.byteLength);
    combined.set(iv, 0);
    combined.set(new Uint8Array(encrypted), iv.length);

    return btoa(String.fromCharCode.apply(null, combined));
  } catch (error) {
    console.error("AES-GCM Encryption error:", error);
    throw error;
  }
};

With these function setup, we are able to generate our AES key, and then RSA Encrypt it using the public key of the secure enclave.

const enclavePublicKey = 
  "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvQOa1gkatuN6KjaS4KEWsVZAN9i4Cf0j9jlmBW5RwCJ3Bxo32McP7axt4Ev6sMWM24lpCgXgu68S9KBYRcrcEB6dRcaupFGd+ER7M518fiJ0VtCZ+XRnmwn9fqEvotp9DPZOysJkUQ60kugCRKwNvfZzAFcDiubwiqsUY2sCm943a/u9Hym51SEetG+ZFPJZFOBqwRSGkOgGZ+9Ac7ITE+bWLCZk9DlzRu+BIoDOFzXZIn+/0a0X8BnLtRY4g50aew4J+4OllQagBbhYnPMvYExYIEUx6bdjQicw0Js6s2pHr+SFAX23kQtbVOVxb5+KEGp1d+6Q4Gx7FBoyWI5qPQIDAQAB";

const aesKey = await generateAesKey();
const aesKeyBytes = new Uint8Array(aesKey);
const aesKeyString = btoa(String.fromCharCode(...aesKeyBytes));
const encryptedAesKey = await rsaEncrypt(aesKeyString, enclavePublicKey);
const userDetails = {
    username: "john_doe",
    userDisplayName: "john_doe_crypto",
};
const encryptedUser = await aesEncrypt(JSON.stringify(userDetails), aesKey);

Now you have your encryptedAesKey and encryptedUser you can use this value as the value in the request header as X-Encrypted-Key . With X-Scope-Id, X-Encrypted-User and X-Encrypted-Key setup, you can now start interacting with Passport API to register and authenticate users.

Last updated