Duration Skill: Encryption Require: Node.js 8+ Skill: React Browsers: Chrome, Firefox, Safari, Edge or IE 11

This tutorial covers getting up and running with Tanker FileKit.

After having completed this tutorial, you will know:

  • How to install the FileKit SDK inside a JS application
  • How to encrypt, upload, and share files with a recipient
  • How to download and decrypt the shared files
  • The basics of using end-to-end encryption in JS

1. Tutorial Assumptions

This tutorial is designed for developers who want to get started with Tanker's end-to-end encryption SDKs from scratch. It does not assume that you have any prior experience with Tanker.

This tutorial uses diff notations to highlight the code changes that need to be made in each step, e.g.

File: src/File.js
const previousCode = 'my code; // Code to remove const newCode = 'my new code'; // Code to add const unchangedCode = 'unchanged';

2. What is Tanker?

Tanker is the go-to solution to protect sensitive data in your applications and in the cloud, featuring:

  • an open-source encryption SDK for JavaScript, iOS, and Android
  • a secure hosted service that powers crypto key exchanges and identity verification

If you want to learn more about the FileKit SDK used in this tutorial, head over to the FAQ section.

That said, let the fun begin!

3. Create a React application

Note

You're about to create a boilerplate React application. However, Tanker is not tied to React and can be used in any kind of JS project.

To be able to use the create-react-app command-line tool below, please verify that you have Node.js 8.16.0 or 10.16.0 or later version on your development machine.

When ready, create a React application named filetransfer:

npm add --global create-react-app
create-react-app filetransfer

This will configure a default React application. To start the app, simply type:

cd filetransfer
npm run start

To get the same look and feel as the demo application, link this css stylesheet in public/index.html:

File: public/index.html
<head> <!-- ... --> <link href="https://tankerhq.github.io/filekit-tuto-app/static/css/main.6bf92edb.chunk.css" rel="stylesheet" /> </head>

4. Prepare the App Component

Replace the default content of src/App.js by this brand new React Component:

File: src/App.js
import React from 'react'; class App extends React.Component { constructor(props){ super(props); this.state = { ready: false }; } async componentDidMount() { this.setState({ ready: true }); } render() { const { ready } = this.state; if (!ready) return <section><p>Loading...</p></section>; return <section><p>Ready!</p></section>; } } export default App;

The App state only contains a ready boolean for now... but this component will soon prove useful in handling the state and the asynchronous initialization of FileKit.

5. Set up FileKit

Add the dependencies

Add the @tanker/filekit package to your application dependencies:

npm add @tanker/filekit

Add the @tanker/fake-authentication package too:

npm add @tanker/fake-authentication

FileKit identifies users before allowing them to do cryptographic operations. To this end, we will need to provide Tanker identities. They are similar to authentication tokens, but with some differences allowing to encrypt data for users.

The purpose of the @tanker/fake-authentication package is to simulate the presence of a backend service that can distribute these identities to the users of your demo file transfer application.

Note

Tanker identities contain information allowing users to access their encrypted data. While they do not grant access to any data by themselves, they should be managed securely on your server in a real application.

Do not use the @tanker/fake-authentication package in production. Instead, please consult the guide on user management.

Get an appId

Warning

You will need a Tanker account to follow the rest of the tutorial. If you don't have one, please sign up on dashboard.tanker.io

To initialize both FileKit and FakeAuthentication, you will need an appId. To get one, create a new application named FileTransfer in the Tanker dashboard and copy the generated appId.

Initialize FileKit and FakeAuthentication in the App's constructor, using the created appId:

