Complete Registration

To complete registration you'll need the same header values as used in Initiate Registration. The completeRegistration method is the part of the authentication API that finalizes the user registration process. It takes encrypted user data and a registration challenge, verifies it, and if successful, returns an identifier for the user.

Request Spec

HTTP Method: POST

  • Headers

    • x-scope-id: A UUID string representing the application scope.

    • x-encrypted-key: A string containing the encrypted key for secure communication.

  • Body:

    • encrypted_attestation: A string containing the encrypted attestation object.

    The Initiate Registration flow returns an encrypted_creation_challenge which we need to decrypt with the decryption method below, and then, create an attestation, which we then encrypt and send as part of our request.

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

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

const aesDecrypt = async (
  cipherText: string,
  encryptionKey: BufferSource,
  keyFormat: "raw" = "raw",
  keyLength: number = 256
) => {
  if (!encryptionKey) {
    throw Error("Encryption key not initialized");
  }
  let cryptoKey: CryptoKey;
  let decryptedData: ArrayBuffer;

  try {
    const combined = Uint8Array.from(atob(cipherText), (c) => c.charCodeAt(0));
    const iv = combined.slice(0, 12);
    const encryptedData = combined.slice(12);

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

    decryptedData = await decrypt({ name: "AES-GCM", iv }, cryptoKey, encryptedData);

    const decoder = new TextDecoder();
    return decoder.decode(new Uint8Array(decryptedData));
  } catch (error) {
    console.error("AES-GCM Decryption error:", error);
    throw error;
  }
};
const challenge_id = initRegResponse.result.challenge_id;
const credential_creation_opts = await aesDecrypt(
      initRegResponse.result.encrypted_creation_challenge,
      aesKeyt
 );
const cco_json = JSON.parse(cco_str);

// Once you have the credential creation options you can create a webauthn compatible 
// attestation / signature e.g by using the broswer navigator.credentials.create
// function

const attestation = navigator.credentials.create(cco_json);

You can then encrypt the attestation with your AES Key and send it as part of the request

const encrypted_attestation = await aesEncrypt(JSON.stringify(attestation), aesKey);
  • encrypted_user: A string containing the encrypted user data. This is the same encrypted_user from Initiate Registration

  • challenge_id: A UUID string representing the challenge issued during initiation. This is the challenge_id result from Initiate Registration

Response Spec

  • Success:

    • account_id: A UUID string that uniquely identifies the user's account.

    • identifier_hash: A string representing the hash of the user's identifier.

  • Error: An object containing error details.

Request Example

POST / HTTP/1.1
Host: tiramisu.0xpass.io
Content-Type: application/json
x-scope-id: 123e4567-e89b-12d3-a456-426614174000
x-encrypted-key: jp6...yKg==

{
  "jsonrpc": "2.0",
  "method": "completeRegistration",
  "params": {
    "encrypted_attestation": "JZV...Ug==",
    "encrypted_user": "JZV...Ug==",
    "challenge_id": "765e4567-e89b-12d3-a456-426614174000"
  },
  "id": 1
}

Response Example

HTTP/1.1 200 OK
Content-Type: application/json

{
  "jsonrpc": "2.0",
  "result": {
    "account_id": "456e4567-e89b-12d3-a456-426614174000",
    "identifier_hash": "0x25e...1617"
  },
  "id": 1
}

Last updated