Complete Authentication

To complete registration you'll need the same header values as used in Initiate Authentication

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.

    • X-Encrypted-User: The user input encrypted using the encryption key

  • Body:

    • encrypted_assertion: A string containing the encrypted user assertion data.

To create an encrypted_assertion, first have to decrypt the encrypted_request_challenge from the response in Initiate Authentication, with the following functions.

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 request_challenge_str = await aesDecrypt(
      initAuthResponse.result.encrypted_request_challenge,
      aesKey
);

const request_challenge_json = JSON.parse(request_challenge_str) 

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

const assertion = navigator.credentials.get(request_challenge_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(assertion), aesKey);
  • challenge_id: A UUID string representing the unique challenge for this authentication request, returned from Initiate Authentication

Encrypt user for headers.

const params = {
    username: "test_user",
    userDisplayName: "test_user"
}

// Same aesKey created with `generateAesKey`
const encrypted_user = await aesEncrypt(JSON.stringify(params), aesKey);

Response Spec

  • Body:

    • encrypted_jwt: A string containing the encrypted JSON Web Token (JWT) if authentication is successful.

Request Example

POST / HTTP/1.1
Host: https://tiramisu.0xpass.i
Content-Type: application/json
X-Scope-Id: 123e4567-e89b-12d3-a456-426614174000
X-Encrypted-Key: jp6t2GVOvzltN+4VGc21ZKPIbLjEvitE34cFYDvVNrcmF2ukcKMTO8R/F0wbonGZM0NZBg2X94FvirH6Hi2U1zFlXN5srkOdvQL3lVNZ86gbfEtJFPOEAeZkxtTOKOsH4ZXPtUbFOjT2Niblo8njOKibOoAMRKIhtsNTTvRXjHRxnNqVs3QcSe7XbO1DbH/pdRgq+YZN13znlSRsupu4G/h/KBEZr98wXFo8PeDV9F8ZV56F90GqQ3wKzFUBwC9rJihGz0omH+eJA0jB/K7BYt30fhWDnqaLNP2eb1mbIjBCmv6sXqu2jtghr3ejl0YwjP9lCO+aVD7bophfb/IyKg==
X-Encrypted-User: JZVjZw33OGoQDEMcbOdckx4TzspQEKP5j+iAGqf6b6gPleziY/Noyd4uW6KMSujq0HKP2Rb69p9Wi8ic5O8LZl9oTmmWk4op0CUKejqcV5DsNDp83PYzUg==
{
  "jsonrpc": "2.0",
  "method": "completeAuthentication",
  "params": {
    "encrypted_assertion": "JZVjZw33OGoQDEMcbOdckx4TzspQEKP5j+iAGqf6b6gPleziY/Noyd4uW6KMSujq0HKP2Rb69p9Wi8ic5O8LZl9oTmmWk4op0CUKejqcV5DsNDp83PYzUg==",
    "challenge_id": "123e4567-e89b-12d3-a456-426614174000",
  },
  "id": 1
}

Response Example

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

{
  "jsonrpc": "2.0",
  "result": {
    "encrypted_jwt": "JZVjZw33OGoQDEMcbOdckx4TzspQEKP5j+iAGqf6b6gPleziY/Noyd4uW6KMSujq0HKP2Rb69p9Wi8ic5O8LZl9oTmmWk4op0CUKejqcV5DsNDp83PYzUg=="
  },
  "id": 1
}

Last updated