@@ -165,47 +165,173 @@ public class EncryptExample {
165165<TabItem value = " js" label = " Typescript" >
166166
167167``` typescript
168- import { AuthProviders , NanoTDFClient } from ' @opentdf/sdk' ;
169-
170- // Configuration Options
171- const kasEndpoint = " https://kas.example.com" ;
172-
173- // Authentication options (vary by middleware)
174- const oidcOrigin = " https://idp.example.com" ;
175- const clientId = " applicationNameFromIdP" ;
176- const refreshToken = " userRefreshTokenValueFromIdP" ;
177-
178- // AuthProviders are middlewares that add `Authorization` or other bearer tokens to requests.
179- // These include The `refresh` provider can be handed a refresh and optional access token.
180- const authProvider = await AuthProviders .refreshAuthProvider ({
181- clientId ,
182- exchange: ' refresh' ,
183- refreshToken ,
184- oidcOrigin ,
185- });
186-
187- const client = new TDF3Client ({
188- authProvider ,
189- kasEndpoint ,
190- });
191-
192- // ABAC
193- const attributes = [" http://example.com/attr/classification/value/secret" ]
194-
195- // encrypt
196- const source = new ReadableStream ({
197- pull(controller ) {
198- controller .enqueue (new TextEncoder ().encode (string ));
199- controller .close ();
200- },
201- });
202- const ciphertextStream = await client .encrypt ({ offline: true , source , scope: {attributes } });
203-
204- // decrypt
205- const plaintextStream = await client .decrypt ({
206- source: { type: ' stream' , location: ciphertextStream }
207- });
208- const plaintext = await plaintextStream .toString ();
168+ import { AuthProviders , TDF3Client } from ' @opentdf/sdk' ;
169+ import * as fs from ' fs' ;
170+ import * as path from ' path' ;
171+ import * as os from ' os' ;
172+
173+ async function main() {
174+ try {
175+ console .log (" 🚀 Starting OpenTDF example..." );
176+
177+ // Configuration Options
178+ const platformEndpoint = " http://localhost:8080" ;
179+ const kasEndpoint = platformEndpoint + " /kas" ;
180+ const oidcOrigin = " http://localhost:8888/realms/opentdf" ;
181+ const clientId = " opentdf" ;
182+ const clientSecret = " secret" ;
183+
184+ // Authentication options (vary by middleware)
185+ // For client credentials flow
186+ console .log (" 🔑 Setting up authentication..." );
187+ const authProvider = await AuthProviders .clientSecretAuthProvider ({
188+ clientId ,
189+ clientSecret ,
190+ exchange: ' client' ,
191+ oidcOrigin ,
192+ });
193+ console .log (" ✅ Authentication provider created" );
194+
195+ console .log (" 🔧 Creating TDF3 client..." );
196+ const client = new TDF3Client ({
197+ authProvider ,
198+ kasEndpoint ,
199+ platformUrl: platformEndpoint , // Required for decryption
200+ });
201+ console .log (" ✅ TDF3 client created" );
202+
203+ // ABAC - Attribute-Based Access Control
204+ // Option 1: No attributes (simplest for demonstration)
205+ const attributes: string [] = [];
206+
207+ // Option 2: With attributes (requires proper attribute configuration on platform)
208+ // const attributes = ["http://example.com/attr/classification/value/secret"];
209+ // Note: When using attributes, ensure they are properly configured on the platform
210+ // and the client has appropriate permissions
211+
212+ // Create temporary files
213+ const tempDir = os .tmpdir ();
214+ const inputFile = path .join (tempDir , ' opentdf-input.txt' );
215+ const encryptedFile = path .join (tempDir , ' opentdf-encrypted.tdf' );
216+ const decryptedFile = path .join (tempDir , ' opentdf-decrypted.txt' );
217+
218+ console .log (` 📁 Using temp files: ` );
219+ console .log (` Input: ${inputFile } ` );
220+ console .log (` Encrypted: ${encryptedFile } ` );
221+ console .log (` Decrypted: ${decryptedFile } ` );
222+
223+ // Write input data to temporary file
224+ const inputData = " This is sensitive data that will be encrypted with OpenTDF!" ;
225+ console .log (" 📝 Writing input data to temp file..." );
226+ fs .writeFileSync (inputFile , inputData , ' utf8' );
227+ console .log (` ✅ Input file written: ${inputData } ` );
228+
229+ // Encrypt
230+ console .log (" 🔒 Starting encryption..." );
231+ console .log (" 📖 Reading input file for encryption..." );
232+
233+ // Read the file and create a Web ReadableStream
234+ const inputBuffer = fs .readFileSync (inputFile );
235+ const source = new ReadableStream ({
236+ start(controller ) {
237+ controller .enqueue (inputBuffer );
238+ controller .close ();
239+ }
240+ });
241+
242+ console .log (" 📡 Calling client.encrypt..." );
243+ const ciphertextStream = await client .encrypt ({
244+ offline: true ,
245+ source ,
246+ scope: { attributes }
247+ });
248+
249+ // Save encrypted stream to file
250+ console .log (" 💾 Saving encrypted data to temp file..." );
251+
252+ // Use the toBuffer() method which is available on DecoratedReadableStream
253+ try {
254+ const encryptedBuffer = await (ciphertextStream as any ).toBuffer ();
255+ fs .writeFileSync (encryptedFile , encryptedBuffer );
256+ console .log (" ✅ Used toBuffer() method to save encrypted data" );
257+ } catch (err ) {
258+ console .log (" ❌ toBuffer() failed, trying toString():" , err );
259+
260+ // Fallback to toString method
261+ const encryptedString = await (ciphertextStream as any ).toString ();
262+ const encryptedData = Buffer .from (encryptedString , ' binary' );
263+ fs .writeFileSync (encryptedFile , encryptedData );
264+ console .log (" ✅ Used toString() method to save encrypted data" );
265+ }
266+
267+ console .log (' ✅ Data encrypted and saved to file!' );
268+
269+ // Note: You may see a "Mismatched wrapping key algorithm" warning above.
270+ // This is a known issue where the SDK doesn't specify the algorithm preference
271+ // when requesting keys, but it doesn't prevent successful encryption/decryption.
272+
273+ // Now decrypt the file
274+ console .log (" 🔓 Starting decryption..." );
275+ console .log (" 📖 Reading encrypted file for decryption..." );
276+
277+ // Read the encrypted file as buffer and provide it in the correct format for the SDK
278+ const savedEncryptedData = fs .readFileSync (encryptedFile );
279+
280+ console .log (" 📡 Calling client.decrypt..." );
281+ const plaintextStream = await client .decrypt ({
282+ source: { type: ' buffer' , location: savedEncryptedData }
283+ });
284+
285+ // Save decrypted stream to file
286+ console .log (" 💾 Saving decrypted data to temp file..." );
287+
288+ // Use the toBuffer() method for the plaintext stream as well
289+ try {
290+ const decryptedBuffer = await (plaintextStream as any ).toBuffer ();
291+ fs .writeFileSync (decryptedFile , decryptedBuffer );
292+ console .log (" ✅ Used toBuffer() method to save decrypted data" );
293+ } catch (err ) {
294+ console .log (" ❌ toBuffer() failed, trying toString():" , err );
295+
296+ // Fallback to toString method
297+ const decryptedString = await (plaintextStream as any ).toString ();
298+ const decryptedData = Buffer .from (decryptedString , ' utf8' );
299+ fs .writeFileSync (decryptedFile , decryptedData );
300+ console .log (" ✅ Used toString() method to save decrypted data" );
301+ }
302+
303+ // Read and display the decrypted content
304+ const decryptedContent = fs .readFileSync (decryptedFile , ' utf8' );
305+ console .log (' ✅ Data decrypted and saved to file!' );
306+ console .log (` 📤 Decrypted content: "${decryptedContent }" ` );
307+
308+ // Verify the round-trip worked
309+ if (inputData === decryptedContent ) {
310+ console .log (' 🎉 SUCCESS: Input and decrypted data match perfectly!' );
311+ } else {
312+ console .log (' ❌ ERROR: Input and decrypted data do not match!' );
313+ console .log (` Original: "${inputData }" ` );
314+ console .log (` Decrypted: "${decryptedContent }" ` );
315+ }
316+
317+ // Clean up temporary files
318+ console .log (" 🧹 Cleaning up temporary files..." );
319+ try {
320+ fs .unlinkSync (inputFile );
321+ fs .unlinkSync (encryptedFile );
322+ fs .unlinkSync (decryptedFile );
323+ console .log (" ✅ Temporary files cleaned up" );
324+ } catch (cleanupErr ) {
325+ console .log (" ⚠️ Warning: Could not clean up some temporary files:" , cleanupErr );
326+ }
327+ } catch (err ) {
328+ console .error (' Error in OpenTDF example:' , err );
329+ }
330+ }
331+
332+ // Run the example
333+ main ();
334+
209335```
210336
211337</TabItem >
0 commit comments