File: src/App.js
import React from 'react'; import FileKit from '@tanker/filekit'; import FakeAuthentication from '@tanker/fake-authentication'; const appId = 'your App ID'; class App extends React.Component { constructor(props) { super(props); this.fileKit = new FileKit({ appId }); this.fakeAuth = new FakeAuthentication({ appId }); this.state = { ready: false }; } // ... }

6. Start a disposable session

In the scope of this tutorial, the sender of the files is not authenticated, only the receiver will be. To start a one-time, unauthenticated FileKit session, we will use fileKit.startDisposableSession(). We will use this session to encrypt and upload the file.

Generate a Tanker identity using the FakeAuthentication instance and use this identity to call fileKit.startDisposableSession() when the component mounts:

File: src/App.js
import React from 'react'; import FileKit from '@tanker/filekit'; import FakeAuthentication from '@tanker/fake-authentication'; const appId = 'your App ID'; class App extends React.Component { // ... async componentDidMount() { const identity = await this.fakeAuth.getIdentity(); await this.fileKit.startDisposableSession(identity); this.setState({ ready: true }); } // ... }

7. Create the upload form

To upload files, we will create a form in a new component named Upload.

First, create a new file named Upload.js in the src folder.

This component will display a form with the following elements:

  • an input to select a file from the disk
  • an input to specify the email address of a recipient
  • a submit button, entitled Create a secure link

Add the following code to src/Upload.js:

File: src/Upload.js
import React from 'react'; class Upload extends React.Component { constructor(props) { super(props); this.state = { recipient: "", file: null }; } updateFiles(event) { // only taking the first file for simplicity this.setState({ file: event.target.files[0] }); } updateRecipient(event) { this.setState({ recipient: event.target.value }); } onUpload = async (event) => { // TODO: encrypt and upload the file } render() { const { file, recipient } = this.state; return ( <form onSubmit={this.onUpload}> <h1>FileKit tutorial app</h1> <div> <input id="upload-field" type="file" required onChange={e => this.updateFiles(e)} /> <label htmlFor="upload-field"> {file ? file.name : 'Select a file to send'} </label> </div> <input id="recipient-email-field" type="email" placeholder="Enter the recipient email" required value={recipient} onChange={e => this.updateRecipient(e)} name="recipient" /> <button id="send-button" type="submit" disabled={!file || !recipient}> Create a secure link </button> </form> ); } } export default Upload;

Render the Upload component inside the App.render() method to display it:

File: src/App.js
import FileKit from '@tanker/filekit'; import FakeAuthentication from '@tanker/fake-authentication'; import Upload from './Upload'; const appId = 'your App ID'; class App extends React.Component { // ... render() { const { ready } = this.state; if (!ready) return <section><p>Loading...</p></section>; return <section><p>Ready!</p></section>; return <section><Upload fileKit={this.fileKit} fakeAuth={this.fakeAuth} /></section>; } }

We're now able to select a file and fill in a recipient's email address, but the submit button does not trigger any action yet.

8. Encrypt, and upload files

To encrypt and upload the selected file to FileKit's storage, call fileKit.upload() with the selected File object from the state:

File: src/Upload.js
class Upload extends React.Component { // ... onUpload = async (event) => { // TODO: encrypt and upload the file event.preventDefault(); const { fakeAuth, fileKit } = this.props; const { file, recipient } = this.state; const fileId = await fileKit.upload(file); } // ... }

And that's all you need to securely encrypt and upload a file!

Under the hood, the file is encrypted and streamed to the cloud storage in a memory-efficient manner. By default, only the user who uploaded the file would be authorized to download and decrypt it. This is not enough for a file transfer application!

9. Share encrypted files

To share the encrypted file with the recipient, FileKit needs to identify them. This is done using Tanker public identities. Public identities identify users (or future users) and do not contain any sensitive information.

To generate public identities using the fake authentication, just call fakeAuth.getPublicIdentities(). Then, call fileKit.share() with the fileId from filekit.upload() and the generated public identity:

File: src/Upload.js
onUpload = async (event) => { event.preventDefault(); const { fakeAuth, fileKit } = this.props; const { file, recipient } = this.state; const fileId = await fileKit.upload(file); const recipientPublicIdentities = await fakeAuth.getPublicIdentities([recipient]); await fileKit.share([fileId], { shareWithUsers: recipientPublicIdentities }); }

10. Create a sharing link

To download the file, the recipient will need to:

  • provide their email address and the file ID
  • verify their email address to prove their identity

One easy way to transmit the recipient email address and the file ID is to create a download link that contains these values in a query string, then display it so that it can be sent to the recipient.

Craft a download link in the onUpload() method, then display it when needed:

File: src/Upload.js
import React from 'react'; const appUrl = "http://127.0.0.1:3000"; class Upload extends React.Component { // ... onUpload = async (event) => { event.preventDefault(); const { fakeAuth, fileKit } = this.props; const { file, recipient } = this.state; const fileId = await fileKit.upload(file); const recipientPublicIdentities = await fakeAuth.getPublicIdentities([recipient]); await fileKit.share([fileId], { shareWithUsers: recipientPublicIdentities }); const downloadLink = appUrl + '?fileId=' + encodeURIComponent(fileId) + '&email=' + encodeURIComponent(recipient); this.setState({ downloadLink }); } render() { const { file, recipient } = this.state; const { downloadLink, file, recipient } = this.state; if (downloadLink) { return ( <> <h1>Done!</h1> <p>You can now send this link to <b>{recipient}</b></p> <p id="download-link" href={downloadLink}>{downloadLink}</p> </> ); } return ( // ... } }

11. Create the download component

The last step of this tutorial is to handle the download part. Create a file named Download.js in the src folder, then create a basic component in it:

File: src/Download.js
import React from 'react'; class Download extends React.Component { constructor(props) { super(props); this.state = { downloadDone: false }; } async componentDidMount() { this.setState({ downloadDone: true }); } render() { if (!this.state.downloadDone) return <p>Downloading file...</p>; return ( <> <p>Download complete!</p> <button onClick={this.props.doneCb} id="exit-button">Upload a new file</button> </> ); } } export default Download;

To download the file, we will need to get the fileId and the recipient's email from the URL. In the App constructor, parse the email and file ID from the URL query parameters and store them in the state.

File: src/App.js
// ... class App extends React.Component { constructor(props) { super(props); this.fileKit = new FileKit({ appId }); this.fakeAuth = new FakeAuthentication({ appId }); this.state = { ready: false }; const urlParams = new URLSearchParams(window.location.search); const fileId = urlParams.get('fileId'); const email = urlParams.get('email'); this.state = { fileId, email, ready: false }; }

Also display the Download component when needed, and add a downloadDone() method to reset the state when the download is finished. This callback is triggered when the Upload a new file button in the Download component is pressed.

File: src/App.js
import React from 'react'; import FileKit from '@tanker/filekit'; import FakeAuthentication from '@tanker/fake-authentication'; import Download from './Download'; import Upload from './Upload'; const appId = 'your App ID'; class App extends React.Component { // ... downloadDone = () => { this.setState({ fileId: null }); } render() { const { ready } = this.state; const { fileId, ready } = this.state; if (!ready) return <section><p>Loading...</p></section>; return <section><Upload fileKit={this.fileKit} fakeAuth={this.fakeAuth} /></section>; return ( <section> {fileId ? ( <Download fileKit={this.fileKit} fileId={fileId} doneCb={this.downloadDone} /> ) : ( <Upload fileKit={this.fileKit} fakeAuth={this.fakeAuth} /> )} </section> ); } }

12. Download and decrypt files

To download the file, we will need to create an authenticated session for the recipient. This is done by using filekit.start().

When calling fileKit.start(), FileKit will automatically display an email verification procedure when needed. This is to ensure the person trying to download the file is the intended recipient.

In the componentDidMount() function of the App, call fakeAuth.getIdentity() to get the recipient's Tanker identity and call fileKit.start() with the identity and the user's email address:

File: src/App.js
class App extends React.Component { // ... async componentDidMount() { const identity = await this.fakeAuth.getIdentity(); await this.fileKit.startDisposableSession(identity); const { email } = this.state; if (email) { const identity = await this.fakeAuth.getIdentity(email); await this.fileKit.start(email, identity); await this.fakeAuth.setIdentityRegistered(email); } else { const identity = await this.fakeAuth.getIdentity(); await this.fileKit.startDisposableSession(identity); } this.setState({ ready: true }); } // ... }

Finally, download and decrypt the file when the Download component has been mounted:

File: src/Download.js
class Download extends React.Component { // ... async componentDidMount() { const { fileId, fileKit } = this.props; await fileKit.downloadToDisk(fileId); this.setState({ downloadDone: true }); } // ... }

When downloading, the file is decrypted on the fly before being sent to disk (optionally). The original file metadata such as name, mimetype and lastModified date, have been preserved.

13. Test the application

Run the app code in your web browser, using:

npm run start

Then,

  • Enter your email address in the recipient email input
  • Choose a file using the file selector and click the Create a secure link button
  • The download link appears, copy it

Now you play the role of the recipient. Open the link in a new tab:

  • A modal window should appear asking you to enter a verification code
  • Check the recipient's email inbox for an email from verification@tanker.io and use the verification code to verify the recipient's email address
  • The download should start immediately
  • Verify that the downloaded file is the same as the uploaded file


Congratulations! 🎊 🎉 🍾

Your end-to-end encrypted file transfer is now functional.

In a real application, you would of course have to replace @tanker/fake-authentication with your own user management backend, and integrate the Tanker identities in it. It goes beyond the scope of this tutorial, but you can read the guide about user management for more information.

So, how did it go?

Excited 👍, disappointed 👎, found a bug 🐛... please send your feedback to support@tanker.io.
We appreciate your ideas and we would be very happy to hear about your use cases too!

Let's stay in touch on GitHub: Star

14. What's next?

Now that you've seen your first application using Tanker, you should feel free to update it and experiment on your own.

For more details and to go beyond this example application, please refer to: