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.
Copy 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.
Copy 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.
Copy 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 7 months ago